数据库中的每一条用户密码、每一笔交易记录、每一封邮件地址,都是攻击者垂涎的目标。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 策略矩阵
| 策略名 | 目标表 | 适用操作 | 表达式 | 用户 |
|---|---|---|---|---|
| 用户级隔离 | orders | ALL | user_id = ? | app_backend |
| 部门级隔离 | documents | SELECT | dept_id IN (SELECT dept_id FROM user_depts WHERE user_id = ?) | app_backend |
| 公开查询 | products | SELECT | published = true | app_read |
| 完全开放 | audit_logs | SELECT | TRUE | app_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
关键安全实践:
- 绝不用
trust或password方法 - 绝不允许
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 | 启用 SSL | ssl = 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.2 | RLS 启用检查 | 敏感表 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 记录,写操作频繁时建议:
- 只审计
write和ddl,不审计read - 使用异步日志(rsyslog 异步模式)
- 日志文件输出到独立磁盘
相关阅读
- PostgreSQL 详解 — 核心架构、权限体系基础
- PostgreSQL Docker 部署与初始化 — 容器化安全配置
- PostgreSQL 高可用与备份 — 流复制、Patroni
- PostgreSQL 性能优化 — shared_buffers、连接池
- PostgreSQL 专题导航
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。