23.5 密钥、审计与敏感信息
数据库安全材料不只是一串密码:
其中任何一项若进入 Git、命令行历史、日志、监控标签或实验产物,后续的 权限设计都可能失效。另一方面,为了“绝不记录敏感信息”而关闭所有日志,也 会让越权事件无法发现和调查。
本节处理的是这组张力:秘密必须最小暴露,安全行为必须留下足够而受保护的 证据。
23.5.1 凭据生成、存放、轮换和撤销
先建立 secret inventory
每类 secret 都要有 owner 和生命周期:
| 字段 | 要回答的问题 |
|---|---|
| identity | 它代表哪个人、服务或组件 |
| scope | 能访问哪些入口、数据库和角色 |
| source | 谁生成,熵和算法是否合格 |
| storage | secret manager、HSM、受限文件还是其他载体 |
| delivery | 哪个 workload 如何获得 |
| readers | 哪些人、服务账户和进程可读 |
| lifetime | 创建、启用、到期和最大使用时长 |
| rotation | 是否支持重叠版本,多久轮换 |
| revocation | 如何阻止新认证 |
| session eviction | 如何处理既有连接 |
| downstream | 是否复制到代理、CI、备份或灾备 |
| evidence | 如何证明已完成且不导出秘密 |
如果连“有几份副本”都不知道,就无法声称秘密已经撤销。
生成
机器凭据应由密码学安全随机源生成,避免:
长度和字符集要兼容客户端、URI、配置格式与 secret manager;不要为了规避 转义问题而把熵降得过低。优先通过结构化参数或独立字段传递,避免把密码拼进 连接 URI。
证书与 key 的生成还要固定:
CA private key 与数据库 server key 不应由同一批日常运维主体任意读取。
存放和交付
首选工作流:
如果使用文件:
- 明确 owner/group;
- 通常使用
0600或经过评审的0640; - 目录同样不可遍历;
- 不写入镜像层、共享 volume 或备份;
- 不把内容输出到 diagnostics;
- 用完安全删除临时副本,并考虑文件系统/快照语义。
.pgpass 要求严格权限,它适合受控本地客户端,不是企业 secret manager。
环境变量适合某些运行时注入,但必须处理进程继承、crash dump、support bundle
和调度平台元数据风险。
最危险的交付路径通常很方便:
URI 可能进入 process list、shell history、trace、错误和工单。即使工具会 隐藏部分内容,也不应把安全性押在每个中间层都正确脱敏。
数据库密码变更
交互式 psql 可使用:
它在客户端提示密码并发送 verifier,避免明文出现在命令历史和 server log。
官方说明见 psql \password。直接执行:
可能让明文进入 client history 或 server log;PostgreSQL 官方也明确警告
这一点,参见 ALTER ROLE。
自动化系统应通过 secret-safe API、受控 stdin/fd 或专门管理函数完成,且 验证流水线不会回显命令与异常。
轮换是状态机
密码轮换不能只有“ALTER 成功”:
每个状态需要证据与回滚条件。若 PostgreSQL role 只能保存一个当前 verifier, S2 与 S3 之间的兼容窗口可以采用:
- 蓝绿两个 login role;
- 应用小批量快速 rollout;
- 代理/身份系统支持的双版本机制;
- 计划内短暂重连窗口。
不要假装一个 role 可以同时接受两个普通 PostgreSQL 密码。
撤销新认证与终止旧会话是两件事
本章的受控临时 login 实测:
因此应急撤销至少有两条动作:
以及在识别范围并评估事务影响后:
第二条是有破坏性的:会中断事务,产生 commit outcome uncertainty,并可能 触发重连风暴。必须先阻止代理和应用继续获取旧 secret,再终止既有 session。
代理层也要轮换
PgBouncer 的 auth_file、auth_query、auth_user 和 server login 都可能
持有或派生认证材料。轮换时要明确:
哪一段在变。更新 PostgreSQL verifier 后:
- PgBouncer 是否缓存旧认证材料;
- 是否需要
RELOAD; - 是否需要
RECONNECTserver pools; - 已有 client connection 是否继续使用;
- 所有 PgBouncer 节点是否完成;
- 直连和池化入口是否给出一致的允许/拒绝结果。
本章临时 role 直连新认证成功,但池入口拒绝,因为它没有进入 PgBouncer 认证面。这是预期的最小暴露,不是轮换失败。
证书和 CA 轮换
证书轮换同样要区分:
PostgreSQL reload 新 server certificate 不会让所有现有 TLS session 自动 重新握手。PgBouncer 两侧又各有 TLS 状态。验收必须建立新连接并核对证书 fingerprint/issuer/SAN,而不是只看文件 mtime。
CA rollover 先扩充 trust bundle,再切 server/client certificate,最后等 fleet 全部迁移后删除旧 CA。倒序操作会把尚未更新的客户端全部拒绝。
应急凭据
break-glass credential 应:
- 与日常 workload secret 分开;
- 双人或受审批取用;
- 短时激活;
- 不进入普通自动化;
- 使用后立即轮换;
- 关联 incident/change id;
- 从数据库外部收集不可篡改证据;
- 定期演练“能取出、能使用、能回收”。
一个从未测试、到事故时才发现过期的应急密码,不是恢复能力。
secret-free 证据
证明轮换无需保存 secret:
本章证据显式拒绝:
它检查“secret signature 不存在”,同时对证据文件使用 0600。文件权限不能
替代内容最小化;二者都要做。
23.5.2 审计目标、日志范围与访问控制
从调查问题反推记录
先定义要回答的问题:
“记录所有 SQL”并不能自动回答这些问题,反而可能产生无法检索的海量敏感 数据。
四类证据互相补充
| 来源 | 擅长回答 | 局限 |
|---|---|---|
| PostgreSQL standard log | 连接、错误、慢 SQL、DDL、运行事件 | 不是完整对象审计 |
| pgAudit | 结构化 session/object audit classes | 有容量成本,superuser 不可可靠自审 |
| Pigsty/config repository | 谁声明、评审、发布了配置 | 不证明运行实例已收敛 |
| host/proxy/secret manager/IdP | 网络入口、secret 读取、外部身份 | 不知道 SQL 对象语义 |
还应关联:
共享应用 login 下,数据库看不到每个终端用户,应用必须提供受信 actor/request
关联。不要把用户可自行填写的 application_name 当成强身份。
standard log
PostgreSQL 18 可分别记录 connection receipt、authentication、 authorization、setup duration,以及 disconnect。还可记录:
- error statement;
- DDL/DML/all statement classes;
- duration threshold 或 sampling;
- lock/recovery/autovacuum/checkpoint;
- SQLSTATE、session id、transaction id、query id;
- application/database/user/client address。
log_line_prefix 至少要支持跨行关联,例如:
具体字段按日志格式和数据分类选择。若使用 JSON/CSV,仍要确保 collector、 rotation、磁盘满和转发失败被监控。PostgreSQL logging collector 为避免丢 消息可能在落后时阻塞 backend;syslog 则可能选择丢消息。这也是容量设计, 不是简单开关。
pgAudit 能补什么
pgAudit 将行为分为 READ、WRITE、FUNCTION、ROLE、DDL、MISC
等审计类,并能提供更适合对象审计的记录。部署要点:
只安装 package 或只执行 CREATE EXTENSION 都不等于审计已经运行。官方项目
说明见 pgAudit。
不要默认 pgaudit.log=all:
- 高频 SELECT 会产生巨大日志;
- bind parameter 可能含 PII/secret;
- 日志 IO/转发可能成为负载瓶颈;
- 噪声可能淹没 role/DDL 等关键事件;
- retention 成本和访问面迅速扩大。
应从审计目标选择 session classes 或 object audit,并对新表纳入策略做持续 检查。
superuser 不能可靠地审计自己
pgAudit 官方明确指出,不能可靠审计 superuser。superuser 能改变设置、停用 扩展、修改日志路径或干预本机数据。解决思路不是再加一条数据库内 trigger, 而是:
“不可变”要具体:谁拥有 bucket retention policy、谁能删除 collector、日志 在源端滞留多久、断网时如何缓冲、时间如何同步,都要写进控制设计。
角色和授权变更
高价值事件:
仅记录成功 DDL 不够。还需:
- 失败尝试;
- 变更前后投影或配置 diff;
- 审批 id;
- 执行主体;
- 节点收敛状态;
- 正负验收;
- 回滚结果。
日志本身是敏感数据
日志可能包含:
因此要实施:
- 最小读权限,读日志本身也审计;
- 传输和静态加密;
- retention 与合法删除;
- 环境/租户隔离;
- 索引系统访问控制;
- 防止下载到个人设备;
- incident legal hold;
- collector health 与 ingestion gap 告警。
本章环境结论
沙箱运行事实:
它能支持实验和部分运维诊断,不能被描述为完整生产审计。生产 gate 因此保留
pending,不是把“有日志”写成“满足审计要求”。
23.5.3 参数、SQL 文本与日志脱敏
参数绑定解决注入,不保证不落日志
应用正确使用:
能避免把 $1 当 SQL 语法解释,但 extended query protocol 的 Bind value
仍可能被 statement/duration/audit/error logging 记录。安全评审必须把:
当成两个问题。
PostgreSQL 的参数日志开关
PostgreSQL 18:
精确行为见 PostgreSQL:错误报告与日志。
本章沙箱:
含义是:
- error path 默认不附 bind values;
- 非 error 的 statement/duration logging 路径可能保留完整 bind values。
因此它被标记为生产差距。不能只因为 error 参数为 0 就得出“参数不进日志”。
截断不是脱敏
把每个参数截断到 64 bytes 仍可能完整暴露:
0 能阻止特定 PostgreSQL log path 记录 bind 参数,但:
- SQL literal 仍在 statement text;
- application log 可能记录参数;
- pgAudit/extension 行为要单独验证;
- error message 可能引用业务值;
- trigger/function 自己可能
RAISE LOG; - proxy/APM/driver trace 可能复制 SQL。
脱敏必须是端到端数据流评审。
不要把秘密写进 SQL literal
高风险语句:
即便业务表有 RLS,这些 literal 也可能进入 statement、DDL、audit、客户端 历史或 trace。对于 credential provisioning,使用专门的 secret-safe 通道; 对于业务 secret,使用参数绑定并缩小数据库日志范围。
在源头分类字段
建议把请求数据分成:
| 类别 | 示例 | 日志策略 |
|---|---|---|
| public operational | version、region、status | 可结构化记录 |
| internal identifier | request id、tenant surrogate id | 最小化、受控保留 |
| personal/confidential | email、address、business data | 默认不记 value |
| credential/cryptographic | password、token、private key | 永不记录 |
| regulated/highly sensitive | payment/health/government id | 专门政策与审计 |
数据库团队不能只靠列名猜分类。应用 schema、数据目录和日志 policy 要共享 同一份分类元数据。
SQL fingerprint 与 value 分离
性能分析通常不需要参数值。优先保留:
而不是:
第 25、26 章会用 pg_stat_statements、query id 和计划证据分析性能;这些
方法能显著减少为了可观测而复制业务值的必要。
pipeline 脱敏是第二道防线
collector 侧可以:
- 删除已知敏感字段;
- token/password pattern 检测;
- 限制异常样本和 payload;
- 对 identifier 做受控 pseudonymization;
- 阻止包含 private key/verifier 的事件;
- 记录 redaction rule version。
但 regex 不能成为唯一控制。SQL 语法、编码、嵌套 JSON、base64 和未知字段 会绕过它。首要措施仍是在源端不产出秘密。
脱敏测试
上线前使用纯 synthetic canary:
执行经过批准的测试请求,然后检查:
期望是 credential marker 零命中,其他 marker 只出现在预先批准的位置。不要 用真实 secret 做日志泄漏测试。
审计与隐私的验收
最终不是“多记”或“少记”,而是:
这是安全日志与普通调试日志的根本区别。
上一节:行级安全与连接池上下文 · 返回本章目录 · 下一节:Pigsty 安全基线 · 查看全书目录 · 查看索引中心