数据库安全

本节描述数据库安全相关操作

IP黑白名单和SSL加密连接

参考 服务器管理--客户端认证 章节

账号权限控制

参考 用户管理--用户权限 章节

账号有效期控制

参考 用户管理--用户权限 章节

备份恢复

参考 物理数据备份--备份恢复 章节 强烈建议开启数据库归档archive_mode

网络安全

建议系统管理员配置防火墙,只允许可信来源访问数据库端口.

passwordcheck 密码复杂度检查

此插件用于在创建或修改数据库用户密码时进行密码强度验证,可以有效防止使用弱密码。

安装配置

  • 修改unvdbsvr.conf添加共享库 在配置文件中添加: shared_preload_libraries = 'passwordcheck'  # 添加udaudit到共享库列表

  • 重启数据库并创建插件

unvdb=# CREATE EXTENSION passwordcheck;
CREATE EXTENSION

密码验证规则

插件将强制执行以下密码规则:

最小长度要求:密码长度必须不少于8个字符
复杂度要求:密码必须同时包含字母和非字母字符
用户名限制:密码中不能包含用户名
加密密码限制:对于已加密的密码,只能验证是否与用户名相同

使用示例

  • 创建新用户

unvdb=# -- 不符合要求的密码示例
CREATE ROLE test_user1 PASSWORD 'short';  -- 将失败:密码太短
CREATE ROLE test_user2 PASSWORD 'onlyletters';  -- 将失败:没有非字母字符
CREATE ROLE test_user3 PASSWORD 'test_user3';  -- 将失败:包含用户名
  • 符合要求的密码示例

CREATE ROLE test_user4 PASSWORD 'MyP@ssw0rd';  -- 成功:符合所有要求
ERROR:  password is too short
ERROR:  password must contain both letters and nonletters
ERROR:  password must not contain user name
CREATE ROLE
unvdb=# 
  • 修改用户密码

unvdb=# -- 不符合要求的密码示例
CREATE ROLE test_user1 PASSWORD 'short';  -- 将失败:密码太短
CREATE ROLE test_user2 PASSWORD 'onlyletters';  -- 将失败:没有非字母字符
CREATE ROLE test_user3 PASSWORD 'test_user3';  -- 将失败:包含用户名
  • 符合要求的密码示例

CREATE ROLE test_user4 PASSWORD 'MyP@ssw0rd';  -- 成功:符合所有要求
ERROR:  password is too short
ERROR:  password must contain both letters and nonletters
ERROR:  password must not contain user name
CREATE ROLE
  • 不符合要求的密码示例

ALTER ROLE test_user4 PASSWORD '123456';  -- 将失败:密码太短
ALTER ROLE test_user4 PASSWORD 'abcdefgh';  -- 将失败:没有非字母字符
  • 符合要求的密码示例

ALTER ROLE test_user4 PASSWORD 'NewP@ssw0rd';  -- 成功:符合所有要求
ERROR:  password is too short
ERROR:  password must contain both letters and nonletters
ALTER ROLE
unvdb=# 

udaudit 审计

实现DDL,DML操作的日志记录

安装

  • 修改unvdbsvr.conf添加共享库 在配置文件中添加: shared_preload_libraries = 'udaudit'  # 添加udaudit到共享库列表

  • 重启数据库并创建插件

unvdb=# create extension udaudit;
CREATE EXTENSION

配置审计选项

  • 查看当前配置

unvdb=# -- 查看审计日志级别
SHOW udaudit.log;
 udaudit.log 
-------------
 none
(1 row)
unvdb=# -- 查看审计日志输出级别 
SHOW udaudit.log_level;
 udaudit.log_level 
-------------------
 log
(1 row)
unvdb=# -- 查看其他相关配置
SHOW udaudit.log_client;
SHOW udaudit.log_catalog;
SHOW udaudit.log_parameter;
 udaudit.log_client 
--------------------
 off
(1 row)

 udaudit.log_catalog 
---------------------
 on
(1 row)

 udaudit.log_parameter 
-----------------------
 off
(1 row)
  • 修改配置

unvdb=# -- 设置审计级别(可选:DDL,FUNCTION,MISC,READ,ROLE,WRITE等)
ALTER SYSTEM SET udaudit.log = 'DDL, READ, WRITE';

-- 设置日志输出级别
ALTER SYSTEM SET udaudit.log_level = 'notice';

-- 启用客户端日志显示
ALTER SYSTEM SET udaudit.log_client = on;

-- 重新加载配置
SELECT pg_reload_conf();
ALTER SYSTEM
ALTER SYSTEM
ALTER SYSTEM
 pg_reload_conf 
----------------
 t
(1 row)

审计功能测试

  • DDL审计

unvdb=# -- 创建测试表
CREATE TABLE test_audit(id int, name text);

-- 查看审计日志,将显示类似:
-- AUDIT: SESSION,1,1,DDL,CREATE TABLE,TABLE,public.test_audit,...

-- 修改表结构
ALTER TABLE test_audit ADD COLUMN email text;

-- 删除表
DROP TABLE test_audit;
NOTICE:  AUDIT: SESSION,1,1,DDL,CREATE TABLE,TABLE,public.test_audit,"CREATE TABLE test_audit(id int, name text)",<not logged>
CREATE TABLE
NOTICE:  AUDIT: SESSION,2,1,DDL,ALTER TABLE,TABLE,public.test_audit,ALTER TABLE test_audit ADD COLUMN email text,<not logged>
ALTER TABLE
NOTICE:  AUDIT: SESSION,3,1,DDL,DROP TABLE,TABLE,public.test_audit,DROP TABLE test_audit,<not logged>
DROP TABLE
  • DML审计

unvdb=# -- 创建测试表
CREATE TABLE test_audit(id int, name text);
NOTICE:  AUDIT: SESSION,4,1,DDL,CREATE TABLE,TABLE,public.test_audit,"CREATE TABLE test_audit(id int, name text)",<not logged>
CREATE TABLE
unvdb=# -- 插入数据
INSERT INTO test_audit VALUES(1, 'test');

-- 查询数据
SELECT * FROM test_audit;

-- 更新数据
UPDATE test_audit SET name = 'test2' WHERE id = 1;

-- 删除数据
DELETE FROM test_audit WHERE id = 1;
NOTICE:  AUDIT: SESSION,5,1,WRITE,INSERT,,,"INSERT INTO test_audit VALUES(1, 'test')",<not logged>
INSERT 0 1
NOTICE:  AUDIT: SESSION,6,1,READ,SELECT,,,SELECT * FROM test_audit,<not logged>
 id | name 
----+------
  1 | test
(1 row)

NOTICE:  AUDIT: SESSION,7,1,WRITE,UPDATE,,,UPDATE test_audit SET name = 'test2' WHERE id = 1,<not logged>
UPDATE 1
NOTICE:  AUDIT: SESSION,8,1,WRITE,DELETE,,,DELETE FROM test_audit WHERE id = 1,<not logged>
DELETE 1
  • 用户操作审计

unvdb=# -- 创建角色
CREATE ROLE audit_test WITH LOGIN PASSWORD 'password';

-- 授权操作
GRANT SELECT ON test_audit TO audit_test;

-- 回收权限
REVOKE SELECT ON test_audit FROM audit_test;
CREATE ROLE
GRANT
REVOKE

常用审计配置说明

udaudit.log: 指定需要审计的操作类型

  • DDL: 数据定义语言

  • FUNCTION: 函数调用

  • MISC: 其他操作

  • READ: SELECT操作

  • ROLE: 用户和权限操作

  • WRITE: INSERT/UPDATE/DELETE操作

udaudit.log_level: 指定审计日志的输出级别

  • debug

  • info

  • notice

  • warning

  • log

udaudit.log_client: 是否在客户端显示审计日志

  • on: 显示

  • off: 不显示

udaudit.log_catalog: 是否审计系统表操作

  • on: 审计

  • off: 不审计

审计日志

审计日志格式说明: AUDIT: [类型],[语句ID],[子语句ID],[操作类别],[命令],[对象类型],[对象名],[SQL语句]

举例: AUDIT: SESSION,1,1,DDL,CREATE TABLE,TABLE,public.test_audit,CREATE TABLE test_audit(id int, name text)

可以通过以下方式查看审计日志:

数据库日志文件 系统日志(如果配置输出到syslog) 客户端日志(如果udaudit.log_client=on)

ud_auth_mon 登录失败锁定

此功能实现登录失败N次后锁定N分钟,有效避免密码爆破对数据库产生更大的影响.

安装

修改unvdbsvr.conf shared_preload_libraries = 'ud_auth_mon'

创建插件

drop extension if exists ud_auth_mon;
create extension ud_auth_mon;

查看和修改配置

--Maximum number of failed login attempts before lockout
unvdb=# show ud_auth_mon.max_failed_attempts;
 ud_auth_mon.max_failed_attempts 
--------------------------------
 3
(1 row)
--Account lockout period in minutes
unvdb=# show ud_auth_mon.lockout_period;
 ud_auth_mon.lockout_period 
---------------------------
 15min
(1 row)
unvdb=# alter system set ud_auth_mon.max_failed_attempts = 2;
ALTER SYSTEM
unvdb=# alter system set ud_auth_mon.lockout_period = 5;
ALTER SYSTEM
unvdb=# select pg_reload_conf();
 pg_reload_conf 
---------------
 t
(1 row)
unvdb=# show ud_auth_mon.max_failed_attempts;
 ud_auth_mon.max_failed_attempts 
--------------------------------
 2
(1 row)
unvdb=# show ud_auth_mon.lockout_period;
 ud_auth_mon.lockout_period 
---------------------------
 5min
(1 row)

查看初始状态

unvdb=# select * from ud_auth_mon;
 rolname | unknown_user | uid | successful_attempts |      last_successful_ts       | total_hba_conflicts | total_auth_failures | last_failed_ts | recent_failures | lockout_until 
---------+--------------+-----+---------------------+-------------------------------+---------------------+---------------------+----------------+-----------------+---------------
 unvdb   | f            |  10 |                   1 | 2025-01-03 13:24:16.032898+08 |                   0 |                   0 |                |               0 | 
(1 row)

第一次失败尝试

[user@localhost]$ ud_sql -h 192.168.4.216 -p 5678 -d unvdb -U u1 Password for user u1: wrong ud_sql: error: connection to server at “192.168.4.216”, port 5678 failed: FATAL: password authentication failed for user “u1”

第二次失败尝试

[user@localhost]$ ud_sql -h 192.168.4.216 -p 5678 -d unvdb -U u1 Password for user u1: wrong ud_sql: error: connection to server at “192.168.4.216”, port 5678 failed: FATAL: account is locked due to too many failed attempts DETAIL: Please try again after 5 minutes

查看状态:

unvdb=# select * from ud_auth_mon;

  • 现在测试锁定期间使用正确密码

[user@localhost]$ ud_sql -h 192.168.4.216 -p 5678 -d unvdb -U u1
Password for user u1: correct_password
ud_sql: error: connection to server at "192.168.4.216", port 5678 failed: FATAL:  account is locked due to too many failed attempts
DETAIL:  Please try again after 4 minutes
  • 测试解锁功能

unvdb=# select reset_auth_lockout('u1');
 reset_auth_lockout 
--------------------
 
(1 row)

unvdb=# select * from ud_auth_mon;
  • 解锁后尝试登录

[user@localhost]$ ud_sql -h 192.168.4.216 -p 5678 -d unvdb -U u1
Password for user u1: correct_password
ud_sql (24.2, server 22.4)
Type "help" for help.

unvdb=> 

恢复原始配置

alter system set ud_auth_mon.max_failed_attempts = 3;
alter system set ud_auth_mon.lockout_period = 15;
select pg_reload_conf();
show ud_auth_mon.max_failed_attempts;

show ud_auth_mon.lockout_period;