2. PostgreSQL安全架构与威胁建模
要治理 PostgreSQL 的安全,先要弄清它“长什么样”。PostgreSQL 不是单进程数据库,而是一套“主进程 + 后台辅助进程 + 每连接一进程”的多进程架构。理解这个进程模型,是理解认证、权限与攻击面的前提。
一、进程模型:安全边界从哪里开始
启动 PostgreSQL 时,最先运行的是 postmaster(主进程)。它负责监听端口、接受连接、派生后端进程,并管理共享内存与后台辅助进程。常见的后台进程包括:checkpointer(检查点)、background writer(后台写)、walwriter(WAL 写)、autovacuum launcher(自动清理调度)、logical replication launcher(逻辑复制调度)等。客户端每建立一个连接,postmaster 就 fork 出一个独立的 backend 进程,该进程以启动数据库服务的操作系统用户身份运行。
这里有两个关键安全含义:第一,所有后端进程共享同一个 OS 用户身份,因此数据库内的权限隔离必须靠 PostgreSQL 自身的权限体系完成,不能依赖操作系统用户隔离;第二,任何能连上 5432 端口并完成认证的客户端,都会获得一个后端进程,因此“谁能连、以谁连、连上能做什么”构成三层递进的安全控制。
二、认证流程:pg_hba.conf 是真正的守门人
客户端连接时,postmaster 先根据 listen_addresses 和 port 决定是否接受 TCP 连接,然后按 pg_hba.conf 中从上到下的顺序逐条匹配。匹配字段包括连接类型(local/host/hostssl)、数据库名、用户名、客户端地址和认证方法。第一条匹配的记录生效,后续不再检查。这意味着规则顺序本身就是安全策略的一部分。
一个典型的加固片段如下(示例配置,需按实际环境调整):
# TYPE DATABASE USER ADDRESS METHOD
local all postgres peer
host all app_user 10.0.1.0/24 scram-sha-256
host all all 0.0.0.0/0 reject
hostssl all all 0.0.0.0/0 scram-sha-256
这里 peer 依赖操作系统用户身份,适合本地管理;scram-sha-256 是当前推荐的密码认证方式,相比 md5 不再以可离线爆破的哈希形式存储口令;最后的 reject 是显式拒绝,避免“漏网”连接落到默认策略。修改 pg_hba.conf 后需执行 SELECT pg_reload_conf(); 或重启服务生效。
三、权限体系:从角色到行级安全
PostgreSQL 的权限模型以角色(role)为核心。角色可以登录(LOGIN)、可以继承(INHERIT)、可以是超级用户(SUPERUSER)。权限授予遵循“数据库 → schema → 对象”的层级:先要能连接数据库(CONNECT),再要能访问 schema(USAGE),最后才是表、列、序列、函数上的 SELECT/INSERT/UPDATE/DELETE/EXECUTE 等权限。
实践中应遵循最小权限原则:应用账号只授予必要的 DML 权限,不授予 DDL;管理账号与业务账号分离;对敏感表启用行级安全(RLS)。例如:
CREATE ROLE app_user LOGIN PASSWORD 'StrongPass!2024';
GRANT CONNECT ON DATABASE appdb TO app_user;
GRANT USAGE ON SCHEMA public TO app_user;
GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO app_user;
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY orders_tenant ON orders
USING (tenant_id = current_setting('app.tenant_id')::int);
RLS 让同一张表对不同租户呈现不同行集,是防止越权读取的有效手段。注意 SECURITY DEFINER 函数会以定义者权限执行,若未严格校验输入,可能成为提权通道,应谨慎使用并固定 search_path。
四、常见攻击面与 STRIDE 威胁建模实战
把进程模型、认证和权限串起来看,攻击面就清晰了:网络层(端口暴露、未加密传输)、认证层(弱口令、暴力破解、pg_hba 配置过宽)、授权层(过度授权、RLS 缺失、SECURITY DEFINER 滥用)、数据层(备份泄露、WAL 泄露)、扩展层(不受控的扩展与函数)。
用 STRIDE 对一条“应用通过 app_user 连接订单库”的数据流建模,可得到如下威胁清单(示例):
| STRIDE 类别 | 具体威胁 | 验证/缓解要点 |
|---|---|---|
| Spoofing 仿冒 | 攻击者用窃取的口令冒充 app_user | 启用 scram-sha-256,限制来源 IP,启用连接审计 |
| Tampering 篡改 | SQL 注入修改订单数据 | 参数化查询,最小权限,RLS 限制行范围 |
| Repudiation 抵赖 | 无法追溯谁改了数据 | 开启 pgaudit 或日志记录 DDL/DML,保留 WAL 归档 |
| Info Disclosure 泄露 | 备份文件或明文传输泄露 | 强制 hostssl,备份加密,限制文件系统权限 |
| DoS 拒绝服务 | 大量连接耗尽 backend 进程 | 设置 max_connections、连接池、statement_timeout |
| Elevation 提权 | 利用 SECURITY DEFINER 函数越权 | 固定 search_path,最小化 definer 函数,审计 EXECUTE 权限 |
把上述内容落到操作上,可以形成一份可执行的核查清单:确认 listen_addresses 仅绑定必要地址;确认 pg_hba.conf 无 trust 且末尾有 reject;确认所有远程连接使用 scram-sha-256 或证书;确认应用账号无 SUPERUSER、无 CREATEDB、无 CREATEROLE;确认敏感表已启用 RLS;确认日志能覆盖认证失败与权限拒绝事件。逐项核对后,PostgreSQL 的安全架构才真正从“知道”变成“做到”。