2. PostgreSQL安全架构与威胁建模

要治理 PostgreSQL 的安全,先要弄清它“长什么样”。PostgreSQL 不是单进程数据库,而是一套“主进程 + 后台辅助进程 + 每连接一进程”的多进程架构。理解这个进程模型,是理解认证、权限与攻击面的前提。

一、进程模型:安全边界从哪里开始

启动 PostgreSQL 时,最先运行的是 postmaster(主进程)。它负责监听端口、接受连接、派生后端进程,并管理共享内存与后台辅助进程。常见的后台进程包括:checkpointer(检查点)、background writer(后台写)、walwriter(WAL 写)、autovacuum launcher(自动清理调度)、logical replication launcher(逻辑复制调度)等。客户端每建立一个连接,postmaster 就 fork 出一个独立的 backend 进程,该进程以启动数据库服务的操作系统用户身份运行。

这里有两个关键安全含义:第一,所有后端进程共享同一个 OS 用户身份,因此数据库内的权限隔离必须靠 PostgreSQL 自身的权限体系完成,不能依赖操作系统用户隔离;第二,任何能连上 5432 端口并完成认证的客户端,都会获得一个后端进程,因此“谁能连、以谁连、连上能做什么”构成三层递进的安全控制。

PostgreSQL 进程模型与安全控制层次 客户端 psql / 应用 / 驱动 携带 host+port+user+db ① 网络与监听 listen_addresses pg_hba.conf 匹配 ② 认证 scram-sha-256 / md5 peer / cert / ldap postmaster 主进程 监听 / fork / 共享内存管理 ③ 权限体系(backend 内) 角色 → 数据库 → schema → 表/列 → 行级安全 GRANT / REVOKE / RLS / SECURITY DEFINER 后台辅助进程 checkpointer / bgwriter walwriter / autovacuum 数据文件与 WAL base/ / pg_wal/ OS 用户 postgres 可读写 常见攻击面:弱口令/暴力破解 · 过度授权 · SQL 注入 · 未加密传输 · 备份泄露 · 扩展与函数滥用 对应 STRIDE:Spoofing / Tampering / Repudiation / Info Disclosure / DoS / Elevation

二、认证流程: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 权限
威胁建模不是一次性动作。每次新增应用账号、开放端口、引入扩展或调整 pg_hba.conf,都应重新走一遍 STRIDE 检查,确保安全边界与业务变化同步。

把上述内容落到操作上,可以形成一份可执行的核查清单:确认 listen_addresses 仅绑定必要地址;确认 pg_hba.conf 无 trust 且末尾有 reject;确认所有远程连接使用 scram-sha-256 或证书;确认应用账号无 SUPERUSER、无 CREATEDB、无 CREATEROLE;确认敏感表已启用 RLS;确认日志能覆盖认证失败与权限拒绝事件。逐项核对后,PostgreSQL 的安全架构才真正从“知道”变成“做到”。