数据库安全

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

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;

ud_tde 透明加密

透明存储加密方式是一种保护数据库中静态数据的加密技术,使数据在存储时自动加密,有助于防止未授权访问和数据泄露。

安装

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

  • 重启数据库并创建插件

unvdb=# create extension ud_tde;
CREATE EXTENSION

配置TDE选项

  • 查看当前配置

unvdb=# -- 显示数据库的主密钥信息
SELECT  * FROM ud_tde_principal_key_info();
 principal_key_name | key_provider_name | key_provider_id | principal_key_internal_name | principal_key_version |      key_createion_time
       
--------------------+-------------------+-----------------+-----------------------------+-----------------------+-------------------------------
 my-principal-key | file              |               1 | my-principal-key_2        |                     2 | 2024-12-12 15:12:26.017167+08
(1 row)
  • 创建密钥配置

--使用本地文件创建一个新的数据库密钥提供者来存储主密钥。
--函数定义
ud_tde_add_key_provider_file(provider_name VARCHAR(128), file_path TEXT)
--用法
SELECT ud_tde_add_key_provider_file('provider-name','/path/to/the/keyring/data.file');
--示例
SELEC ud_tde_add_key_provider_file('file','myudkeyring'); 

--使用指定的密钥提供者设置数据库的主密钥
--函数定义
ud_tde_set_principal_key(principal_key_name VARCHAR(255), provider_name VARCHAR(255), ensure_new_key BOOLEAN DEFAULT FALSE)
--用法
SELECT ud_tde_set_principal_key('name-of-the-principal-key', 'provider-name');
--用例
SELECT ud_tde_set_principal_key('my-principal-key','file'); 

--创建指定主密钥的新版本,并更新数据库,使其使用新的主密钥版本
--函数定义
ud_tde_rotate_key(new_principal_key_name VARCHAR(255) DEFAULT NULL, new_provider_name VARCHAR(255) DEFAULT NULL, ensure_new_key BOOLEAN DEFAULT TRUE)
--用法
SELECT ud_tde_rotate_principal_key('name-of-the-new-principal-key', 'name-of-the-new-provider');
--使用同样的提供者,创建新的主密钥
SELECT ud_tde_rotate_principal_key('name-of-the-new-principal-key', NULL);
--使用同样的主密钥到新的提供者
SELECT ud_tde_rotate_principal_key(NULL, 'name-of-the-new-provider');
--使用同样的提供者,更新数据库当前主密钥的版本
SELECT ud_tde_rotate_principal_key();
--用例
SELECT ud_tde_rotate_principal_key();
unvdb=# -- 查看其他相关配置
SELECT  * FROM ud_tde_principal_key_info();
  • 表透明加密配置

--创建加密表
--需要在表的定义最后加上 USING ud_tde_basic关键字。如下所示:
CREATE TABLE tablename table definition USING ud_tde_basic;
--示例
CREATE TABLE tb_tel(id int, name varchar(20),tel varchar(20)) USING ud_tde_basic;

--修改加密状态
ALTER TABLE tablename SET access method ud_tde_basic;
--修改为非加密状态
ALTER TABLE tablename SET access method heap;
--示例
CREATE TABLE tb (id INT);
ALTER TABLE tb SET access method ud_tde;
ALTER TABLE tb SET access method heap;

--查看表的机密状态
SELECT udtde_is_encrypted('tablename');
SELECT udtde_is_encrypted('schema.tablename');

--测试加密的另外验证方法
CREATE TABLE tb_tel(id int, name varchar(20),tel varchar(20)) USING ud_tde_basic;
INSERT INTO tb_tel values(1, 'laochen','13812348888'),(2, 'laowang','13812346666');
SELECT pg_relation_filepath('tb_tel');
 pg_relation_filepath 
----------------------
 base/5/16411
(1 row)
hexdump -c base/5/16411 |grep c 
--查看数据文件已经无法显示完整的字符,说明表tb_tel已经被加密。

TDE功能测试

  • 完整测试示例

SELECT ud_tde_add_key_provider_file('file3','myudkeyring3'); --使用本地文件创建一个新的数据库密钥提供者来存储主密钥。
SELECT ud_tde_set_principal_key('my-principal-key3','file3');--使用指定的密钥提供者设置数据库的主密钥
SELECT  * FROM ud_tde_principal_key_info(); --查看密钥信息

SELECT ud_tde_rotate_key('my-principal-key4','file3') --更改主密钥(可选)

CREATE TABLE tb_tel_tde(id int, name varchar(20),tel varchar(20)) USING ud_tde_basic; --创建加密表
insert into tb_tel_tde values(1,'songxd','15801402675'); --插入表数据
SELECT  * FROM ud_tde_principal_key_info(); --查看密钥信息
SELECT udtde_is_encrypted('tb_tel_tde'); --查看表是否加密

透明存储加密(TDE)与其他功能结合

主备集群的透明存储加密:

对于表加密。九有数据库集群支持表透明存储加密。如果主库使用表加密,相关信息会自动同步到备库中。主库如果修改加密密钥,集群会将密钥信息自动同步到备库中。

透明存储加密与接口的结合:

透明存储加密对九有数据库程序接口调用的操作没有任何影响,对应用程序透明。除了传输安全部分,透明存储操作对应用接口透明。

使用透明存储加密的注意事项

• 为加密数据设置单独的安全管理员。

• 确定数据库中数据的敏感度,所需要的保密和风险级别。不敏感的数据不需要进行加密操作。

• 管理数据文件的安全性及时删除一些加密前的文件,旧的文件可能存在明文数据。如果特权操作系统用户绕过数据库访问控制,可能直接获取数据信息。为避免这种项目,可以将加密的对象移动到新的表空间中,然后删除原来的表空间文件。