PostgreSQL 数据库安全指南

3 minute read

背景

数据库在一个企业中通常都处于非常核心的位置,数据库安全是一个非常严肃的话题。

从机房、网络、服务器、数据交换设备、操作系统、应用程序、数据库本身,数据库所处的环境非常复杂,安全隐患也非常多。

所以本文将从各个层面帮助大家理解和避免一些常见的安全隐患问题。

本文是PostgreSQL使用安全指导性的文章,涉及详细的用法或原理请参考相关链接。

如何安全的使用PostgreSQL,让用户高枕无忧呢?

可以分为如下几个方面来加固你的数据库。

一、认证安全

认证前的安全,端口暴露度的把握。

《PostgreSQL 认证安全》

认证是使用数据库的第一关,如果认证不安全,你的数据库将很容易被入侵。

1. pg_hba.conf安全

配置合理的pg_hba.conf,将权限控制到最小。

任何情况下都不允许trust认证方法;

超级用户只允许从本地连接,不允许从网络连接;

将dbname+username+ip限制到最小,”授权用户”只能从”授权IP”过来连接”授权数据库”;

如果使用数据库密码认证,请务必使用md5认证方法,网络传输的密码是md5+随机字符加密后的密文。

2. 密码复杂度策略

创建用户或修改用户密码时,强制限制密码的复杂度,例如密码长度,包含数字,字母,大小写,特殊字符等,同时排除暴力破解字典中的字符串。

《PostgreSQL 认证安全》

3. 密码更换周期

使用合理的密码更换周期,创建角色时使用VALID UNTIL ‘timestamp’,同时限制密码不能重复使用,

请注意配合监控使用,及时提醒管理员和用户密码快到期了。

4. 密码存储策略

如果使用数据库密码认证,创建角色时请使用encrypted password,这样pg_shadow.passwd存储的是密码+角色名的MD5码,否则是明文。

postgres=# create role r_test unencrypted password 'hello123' login;  
postgres=# select usename,passwd from pg_shadow where usename='r_test';  
 usename |  passwd    
---------+----------  
 r_test  | hello123  
(1 row)  
  
postgres=# alter role r_test encrypted password 'hello123';  
ALTER ROLE  
postgres=# select usename,passwd from pg_shadow where usename='r_test';  
 usename |               passwd                  
---------+-------------------------------------  
 r_test  | md5bb0d7bef45a0530ac529e7b43943a2d1  
(1 row)  
  
postgres=# select md5('hello123r_test');  
               md5                  
----------------------------------  
 bb0d7bef45a0530ac529e7b43943a2d1  
(1 row)  

5. 设置密码时防止密码被记录到数据库日志,history,审计日志,pg_stat_activity, pg_stat_statements中.

(例如使用了readline, 堡垒机, 或者开启了log_statement)

  ~/.psql_history  
  pg_log/xxx.csv  
  堡垒机日志  
  pg_stat_activity视图  
  pg_stat_statements视图与文件(与PG版本有关)  

6. 外部表密码安全

回收pg_user_mappings视图的public权限,否则mapping用户可以看到user mapping下的密码。

revoke all on view pg_user_mapings from public;    

7. dblink密码安全

普通用户使用dblink时,需要提供连接用户和密码,不建议使用。如果一定要用,请限制dblink目标用户在目标数据库集群的权限到最小化。

同时开放DBLINK还有一个问题,请参考

《开放dblink , fdw带来的安全隐患》

8. 如果使用外部认证,如AD域,请加固对应的认证服务。

9. 应用程序配置文件中如果需要配置用户和密码,请确保应用程序服务器的安全。防止配置文件泄露。

10. 数据库本身的认证方式加固,但是需要客户端驱动同时来支持,修改认证协议。

参考

《PostgreSQL psql 安全设置数据库用户密码的方法之一》

二、数据传输安全

确保数据传输过程的安全,即使数据被截获,也不需要担心。

1. 数据传输加密

如果你的网络是不可靠的,请使用加密传输,例如OPENSSL。

参考,

《PostgreSQL 如何实现网络压缩传输或加密传输(openssl)》

2. 认证过程加密

认证过程加密,指认证过程中,网络上传输的密码安全,如果使用数据库认证,请使用MD5方法(配置pg_hba.conf)。确保网络中传输的是随机码和MD5加密后的MD5。

但是请注意,MD5也不能泄露,泄露是危险的,可以参考以下文档使用MD5进行认证

《PostgreSQL 对比 MySQL - 秘钥认证》

三、数据安全

你的数据安全吗?如果你存储的敏感数据在数据库中是明文的,一旦数据库暴露,用户数据可能泄露,如何尽可能的保证泄露的数据的安全呢?

1. 字段存储加密

将敏感数据加密后存储在数据库中,即使加密数据泄露,只要加解密方法没有泄露,也是相对安全的。

加解密方法建议放在应用端实现,如果加解密在数据库端实现,用户一旦入侵数据库,更容易破解。(或者加密在数据库端实现,解密在应用程序端实现)

《固若金汤 - PostgreSQL pgcrypto加密插件》

2. 敏感数据,跟踪并记录DML,truncate操作的undo

对于非常敏感的数据,我们应该记录对这些数据操作的UNDO,在必要时刻可以快速的回滚到误操作前。

这种方法主要是对付SQL注入,人为误操作(包括delete,update,insert,truncate的回滚)。

请参考,

《PostgreSQL 闪回 - flash back query emulate by trigger》

3. 函数代码加密

如果我们将业务逻辑放在数据库函数中处理的话,肯定不想让用户看到函数的内容。

对于先编译后执行的函数,例如C函数,是不需要加密的,但是,对于解释性语言函数如plpgsql,建议加密函数的内容。

目前enterprisedb有这个特性,社区版本的PostgreSQL没有这个特性。

请参考,

《PostgreSQL 函数封装 - Wrap Function code like Oracle package》

http://www.cybertec.at/en/products/plpgsql_sec-encrypt-your-stored-procedure-codes/

如果不能加密,至少需要控制普通用户不能查看函数内容。

《PostgreSQL 函数代码隐藏 - How to control who can see PostgreSQL function’s source code》

4. 使用recycle bin插件,用户在删对象时,对象会存储在recycle bin schema下,而不会被真实删除。那么表被误删除或恶意删除后,很容易找回。(使用钩子实现)

请参考,

《PostgreSQL 回收站功能 - 基于HOOK的recycle bin pgtrashcan》

5. 透明加密,防止数据文件被拖走后,泄露数据。

手段包括类型透明加密(TDE),文件透明加密(FDE)

参考

《PostgreSQL 透明加密(TDE,FDE) - 块级加密》

四、权限控制

1. 权限管理

最危险的就是最容易暴露的数据库用户,当然是应用连接数据库的账号(以下简称应用账号)。

应用账号权限越大,应用程序被攻击后破坏性就越大。

例如用户有删数据库,删表,删索引,删表空间,删SCHEMA,删函数等等这样的权限的话,危害极大。

安全建议:

1.1 使用超级用户创建数据库,SCHEMA,应用所需的对象(如表,索引,函数)。

1.2 创建应用账号角色。

1.3 回收数据库,schema,language,应用对象的public权限。

    revoke all on database dbname from public;   
    revoke all on schema sch_name from public;   
    revoke all on language plpgsql from public;   
    revoke all on table ... from public;  
    revoke all on function ... from public;  
    ......  

1.4 将数据库,schema的使用权限赋予给应用账号。

    grant connect on database dbname to approle;  
    grant usage on schema sch_name to approle;  

1.5 将应用需要访问的对象的相关权限赋予给应用账号。

    例如表的select,insert,update,delete权限, 函数的execute权限等.  

这样,应用账号只有对象的使用权限,没有对象的DROP,TRUNCATE,REPLACE权限,相对来说是更安全的。

2. 通过事件触发器禁止应用账号执行DDL,通过这种方法可以限制用户执行DDL,防止被攻击后,用户执行DROP或TRUNCATE删除对象或清空数据 (当然delete不带条件还是能删除数据的,需要用其他手段)。

请参考,

《PostgreSQL 事件触发器 - PostgreSQL 9.3 Event Trigger》

3. 防止执行不带条件的delete,update。

例如,在需要保护的表里,新增一条dummy记录,创建行触发器,当这条记录被更新或删除时,抛出异常。

对于业务上不允许执行删除操作的表,不要赋予该表的delete权限给应用账号,也就不会有这个风险。

4. 函数语言安全

建议回收函数语言的public权限,以及普通用户的权限,用户不能创建函数。执行online code。

例如:

revoke all on language plpgsql from public;  
revoke all on language plpgsql from app_role;  

5. 行级安全策略

限制普通用户只能操作表中的指定条件的记录,用于rewriter改写重写规则,普通用户只能访问满足指定条件的行。

请参考,

《PostgreSQL 行安全策略 - PostgreSQL 9.5 new feature - can define row security policy for table》

6. 对于只需要访问某些行,或某些列的需求,可以通过列权限或视图来限制应用账号的权限。

五、防恶意攻击

1. 视图攻击

用户利用PostgreSQL的优化器原理,创建成本极低的函数,在函数中获取视图限制外的隐藏内容。

如果用户没有创建函数的权限,用户就无法利用这个原理。

或者使用安全栅栏来弥补。

请参考,

《PostgreSQL views privilege attack and security with security_barrier(视图攻击)》

《PostgreSQL leakproof function in rule rewrite(“attack” security_barrier views)》

或者使用基于安全策略来管理数据可见性。

《PostgreSQL 9.4 patch : Row-Level Security》

《PostgreSQL 行安全策略 - PostgreSQL 9.5 new feature - can define row security policy for table》

2. 防止SQL注入

应用层应该有SQL注入预防手段,例如使用简单的过滤器,使用绑定变量等手段。

3. 密码暴力破解

目前可以通过密码错误延迟认证(auth_delay)来增加暴力破解需要的时间。

请参考,

《PostgreSQL 密码安全指南》

4. 防止普通用户通过函数调用陷阱进行攻击

《PostgreSQL 安全陷阱 - 利用触发器或规则,结合security invoker函数制造反噬陷阱》

5. 《Hacking PostgreSQL》

6. 防止恶意占用连接,即使攻击者没有密码,也能占用SERVER SLOT,即攻击者连接数据库时,在提示输入密码的阶段不响应,这个SOLT会被占用,相当于消耗数据库的一个连接。

通过如下参数,可以控制认证过程的时间,但是无法防止恶意的攻击,因为超时后攻击者又可以发起连接。

https://www.postgresql.org/docs/9.6/static/runtime-config-connection.html#RUNTIME-CONFIG-CONNECTION-SECURITY

authentication_timeout (integer)

Maximum time to complete client authentication, in seconds. 

If a would-be client has not completed the authentication protocol in this much time, the server closes the connection. 

This prevents hung clients from occupying a connection indefinitely. 

The default is one minute (1m). This parameter can only be set in the postgresql.conf file or on the server command line.

所以不要随机的暴露端口。

7. 通过端口尝试CANCEL用户QUERY。

《PostgreSQL cancel 安全漏洞》

六、备份,容灾,恢复测试

再好的安全策略,也需要备份。

基于时间点的,块级别增量备份,是比较靠谱的。(你可以选择合适的文件系统,例如btrfs)

请参考,

《PostgreSQL 最佳实践 - 块级增量备份(ZFS篇)方案与实战》

《PostgreSQL 最佳实践 - 块级增量备份(ZFS篇)验证 - recovery test script for zfs snapshot clone + postgresql stream replication + archive》

另外PostgreSQL的每个数据块中都有LSN编号,使用这个也能实现块级别的增量备份,参考

《PostgreSQL 最佳实践 - 块级别增量备份(pg_rman baseon LSN)源码浅析与使用》

《PostgreSQL 最佳实践 - pg_rman 以standby为源的备份浅析》

《PostgreSQL 最佳实践 - pg_rman 数据库恢复示例 与 软件限制解说》

七、审计

审计功能,一般是用于排查问题的,当然也是一种举证的手段,例如你的数据库遭到暴力破坏了,证据非常重要。

这里有一些例子:

如何跟踪postgresql.conf的配置变更?

worker process钩子程序的妙用.

《PostgreSQL 配置文件变更审计 - A custom background worker process to log changes to postgresql.conf to a table》

如何跟踪表中的记录被哪个用户修改或插入?

《PostgreSQL 跟踪谁动了你的记录 - Use insert_username Tracking Who Changed a Table》

使用pg_log_userqueries插件, 审计指定用户,数据库或超级用户的所有执行的SQL.

《PostgreSQL 灵活审计插件 - PostgreSQL per database or per user audit use pg_log_userqueries》

使用hstore插件和触发器跟踪表的行记录变更.

《USE hstore store table’s trace record》

PostgreSQL中如何跟踪表的创建时间, 表定义的修改时间

《PostgreSQL 跟踪DDL时间 - cann’t use pg_class’s trigger trace user_table’s create,modify,delete Time》

PostgreSQL 精细化审计的实施.

1. 审计指定表的INSERT, UPDATE, DELETE, TRUNCATE

2. 审计指定用户对指定表的INSERT, UPDATE, DELETE, TRUNCATE

3. 审计指定表的指定数据的INSERT, UPDATE, DELETE

4. 如何让数据库只审计成功提交的数据, 而不记录回滚事务.

《PostgreSQL 审计成功事务 - PostgreSQL Fine-Grained Table,Column,Row Level Audit》

PostgreSQL 审计功能配置

《PostgreSQL 审计 - PostgreSQL Audit by Database or Role wide parameter》

PostgreSQL 9.3 规则系统改进, 允许在规则的values中使用多次NEW, OLD.

使用规则跟踪数据变更, 记录新老数据.

《PostgreSQL 审计 - PostgreSQL 9.3 Allow OLD and NEW in multi-row VALUES within rules》

如何跟踪基于字段值为条件的行的变更,插入和删除呢?

创建触发器时when的用法, 或在触发器函数中处理. 选择效率高的.

《PostgreSQL 触发器应用 - (触发器WHEN)前置条件过滤跟踪目标记录》

PostgreSQL数据库在上市公司重要应用中的SOX审计

《PostgreSQL数据库在上市公司重要应用中的SOX审计》

审计表的DDL行为, 以及哪个会话在什么时间点,通过什么IP干的.

《PostgreSQL 事件触发器 - DDL审计 , DDL逻辑复制 , 打造DDL统一管理入》

审计变更的行, 以及被变更的字段内容; 新增的行, 删除的行; 以及哪个会话在什么时间点,通过什么IP干的.

《PostgreSQL 触发器应用 - use trigger audit record which column modified, insert, delete.》

pg_audit模块

《PostgreSQL 审计 - pg_audit module》

八、补丁

PostgreSQL社区的更新速度很快,几乎每天都会有commit,有些可能是FIX patch,有些可能是feature,有些可能是性能提升patch,

正常情况下,我们只要跟随小版本的升级就可以了,一般社区遇到比较大的安全漏洞,提交补丁后马上就会发布小版本,如果没有发布小版本,

说明没有大的安全漏洞,你可以通过 http://git.postgresql.org 实时跟踪社区的动态,自行打patch。

大版本的更新,通常情况下大版本有大量的feature,如果需要使用的话,也可以更新到大的版本,但是请注意与应用有关的修改,模块的更新等。

九、外界环境安全

1. 应用程序是否安全?

2. 中间件是否安全?

3. 数据库所在操作系统是否安全?

4. 数据库所在服务器是否安全?

5. 存储安全,存储是否在安全的地方,有没有硬盘被拔掉的风险?

6. 网络安全,如机架交换机,未插网线的端口是否禁用了,是否做了MAC地址过滤或绑定?

7. 机房安全?

十、资源控制

虽然我们前面已经控制的挺好了,但是数据库还有一种风险和网络的DDOS攻击类似,大量的用户请求可以把数据库搞慢。

或者大量的运算量或者IO极大的请求,也很容易把数据库搞慢。

资源控制手段举例:

控制连接数,控制活动连接数,控制SQL执行时间,控制锁等待时间,控制事务空闲时间。

另一方面,因为PostgreSQL的并发控制用到了多版本,所以当更新或删除数据时,老的版本依旧存在于数据库中,需要vacuum进程回收这些数据,

目前有一个缺陷,当有长事务存在时,事务开启后产生的垃圾被视为新的垃圾,不会被回收,所以长事务容易导致数据库膨胀,太长的事务甚至可以导致数据库的xid耗尽,必须关机做vacuum freeze。

请参考,

《PostgreSQL垃圾回收代码分析 - why postgresql cann’t reclaim tuple is HEAPTUPLE_RECENTLY_DEAD》

十一、监控

监控是DBA的眼睛,好的监控可以提前发现问题,将问题排除在发生之前。

参考(zabbix, nagios)

常用监控项请参考,

《PostgreSQL nagios monitor script (archive, vacuum, age, conn, rollback, standby, lock, xact, seq, index…)》

巡检参考

《PostgreSQL AWR报告》

https://raw.githubusercontent.com/digoal/pgsql_admin_script/master/generate_report.sh

十二、开发规约

《PostgreSQL 数据库开发规范》

Flag Counter

digoal’s 大量PostgreSQL文章入口