PostgreSQL 生产安全指南:权限体系、行级安全策略与 SSL 硬实战

详解 PostgreSQL 生产安全加固:角色与权限体系(GRANT/REVOKE/Row Level Security)、SSL/TLS 加密配置、pg_hba.conf 认证矩阵、审计日志(pgaudit)、CVE 修复流程、CIS Benchmark 安全基线,覆盖从安装到运维的全生命周期安全策略。

数据库中的每一条用户密码、每一笔交易记录、每一封邮件地址,都是攻击者垂涎的目标。PostgreSQL 的安全不是选配,而是生产环境的基本要求——从合法用户的权限最小化,到数据传输的端到端加密,再到事后的审计追踪,每一层都必须加固。

本文按"身份认证 → 访问控制 → 数据加密 → 审计追踪 → 漏洞管理"五层递进,提供 PostgreSQL 生产安全加固的完整方案。

一句话总结:每条操作可追溯到人、每张表可精确到行、每比特数据在网络上传输都加密。


一、角色与权限体系

1.1 角色层级设计

PostgreSQL 的角色(role)= 用户 + 权限组。推荐的生产角色设计:

-- 1. 应用层角色(禁止直接登录)
CREATE ROLE app_read NOLOGIN;         -- 只读查询
CREATE ROLE app_write NOLOGIN;        -- 读写
CREATE ROLE app_admin NOLOGIN;        -- DDL + DCL

-- 2. 业务功能角色(禁止直接登录,用于逻辑分组)
CREATE ROLE analytics NOLOGIN;        -- 数据分析
CREATE ROLE scheduler NOLOGIN;        -- 定时任务

-- 3. 登录用户(供应用程序使用)
CREATE USER app_backend WITH PASSWORD 'complex_password_123!' LOGIN;
CREATE USER app_worker  WITH PASSWORD 'another_password_456!' LOGIN;

-- 4. 角色继承
GRANT app_read TO app_backend;
GRANT app_write TO app_worker;
GRANT analytics TO app_backend;

1.2 权限矩阵

权限描述风险推荐做法
SELECT读取数据泄露按需分配,拒绝 *
INSERT新增数据篡改明确目标表
UPDATE修改数据篡改/覆盖限制 WHERE
DELETE删除数据数据丢失极少量用户持有
TRUNCATE清空表灾难性丢失仅管理员
REFERENCES创建外键-需要时赋予
TRIGGER创建触发器隐蔽攻击仅管理员
EXECUTE执行函数取决于函数按需分配

1.3 权限分配示例

-- Schema 级权限
GRANT USAGE ON SCHEMA app TO app_read;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA app TO app_write;

-- 表级细粒度权限(拒绝 SELECT on password_hash)
GRANT SELECT(id, email, name, role) ON users TO app_read;
-- app_read 无法 SELECT password_hash 列

-- 行级更新权限(仅允许更新自己的记录,需配合 RLS)
-- 见下节

-- 限制用户对系统表的访问
REVOKE ALL ON DATABASE myapp FROM PUBLIC;
REVOKE ALL ON SCHEMA public FROM PUBLIC;

-- 清除不必要的默认权限
ALTER DEFAULT PRIVILEGES IN SCHEMA app REVOKE ALL ON TABLES FROM PUBLIC;
ALTER DEFAULT PRIVILEGES IN SCHEMA app GRANT SELECT ON TABLES TO app_read;

二、行级安全策略(RLS)

Row Level Security 是 PostgreSQL 最精细的权限控制,允许在表级策略中定义**“每行数据谁能看”**。

2.1 RLS 启用与策略定义

-- 启用 RLS(默认对表拥有者绕过,对普通用户生效)
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;

-- 创建策略:用户只能看到自己的订单
CREATE POLICY user_order_isolation ON orders
  FOR ALL
  TO app_backend
  USING (user_id = CURRENT_USER_ID());

-- 策略:管理员能看到所有
CREATE POLICY admin_all_orders ON orders
  FOR SELECT
  TO app_admin
  USING (TRUE);

2.2 CURRENT_USER 变量映射

在大多数 ORM(Prisma/Sequelize)中,CURRENT_USER 即数据库连接用户,不适合做应用级用户区分。推荐做法:

-- 为每个请求设置 session 变量
SET app.current_user_id = '123';

-- RLS 策略读取 session 变量
CREATE POLICY user_order_isolation ON orders
  FOR ALL
  TO app_backend
  USING (
    user_id = NULLIF(current_setting('app.current_user_id', true), '')::INT
  );

在应用程序中(Prisma Middleware):

prisma.$use(async (params, next) => {
  await prisma.$executeRaw`SET app.current_user_id = '${userId}'`;
  const result = await next(params);
  await prisma.$executeRaw`SET app.current_user_id = ''`;
  return result;
});

2.3 RLS 策略矩阵

策略名目标表适用操作表达式用户
用户级隔离ordersALLuser_id = ?app_backend
部门级隔离documentsSELECTdept_id IN (SELECT dept_id FROM user_depts WHERE user_id = ?)app_backend
公开查询productsSELECTpublished = trueapp_read
完全开放audit_logsSELECTTRUEapp_admin

三、pg_hba.conf:认证规则

3.1 文件位置与结构

pg_hba.conf(Host-Based Authentication Configuration)定义谁能从哪来、用什么方式认证

位置(典型):/var/lib/postgresql/data/pg_hba.conf

格式:TYPE DATABASE USER ADDRESS METHOD [OPTIONS]

3.2 认证方法对比

方法场景安全性说明
scram-sha-256生产(默认推荐)安全的密码哈希
md5旧版本兼容MD5 已被破解,逐步淘汰
password开发测试明文传输,绝不用
trust开发测试(Unix Socket)无认证,仅本地 sock
peer本地 Unix Socket验证 OS 用户名
cert客户端证书最高双向 mTLS 认证
ldap/radius企业集成外部认证服务器
gss/kerberos大型企业Kerberos 认证

3.3 生产配置文件示例

# TYPE  DATABASE        USER            ADDRESS                 METHOD

# 本地 socket:.
local   all             postgres                                peer

# 本地 TCP(开发用途,仅允许本地回环)
host    postgres        all             127.0.0.1/32            scram-sha-256
host    postgres        all             ::1/128                 scram-sha-256

# 应用服务器访问(限制网段)
host    myapp,analytics app_backend     10.0.1.0/24             scram-sha-256
host    myapp           app_worker      10.0.2.0/24             scram-sha-256

# 监控工具
host    postgres        prometheus      10.0.3.0/24             scram-sha-256

# 拒绝所有其他(默认规则)——安全基线
host    all             all             0.0.0.0/0               reject
host    all             all             ::/0                    reject

关键安全实践

  • 绝不用 trustpassword 方法
  • 绝不允许 host all all 0.0.0.0/0 的开放规则
  • 生产环境关闭 listen_addresses = '10.0.1.0/24,127.0.0.1'

四、SSL/TLS 加密

4.1 传输层加密

# postgresql.conf
ssl = on
ssl_cert_file = '/etc/ssl/certs/server.crt'
ssl_key_file = '/etc/ssl/private/server.key'
ssl_ca_file = '/etc/ssl/certs/ca.crt'        # 客户端证书认证时必需

# 强制 SSL(拒绝明文连接)
ssl_min_protocol_version = 'TLSv1.2'
ssl_ciphers = 'HIGH:!aNULL:!MD5'

# 在 pg_hba.conf 中强制 SSL
hostssl myapp app_backend 10.0.1.0/24 scram-sha-256

4.2 客户端证书认证(mTLS)

# pg_hba.conf — 用客户端证书替代密码
hostssl myapp app_backend 10.0.1.0/24 cert

# 客户端连接字符串
DATABASE_URL="postgresql://app_backend@db.example.com:5432/myapp?sslmode=verify-full&sslcert=/path/to/client.crt&sslkey=/path/to/client.key&sslrootcert=/path/to/ca.crt"

4.3 SSL 模式对比

模式行为推荐
disable不使用 SSL绝不生产
allow优先尝试 SSL不推荐
prefer尝试 SSL,失败回退不推荐
require强制 SSL,但不验证证书基础方案
verify-ca强制 SSL + 验证 CA推荐方案
verify-full强制 SSL + CA + 主机名验证最高安全

五、审计日志(pgaudit)

5.1 启用审计

-- postgresql.conf
shared_preload_libraries = 'pg_stat_statements,pgaudit'
pgaudit.log = 'write, ddl'         -- 审计写操作 + DDL
pgaudit.log_catalog = off          -- 不审计系统目录
pgaudit.log_parameter = on         -- 审计时记录参数值
pgaudit.log_relation = on          -- 对象级审计
pgaudit.role = 'auditor'           -- 对 auditor 角色审计

-- 安装 pgaudit 扩展
CREATE EXTENSION IF NOT EXISTS pgaudit;

5.2 审计规则示例

-- 审计敏感表的所有操作
CREATE AUDIT POLICY access_sensitive_table ON users
  FOR ALL
  EXECUTE FUNCTION pgaudit_ddl_command_end();

-- 查看审计日志(经过 log_destination 输出到文件后解析)
-- 典型输出:
-- 2025-08-01 10:00:00 UTC LOG:  AUDIT: SESSION,1,1,WRITE,INSERT,TABLE,public.users,,"INSERT INTO users ... (bob)"

六、CVE 修复与安全维护

6.1 PostgreSQL 安全公告追踪

官方安全发布源:https://www.postgresql.org/support/security/

6.2 安全补丁流程

# 1. 查看当前版本
SELECT version(); -- PostgreSQL 17.4

# 2. 检查是否有安全更新
# 访问 https://www.postgresql.org/docs/current/release.html

# 3. 升级(小版本原地升级)
sudo apt update && sudo apt install postgresql-17  # Debian/Ubuntu
brew upgrade postgresql@17                          # macOS

# 4. 重启
sudo systemctl restart postgresql

# 5. 验证
SELECT version();

6.3 生产环境 CVE 修复 checklist

□ 订阅 PostgreSQL 安全公告邮件列表(pgsql-announce)
□ 定义 RTO/RPO:关键安全补丁需多长时间内应用?
□ 维护窗口:制定每月维护窗口用于安全更新
□ 灰度测试:先在 staging 环境验证补丁兼容性
□ 回滚方案:补丁失败时如何快速回退?
□ 应用层兼容:PostgreSQL 小版本升级(如 17.3 → 17.4)通常是安全的

七、CIS Benchmark 安全基线

CIS(Center for Internet Security)发布了 PostgreSQL 的安全基线检查清单。以下是关键检查项:

编号检查项基线要求
1.1移除 trust 认证所有连接必须用 scram-sha-256 或更强
1.2移除明文密码pg_hba.conf 无 password
2.1文件权限data_directory 权限为 0700
2.2日志文件权限日志文件权限为 0600
3.1启用 SSLssl = on
3.2最低 TLS 版本ssl_min_protocol_version = 'TLSv1.2'
4.1审计 Write 操作pgaudit.log = 'write, ddl'
4.2审计登录尝试log_connections = on
5.1限制超级用户仅管理员持有 superuser
5.2RLS 启用检查敏感表 ALTER TABLE ... ENABLE ROW LEVEL SECURITY
6.1连接数限制max_connections 按硬件合理设置
6.2超时限制statement_timeout = 30s(防止慢查询拖垮)

常见问题(FAQ)

RLS 对性能有影响吗?

有影响,但通常 < 5%。RLS 在规划时添加过滤谓词,正确使用索引后影响可忽略。如果表行数极大(亿级)且策略复杂,需在策略条件列上加索引。

pg_hba.conf 修改后不生效?

执行 SELECT pg_reload_conf();pg_ctl reload 重载配置。不需要重启 PostgreSQL

密码能在 pg_hba.conf 中配置吗?

不能。PostgreSQL 的用户密码存储在数据库内部(pg_authid 表),不在 pg_hba.conf 中。企业环境可用 LDAP/Kerberos/证书替代本地密码。

审计日志会拖慢性能吗?

是。pgaudit 每次审计创建一个 LOG 记录,写操作频繁时建议:

  • 只审计 writeddl,不审计 read
  • 使用异步日志(rsyslog 异步模式)
  • 日志文件输出到独立磁盘

相关阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「database」更多文章