等保2.0评估:PostgreSQL数据库的深度解析

发表时间: 2022-10-19 14:44

登录数据库

一般用户在pgsql服务器上会创建相应的postgres用户,我们切换到postgres用户来操作数据库,pgsql

以postgres为默认用户。

执行su - postgres

切换后建议查看~/.bash_profile文件中是否将pgsql的bin目录加入环境变量中,方便使用pgsql相关命令

有相应环境变量后,即可直接执行:

psql -U postgres //连接pgsql server

psql -h 127.0.0.1 -d postgres -U postgres //连接pgsql server

在服务器本地postgres用户可直接使用psql命令(这个涉及到pg_hba.conf文件下的登录验证方式,后续会有说明)登录数据库

查看数据库版本:

外部命令行界面,可使用pg_ctl --version

登录到数据库内,使用select version();

接下来对应一些等保相关的条款,对应到pgsql中又是哪些设置呢?


身份鉴别

a)应对登录的用户进行身份标识和鉴别,身份标识具有唯一性,身份鉴别信息具有复杂度要求并定期更换

1.身份标识和鉴别

针对这个测评项,postgresql采用用户名+口令的方式进行身份标识和鉴别,除了这个我们还要搞清楚postgresql数据库的验证方式,这里就涉及到pg_hba.conf文件

pg_hba.conf文件(默认在$PGDATA目录下):

pg_hba.conf文件存放在数据库集群的数据目录里。

HBA 的意思是 host-based authentication:基于主机的认证。在initdb初始化数据目录的时候,它会安装一个缺省的文件。

文件pg_hba.conf的常用格式是一套记录,每行一条。空白行行被忽略,井号( # )开头的注释也被忽略。并且记录不能跨行存在。

每条记录声明一种联接类型,一个客户端 IP 地址范围(如果和联接类型相关的话),一个数据库名,一个用户名字,以及对匹配这些参数的联接使用的认证方法。

第一条匹配联接类型,客户端地址和联接企图请求的数据库名和用户名的记录将用于执行认证。

具体文件内容如下:

① TYPE定义了多种连接PostgreSQL的方式,分别是:“local”使用本地unix套接字,“host”使用TCP/IP连接(包括SSL和非SSL),“host”结合“IPv4地址”使用IPv4方式,结合“IPv6地址”则使用IPv6方式,“hostssl”只能使用SSL TCP/IP连接,“hostnossl”不能使用SSL TCP/IP连接。

② DATABASE指定哪个数据库,多个数据库,库名间以逗号分隔。

“all”只有在没有其他的符合条目时才代表“所有”,如果有其他的符合条目则代表“除了该条之外的”,因为“all”的优先级最低,如下例:

注意:若local mydb all rehect 这条添加在‘all’条下面则不生效,从上至下匹配

这两条都是指定local访问方式,因为前一条指定了特定的数据库mydb,所以后一条all代表的是除了mydb之外的数据库,同理用户的all也是这个道理。

尝试,无法登录。

③ USER指定哪个数据库用户(PostgreSQL正规的叫法是角色,role),多个用户以逗号分隔。

④ ADDRESS 项local方式不必填写,该项可以是IPv4地址或IPv6地址,可以定义某台主机或某个网段。

⑤NETHOD 指定如何处理客户端的认证。常用的有ident、md5、password、trust、reject。

ident:是Linux下PostgresSQL默认的local认证方式,凡是能正确登录服务器的操作系统(注:不是数据库用户)就能使用本用户映射的数据库用户不需密码登录数据库。用户映射文件为pg_ident.conf,这个文件记录着与操作系统用户匹配的数据库用户,如果某操作系统用户在本文件中没有映射用户,则默认的映射数据库用户与操作系统用户同名。

比如,服务器上有名为user1的操作系统用户,同时数据库上也有同名的数据库用户,user1登录操作系统后可以直接输入psql,以user1数据库用户身份登录数据库且不需密码。很多初学者都会遇到psql -U username登录数据库却出现“username ident 认证失败”的错误,明明数据库用户已经createuser。原因就在于此,使用了ident认证方式,却没有同名的操作系统用户或没有相应的映射用户。

解决方案:1、在pg_ident.conf中添加映射用户;2、改变认证方式。

md5:是常用的密码认证方式,如果你不使用ident,最好使用md5。密码是以md5形式传送给数据库,较安全,且不需建立同名的操作系统用户。

password:是以明文密码传送给数据库,建议不要在生产环境中使用。

trust:是只要知道数据库名就不需要密码或ident就能登录,建议不要在生产环境中使用。

reject:是拒绝认证。

所以,针对第一个身份鉴别项,我们就需要查看pg_hba.conf文件,观察是否所有地址都需要通过口令验证,下图的本地local对应的NETHOD值为trust则存在问题,理论上本机上也需要关闭这种验证

2.身份标识具有唯一性

通过用户名(严格意义上说是为角色)进行身份标识,无法创建同名用户,默认符合

3.身份鉴别信息具有复杂度

针对于这个测评点,首先我们需要确认用户当前口令的组成情况,来判断当前口令是否达到等保要求,一般要求口令由大写字母、小写字母、数字、特殊字符中的三种组成,且长度8位以上。

然后我们可以通过show shared_preload_libraries; 命令查看是否启用了passwordcheck模块

密码复杂度模块配置:

passwordcheck.so模块可以实现密码复杂度要求,此模块可以检查密码,如果密码太弱,他会拒绝连接

创建用户或修改用户密码时,强制限制密码的复杂度,限制密码不能重复使用

1)启用模块

添加‘$libdir/passwordcheck’到参数shared_preload_libraries,重启生效

默认so文件都存放在$libdir目录下

使用select name,setting from pg_settings where name like '%dynamic%'; 查看

alter system set shared_preload_libraries=pg_stat_statements,passwordcheck;

重启服务后生效

2.测试

要求:最少8个字符,必须包含数字和字母,密码中不能含有用户名字段(全部字段)

设置完毕后发现简单密码无法设置

4.身份鉴别信息定期更换

暂时没有找到可以查看用户上一次口令的修改时间(有知道的可以告诉我一下)

密码使用期限网上叫我们查看的是:

select * from pg_shadow查看valuntil字段

valuntil值,应该是密码的失效日期(没啥用,和我们预期的需求不太一样),过了这个时间点,即使正确的口令也无法登录,需要管理员修改valuntil的值

通过Navicat管理工具这里可以直接设置


b)应具有登录失败处理功能,应配置并启用结束会话、限制非法登录次数和当登录连接超时自动退出等相关措施

1.登录失败处理功能

postgresql好像不支持,目前无登录次数限制功能

2.操作超时自动退出功能

查看了一下,postgresql 中大概有这么几个 timeout 参数,原文链接

archive_timeout
控制服务器周期性地切换到一个新的 WAL 段文件,通俗的讲,就是定时归档。

authentication_timeout
完成服务器认证的最长时间,如果在这个时间内没有完成认证,服务器将关闭连接。

checkpoint_timeout
自动 WAL 检查点之间的最长时间,增大这个参数会使数据库崩溃后恢复的时间加长。

deadlock_timeout
进行死锁检测之前在一个锁上等待的总时间


idle_in_transaction_session_timeout

空闲事务超时。终止任何已经闲置超过这个参数所指定的时间(以毫秒计)的打开事务的会话。这使得该会话所持有的任何锁被释放,并且其所持有的连接槽可以被重用, 它也允许只对这个事务可见的元组被清理。

lock_timeout
锁等待超时。语句在试图获取表、索引、行或其他数据库对象上的锁时等到超过指定的毫秒数,该语句将被中止。不推荐在postgresql.conf中设置。

statement_timeout
控制语句执行时长,单位是ms。超过设定值,该语句将被中止。
不推荐在postgresql.conf中设置,如非要设置,应该设置一个较大值。

wal_receiver_timeout
中止处于非活动状态超过指定毫秒数的复制链接。这对于正在接收的后备服务器检测主服务器崩溃或网络断开有用。设置为0会禁用超时机制。这个参数只能在postgresql.conf文件中或在服务器命令行上设置。默认值是 60 秒。

wal_sender_timeout
中断那些停止活动超过指定毫秒数的复制连接。这对发送服务器检测一个后备机崩溃或网络中断有用。设置为0将禁用该超时机制。这个参数只能在postgresql.conf文件中或在服务器命令行上设置。默认值是 60 秒。

好像上述没有对应等保要求的,多少时间不操作,自动退出当前数据库的功能,类似‘屏幕保护程序’一样的设置参数暂时没找到。


c) 当进行远程管理时,应采取必要措施防止鉴别信息在网络传输过程中被窃听

1. 查看$PGDATA目录下的postgresql.conf文件

确认ssl字段是否为on

2.查看$PGDATA目录下的pg_hba文件:

在哪些地址段采用了hostssl连接方式

注意:NETHOD列采用password字段,则代表明文传输用户名和口令


使用SSL连接数据库

1)使用命令生成相应的证书

openssl genrsa -des3 -out server.key 2048 创建server.key文件,过程中要求输入密码

可以使用如下命令删除设置的密码:openssl rsa -in server.key -out server.key

2) 创建基于server.key的服务器证书

openssl req -new -key server.key -days 3650 -out server.crt -x509

所有设置都直接回车跳过了

然后修改该文件的权限:chmod og-rwx server.key

如果文件的权限比这个更自由,服务器将拒绝该文件。

第一次未修改文件权限,数据库就启动不了,日志如下

数据库用户所有文件权限必须小于600

在$PGDATA目录下就存在2个文件

3) 修改配置文件使其支持SSL连接

修改postgresql.con文件,把SSL设置成on,将ssl_ca_file='server.crt'

再设置pg_hba.conf文件,新增ssl连接认证规则:

然后重启服务连接即可


d)应采用口令、密码技术、生物技术等两种或两种以上组合的鉴别技术对用户进行身份鉴别,且其中一种鉴别技术至少应使用密码技术来实现

这条个人认为直接去现场观察客户登录该数据库的方式

一般都为用户名+口令,未采用双因子鉴别

应对登录的用户分配账户和权限

针对这个测评项,首先我们要搞清楚postgresql数据库的相应权限问题

在postgresqll数据库中,认为用户就是一个相应的role,PostgreSQL使用角色的概念管理数据库访问权限。一个角色可以被看成是一个数据库用户或者是一个数据库用户组,这取决于角色被怎样设置。角色可以拥有数据库对象(例如,表和函数)并且能够把那些对象上的权限赋予给其他角色来控制谁能访问哪些对象。此外,还可以把一个角色中的成员资格授予给另一个角色,这样允许成员角色使用被赋予给另一个角色的权限。

1.系统权限

1.1 role属性

可以认为是这个用户所具有的系统权限.

  • LOGIN --具有登录权限
  • SUPERUSER --超级用户,具有所有系统权限,除了登录验证
  • CREATEDB --创建数据库权限
  • CREATEROLE --创建role权限
  • PASSWORD --设置密码

1.2 修改属性

创建test角色

create role test login;

alter role test createdb createrole password '****@123';

alter role test nocreatedb nocreaterole superuser;

通过psql程序登录可使用\du命令列出现有角色,以及相应的角色权限

2.role成员权限

role membership(role成员)为了管理上的方便,我们可以创建一个role group,然后可以将各用户或者有特殊权限的role组织在一起,各个role就是这个role group的membership.

role group 是不带login的role,因为pg使用role来表示所有的角色、用户、用户组,所以不要混淆,创建语句都是create role.

2.1 继承role group

1)直接继承

我们创建一个用户,两个角色,分别有直属一个表的查询权限create role jack login inherit;create role r1;create role r2;

然后创建对应的表

授予查询权限

进行grant授权,使jack成为r1,r2的membership

测试角色切换

jack 继承了r1,r2的权限

2)间接继承

移除membership

可以使用revoke group_role from role1,…命令

此时,r1属于r2,然后jack又属于r1

关闭r1的继承

alter role r1 noinherit;

然后再尝试访问tab,发现tab2无法访问

取消继承权限后,r1也无权限访问

移除后将无权限

注意:授权不能形成回路

3)系统权限任何时候都不会主动继承

系统权限不会继承,只有主动set才生效

使用set命令后,该会话会具有group_role的临时系统权限,且会被认为是group_role角色,创建的表之类的均为set role的权限

重新开启会话后,无权限

三种方式还原到最初的jack角色:

综上所述:角色属性login、superuser、createdb和createrole可以被认为是一种特殊权限,但是它们从来不会像数据库对象上的普通权限那样被继承。要使用这些属性,你必须实际set role到一个有这些属性之一的特定角色。

4)角色删除

删除role,role下有权限或者是对象属于此role,则删除不了

移除掉相关权限关联后进行删除,然后涉及到r1的成员或者是group_role自动释放

5)role总结

  • pg中的role包含了用户,角色,角色组,成员等所有含义,如使用create role来创建
  • 一个role可以成为多个role的成员,根据role的inherit属性来决定是否集成其他role的各种权限
  • 继承关系不能形成回路
  • 删除role需要先清理此role关联的各种权限

3.等保查看相应点

查看当前数据库内有哪些角色,可通过工具进行查看

然后再查看对应角色对应的权限

命令行界面可以通过\du命令列出现有角色和权限

个人认为,针对第一个条款,不管权限分配是否合理,只要给用户分配权限了就算符合

这条条款主要是为了防止匿名用户的登录,像NTP匿名登录那种

至于权限是否合理,已在后面的最小权限,管理用户的权限分离,访问控制粒度的地方体现,应在那些条款内去判断,所以对于第一条我们只要看他是否为用户分配了相应的账户和权限即可。


应重命名或删除默认账户,修改默认账户的默认口令

1.默认角色

可以通过工具进行查看

PostgreSQL提供一组默认角色, 他们可以访问特定的、通常需要的特权功能和信息。管理员可以将这些角色 GRANT 给用户和/或其环境中的其他角色, 为这些用户提供对指定功能和信息的访问权限。请注意,每个默认角色的特定权限可能会因为将来添加额外的功能而发生变化。管理员应监控发行说明以进行更改,如下图:

角色

允许的权限

pg_read_all_settings

阅读所有配置变量,即使那些通常只对超级用户可见的配置变量。

pg_read_all_stats

阅读所有pg_stat_*视图并使用各种统计相关的扩展,甚至那些通常只对超级用户可见的扩展。

pg_stat_scan_tables

执行可能对表进行可能需要很长时间ACCESS SHARE锁定的监视功能。

pg_signal_backend

给其他后端发送信号(比如: 取消查询、终止)。

pg_monitor

读取/执行各种监视视图和函数。此角色是pg_read_all_settings、 pg_read_all_stats和 pg_stat_scan_tables的成员。

pg_mointor、pg_read_all_settings、pg_read_all_stats和pg_scan_tables角色旨在允许管理员轻松配置角色以见识数据库服务器。他们授予一组通用权限,允许角色读取通常仅限于超级用户的各种有用的配置设置,统计和其他系统信息。应小心授予这些角色,以确保只在需要执行所需监视的情况下才会使用这些角色。管理员可以使用grant命令给这些用户授予访问权限:grant pg_signal_backend to admin_user;上述的的默认角色默认都是不可登录的。

2. 等保查看相应点

默认账户为postgres,查看其是否设置禁止登录,或将其重命名

在psql的程序下可以通过\du命令查看,无法登录将有cannot login字样


访问控制

1. 应及时删除或停用多余的、过期的账户,避免共享账户的存在

查看当前已有账户,询问管理员是否存在多余过期账户


2. 应授予管理用户所需的最小权限,实现管理用户的权限分离

这个管理用户的权限分离,像安全设备那种的三权分立用户,个人认为数据库层面不太好实现,一般不符合,要不就询问客户取证。


3. 应由授权主体配置访问控制策略,访问控制策略规定主体对客体的访问规则

确认各用户的操作权限,例如:赋予zfy角色所有表的查询权限

然后查询这个用户对应表的权限,就均拥有了select权限。

这里的授权主体一般为数据库管理员,对应的账户postgres,然后给zfy这个账户授予了select权限,也就是访问控制规则了。


4. 访问控制的粒度应达到主体为用户级或进程级,客体为文件、数据库表级

这个感觉是流氓条款。。。不知道我的理解对不对

看字面意识就是要主体为用户,客体为数据库表级?所以就是需要每一张表单独给用户授权,不能授权整个库或者全局权限,就是权限这块需要做的更细致,这个感觉一般都做不到。

查看访问控制策略规则,确认是否达到用户级、数据库表级。

如果有多个角色super之类的肯定不符合,这样粒度就不是表级了。


5. 应对重要主体和客体设置安全标记,并控制主体对有安全标记信息资源的访问

敏感标记,这个数据库应该没有自带这个功能。


安全审计

1. 应启用安全审计功能,审计覆盖到每个用户,对重要的用户行为和重要安全事件进行审计

PostgreSQL有3种日志:

  • pg_log(数据库运行日志) 内容可读 默认关闭的,需要设置参数启动
  • pg_xlog(WAL 日志,即重做日志) 内容一般不具有可读性 强制开启
  • pg_clog(事务提交日志,记录的是事务的元数据) 内容一般不具有可读性 强制开启

1.1 pg_log

作用: 这个日志一般是记录服务器与DB的状态,比如各种Error信息,定位慢查询SQL,数据库的启动关闭信息,发生checkpoint过于频繁等的告警信息,诸如此类。该日志有.csv格式和.log。建议使用.csv格式,因为它一般会按大小和时间自动切割,毕竟查看一个巨大的日志文件比查看不同时间段的多个日志要难得多。pg_log是可以被清理删除,压缩打包或者转移,同时并不影响DB的正常运行。当我们有遇到DB无法启动或者更改参数没有生效时,第一个想到的就是查看这个日志。

日志配置文件

在$PGDATA/postgresql.conf文件:

涉及的参数:

涉及的参数有:

logging_collector --是否开启日志收集开关,默认off,开启要重启DB

log_destination --日志记录类型,默认是stderr,只记录错误输出

log_directory --日志路径,默认是$PGDATA/pg_log, 这个目录最好不要和数据文件的目录放在一起, 目录需要给启动postgres的操作系统用户写权限.

log_filename --日志名称,默认是
postgresql-%Y-%m-%d_%H%M%S.log

log_file_mode --日志文件类型,默认为0600

log_truncate_on_rotation --默认为off,设置为on的话,如果新建了一个同名的日志文件,则会清空原来的文件,再写入日志,而不是在后面附加。

log_rotation_age --保留单个文件的最大时长,默认是1d,也有1h,1min,1s,个人觉得不实用

log_rotation_size --保留单个文件的最大尺寸,默认是10MB

log_error_verbosity --默认为default,verbose表示冗长的

log_connections --用户session登陆时是否写入日志,默认off

log_disconnections --用户session退出时是否写入日志,默认off


1.2 px_log

代表WAL日志,即重做日志

作用:这个日志是记录的Postgresql的WAL信息,也就是一些事务日志信息(transaction log)。默认单个大小是16M,源码安装的时候可以更改其大小(./configure --with-wal-segsize=target_value 参数,即可设置)这些日志会在定时回滚恢复(PITR), 流复制(Replication Stream)以及归档时能被用到,这些日志是非常重要的,记录着数据库发生的各种事务信息,不得随意删除或者移动这类日志文件,不然你的数据库会有无法恢复的风险

WAL:PostgreSQL在将缓存的数据刷入到磁盘之前,先写日志, 这就是PostgreSQL WAL ( Write-Ahead Log )方式,也就是预写日志方式

日志目录:

在$PGDATA目录下

不可读

1.3 pg_clog

pg_clog这个文件也是事务日志文件,但与pg_xlog不同的是它记录的是事务的元数据(metadata),这个日志告诉我们哪些事务完成了,哪些没有完成。这个日志文件一般非常小,但是重要性也是相当高,不得随意删除或者对其更改信息。

1.4 等保查看点

那么在我们测评的时候,一般会查询以下参数:

1)开启数据库运行日志(pg_log)收集

show logging_collector; --是否开启日志收集,默认off

2)其他一些日志配置

show log_destionation; --日志记录类型,默认是stderr,只记录错误输出

show log_directory; --日志路径,默认是$PGDATA/pg_log

show log_filename; --日志名称,默认是
postgresql-%Y-%m-%d_%H%M%S.log

show log_connections; --用户session登录时是否写入日志,默认off

show log_disconnections; --用户session退出时是否写入日志,默认off

show log_statement; --记录用户登录数据库后的各种操作

log_statement参数值:

none,不记录

ddl,记录create,drop,和alter

mod,记录ddl+insert,delete,update和truncate

all,mod+select


2. 审计记录应包括事件的日期和时间、用户、事件类型、事件是否成功及其他与审计相关的信息

2.1 查看数据库当前时间

2.2 查看日志文件

1) 查看$PGDATA目录postgresql文件

log_line_prefix参数

也可使用命令查看

2)位置:在$PGDATA目录下的:

查看其中一条日志,默认包含事件的时间、日期、主客体标识等:


3. 应对审计记录进行保护,定期备份,避免受到未预期的删除、修改或覆盖等

1)查看存储在本机的日志文件权限

查看log_file_mode参数:

在操作系统本地生成的也就是600权限

2)本机轮替规则

查看 log_truncate_on_rotation参数,需为开启状态

再查看log_filename参数,默认参数如下:

产生的日志文件,每天产生一个,不轮替:

为了让日志文件自动覆盖,达到保留多少日志的目的,可以进行如下设置:

log_truncate_on_rotation = on

log_filename = 'postgresql-%I.log' #最多保存12小时的日志,每小时一个文件

log_filename = 'postgresql-%H.log' #最多保存24小时的日志,每小时一个文件

log_filename = 'postgresql-%w.log' #最多保存一周的日志,每天一个文件

log_filename = 'postgresql-%d.log' #最多保存一个月的日志,每天一个文件

log_filename = 'postgresql-%j.log' #最多保存一年的日志,每天一个文件

3)备份

现场核查用户是否有备份措施,日志保存时间是否达到6个月以上。


4. 应对审计进程进行保护,防止未经授权的中断

审计进程的开关:

1)super权限账户

无法直接关闭,需要重启服务才行

其他一些参数能修改

2)普通账户无权限

给他授权super权限账户后即可


结论:

logging_collector仅具有重启服务权限的账户具有。

其余一些权限设置,查看用户权限,具有superuser权限用户可操作


入侵防范

1. 应遵循最小安装的原则,仅安装需要的组件和应用程序

不适用


2. 应关闭不需要的系统服务、默认共享和高危端口

不适用


3. 应通过设定终端接入方式或网络地址范围对通过网络进行管理的管理终端进行限制

1)确认监听地址

进入$PGDATA目录,查看postgresql.con文件,linsten addresses参数,* 代表监听所有地址。

2)确认远程管理地址

查看$PGDATA目录下的pg_hba文件:

确认ADDRESS字段是否进行了地址限制


4. 应提供数据有效性检验功能,保证通过人机接口输入或通过通信接口输入的内容符合系统设定要求

不适用


5. 应能发现可能存在的已知漏洞,并在经过充分测试评估后,及时修补漏洞

根据国标要求也就是要做两点:

1)需要对数据库进行漏洞扫描(其他发现风险的方式也行),确认是否存在高风险漏洞;

2)在对系统补丁进行更新时,需要进行充分的测试评估,也就是要留存相关测试评估证据,然后还要看是否及时对该漏洞进行修补。


6. 应能够检测到对重要节点进行入侵的行为,并在发生严重入侵事件时提供报警

该测评点一般在数据库服务器上体现,数据库不适用


总结

该数据库的等保要求个人认为大致是这样的,剩下的还有数据完整性、保密性、个人信息保护相关条款,有时间再更吧。