23 固若金汤:认证、授权与数据安全
数据库安全不是在系统外面再围一堵墙,而是让每一次越过边界都留下可验证的 答案:
只检查其中一层会产生危险的“半安全”:
本章先建威胁模型,再沿认证、授权、行级安全、密钥和审计一路向内。最后把 第 22 章的 transaction pool 纳入模型:同一个 PostgreSQL backend 会先后 服务不同客户端,因此 session 级安全上下文不仅会“丢失”,还可能泄漏给下一 个租户。
本章目标
完成本章后,你应当能够:
- 用资产、主体、入口、信任跨越和失败后果编写数据库威胁模型;
- 区分终端用户、应用 login、effective role、object owner 与 break-glass;
- 从
pg_hba_file_rules解释 first-match,而不是凭配置片段猜认证结果; - 区分认证方法、密码存储、传输加密和服务器身份校验;
- 使用 SCRAM,理解 channel binding、密码轮换与客户端兼容边界;
- 说明
sslmode=require、verify-ca与verify-full分别证明什么; - 用
pg_stat_ssl、证书 SAN、HBA 和真实连接共同验收 TLS; - 把 LOGIN、group、owner、migrate、runtime、readonly 角色拆开;
- 正确使用 PostgreSQL 16+ membership 的
ADMIN、INHERIT与SET; - 设计 schema/table/sequence/function/default privilege 的最小权限;
- 识别
PUBLIC、owner、search_path、SECURITY DEFINER和预定义高权 角色造成的越权路径; - 为共享表设计 RLS 的
USING与WITH CHECK; - 解释 default-deny、permissive
OR、restrictiveAND与完整表操作边界; - 使用
FORCE ROW LEVEL SECURITY约束 owner,并明确 superuser/BYPASSRLS仍会绕过; - 通过事务级
SET LOCAL ROLE和 tenant context 支持 transaction pool; - 复现 session
SET跨客户端泄漏,并证明事务局部状态在提交后消失; - 设计生成、分发、双版本轮换、撤销和应急回收的凭据生命周期;
- 区分普通运行日志、对象/会话审计、平台审计和合规证据;
- 在 Pigsty 中声明用户、HBA 和接入层,再从渲染产物与运行事实反查;
- 对一个环境给出“通过、带例外通过、待整改或拒绝”的诚实安全结论。
前置与后续
前置:
- 第 6 章 开发规约 已处理参数绑定、受控
search_path、SECURITY DEFINER和 secret-free 交付; - 第 10 章 并发控制 已建立事务与 session 边界;
- 第 19 章 部署基线 已固定 Pigsty v4.5.0 四机 nonproduction sandbox;
- 第 20 章 高可用 已区分 data plane、control plane 与 break-glass;
- 第 21 章 备份体系与恢复演练 已说明备份、WAL、日志同样是敏感 数据载体;
- 第 22 章 服务接入 已证明 transaction pooling 会复用 backend 并保留某些 session 状态。
后续:
- 第 24 章把安全例外、owner、轮换、SOP 和审批纳入治理;
- 第 25 章把连接、认证失败、角色变更与审计事件接入可观测系统;
- 第 29、30 章处理复制/迁移/升级中的身份与双版本兼容;
- 第 31 章把泄露、越权、凭据失陷与取证放进事件响应;
- 第 32–35 章会再次约束备份、恢复、故障操作和抢救身份。
学习路径
顺序很重要。若先写一条 RLS policy、最后才问“tenant id 从哪里来”,就可能 把用户自己提交的 tenant id 原样写入 GUC,得到一套语法正确却可随意越权的 系统。
六层安全证明
| 层 | 要证明的问题 | PostgreSQL / Pigsty 证据 |
|---|---|---|
| 暴露面 | 哪些端口和网络能到达 | listener、防火墙/安全组、HAProxy、HBA |
| 传输 | 对端是谁、链路是否加密 | sslmode、CA/SAN、pg_stat_ssl、PgBouncer TLS |
| 认证 | login 是谁、凭据是否有效 | HBA first-match、SCRAM/cert/外部身份、认证日志 |
| 授权 | current role 能做什么 | role graph、ACL、owner、default privilege |
| 数据 | 哪些行可见、哪些新值可写 | RLS flag、policy、正负测试、FORCE RLS |
| 治理 | 谁能变更、撤销、调查 | inventory、审批、secret manager、audit/retention |
这六层不能互相代替。例如 PostgreSQL hostssl 只要求连接使用 TLS;客户端若
选择不校验证书名称,仍可能把密码发给错误的服务器。反过来,verify-full
只能验证连接到证书所代表的服务器,不能证明这个 login 应当读取某个租户。
角色分层
本章采用一个可复用的角色图:
职责:
| 角色 | LOGIN | 主要权限 | 明确不应拥有 |
|---|---|---|---|
| application login | 是 | 只允许切换到批准的 runtime role | owner、DDL、ADMIN OPTION |
| runtime | 否 | USAGE + 必要 DML | schema CREATE、TRUNCATE、BYPASSRLS |
| readonly | 否 | USAGE + SELECT | 写入、迁移 |
| migrate | 否 | 可切换到 owner | 日常服务流量、凭据 |
| owner | 否 | 拥有应用对象和 policy | 日常 LOGIN |
| break-glass | 独立 | 紧急高权动作 | 无审批、无时限、无审计 |
PostgreSQL 16 起,membership 自身有 ADMIN、INHERIT 与 SET 选项。
本章使用:
因此 test 登录后不会隐式得到 runtime 权限,但能在批准的事务中
SET LOCAL ROLE;它也不能把 runtime 身份再授予别人。
租户事务合同
共享表 RLS 的最小请求序列:
第三个参数 true 表示 transaction-local。提交或回滚之后,角色和 tenant
context 都不应继续生效。
表同时使用:
policy 分开描述读写:
USING 回答“旧行能否进入操作”;WITH CHECK 回答“新行版本能否存在”。
只写其中一边,常会允许把一行从本租户改到另一个租户,或者插入不可见数据。
正式实验
角色与对象:
RLS 观测:
连接池反例临时把入口 PgBouncer 从:
改为:
客户端 A 用 session 级 set_config(..., false) 设置 tenant A;关闭后,客户端 B
在同一个 backend、没有设置 tenant 的情况下仍读到 tenant A 的两行。这是
有意注入的失败,不是支持方式。
刷新 pool 后,四个事务依次在同一个 backend 上得到:
这证明隔离来自事务边界,而不是恰好换了 backend。随后四个 pool 参数精确
复位,并对三个节点执行 RECONNECT test 清理注入的 session 状态。
TLS 与认证观测:
凭据轮换使用一个不进入 PgBouncer userlist 的 direct-only synthetic role:
密码值、SCRAM verifier、raw userlist 和 private key 都没有进入证据。
生产结论
本章 formal sandbox 结论是:
这不是“Pigsty 不安全”的概括,而是对这一份 dev/test inventory 和运行状态的 精确判断。Pigsty 默认面向可信内网的开发、测试和演示;生产必须依据自己的 威胁模型收紧密码、网络、HBA、证书、审计与 secret 管理。
本章例外
在前四章下卷例外之外,本章保留:
本章目录
23.1 威胁模型与信任边界
23.2 认证与连接准入
23.3 角色与最小权限
- 23.3.1 login、group、owner 与 runtime role
- 23.3.2 schema、table、sequence、function 权限
- 23.3.3 默认权限、所有权迁移与越权路径
23.4 行级安全与连接池上下文
- 23.4.1 RLS policy、owner bypass 与强制 RLS
- 23.4.2 租户身份通过事务参数传递
- 23.4.3 transaction pooling 下使用
SET LOCAL - 23.4.4 验证复用连接不会泄漏上一个租户状态
23.5 密钥、审计与敏感信息
23.6 Pigsty 安全基线
23.7 实战:隔离两个租户
- 23.7.1 建立应用角色、迁移角色和只读角色
- 23.7.2 通过 PgBouncer 事务池验证 RLS
- 23.7.3 注入会话状态泄漏与越权访问并修复
- 23.7.4 输出权限矩阵、轮换证据与应急回收步骤
实验入口
lab-contract.md:权限、变更、证据和 reset 边界;requirements.json:机器验收合同;threat-model.json:资产、主体和信任跨越;role-contract.json:五类 synthetic role;tenant-contract.json:事务级租户上下文;security-adr.md:RLS 与连接池决策;topology.mmd:端点、角色和数据边界;task.sh:唯一安全入口;security-run.json:正式参考结果;negative-cases.json:二十个反例。
task.sh all 只重验既有证据,不登录应用身份、不改 role/password/pool/HBA/
certificate,也不删除 fixture。drill:security 和 reset:fixture 是两条
完全分离、精确守卫的路径。
参考资料
- PostgreSQL 18:Client Authentication
- PostgreSQL 18:pg_hba.conf
- PostgreSQL 18:Password Authentication
- PostgreSQL 18:libpq SSL Support
- PostgreSQL 18:Database Roles
- PostgreSQL 18:Row Security Policies
- PostgreSQL 18:Error Reporting and Logging
- PgBouncer:Features
- PgBouncer:Configuration
- Pigsty:Security Considerations
- Pigsty:HBA Rules
上一章:四通八达:服务接入、连接池与路由 · 返回下卷导读 · 下一章:纲举目张:SLO、SOP 与组织治理 · 查看全书目录 · 查看索引中心