导语:PostgreSQL 逻辑复制漏洞 CVE-2026-120145 以 CVSS 8.8 的高危评级进入 GitHub Advisory Database,其核心问题在于复制目标授权校验的缺失——被攻陷的发布端只需把系统目录命名为复制目标,就能让超级用户拥有的订阅进程在操作系统层面执行任意代码。这不是一次普通的 SQL 注入式风险,而是复制协议信任边界被击穿后的权限跃迁,值得每一位负责数据库基础设施的架构师逐层拆解。
一、PostgreSQL 逻辑复制漏洞的事件全景与影响面
先看官方通告给出的关键事实:该漏洞编号 CVE-2026-120145,CVSS v3 基础评分 8.8,攻击向量为 Network、攻击复杂度 Low、所需权限 None、需要用户交互,机密性/完整性/可用性三项影响均为 High。受影响版本覆盖 PostgreSQL 18.7、17.12、16.16、15.20、14.25 之前的全部小版本,也就是说从 14 到 18 的五个主要分支同时中招,这在 PostgreSQL 的安全公告历史上属于影响面相当广的一次。
通告原文对攻击路径的描述非常精炼,但信息密度极高:Missing authorization in PostgreSQL logical replication allows a compromised publisher to execute arbitrary code as the operating system user running the superuser-owned subscription, via naming a system catalog as a replication target. 拆开来看,这里有三个关键限定条件,决定了风险的真实边界:
- 前提一:发布端已被攻陷。攻击者需要先控制 publisher 侧,才能构造恶意的复制目标名称。这意味着该漏洞更准确的定位是「横向移动与权限提升的跳板」,而非可直接从公网打穿的入口。
- 前提二:订阅由超级用户拥有。只有 superuser 拥有的 subscription 才处于脆弱状态。普通业务账号创建的订阅不受影响,这一条直接决定了修复优先级的分层策略。
- 前提三:复制目标可指向系统目录。授权校验缺失使得
pg_catalog下的系统表可以被指定为复制目标,从而绕过正常的对象级权限检查。
很多团队看到「需要发布端被攻陷」就降低优先级,这是危险的误判。在多租户或跨团队共享复制链路的环境中,发布端往往由业务方自行运维,其安全水位远低于核心数据库。一旦发布端失守,攻击者即可借助超级用户订阅进程完成从数据库层到操作系统层的权限跃迁,CVSS 给出 8.8 正是基于这一跃迁的严重性。
企业级架构规范
flowchart TD
subgraph 攻击面 ["攻击者控制域 (Compromised Publisher)"]
A["发布端被攻陷 (Publisher Compromised)"] --> B["构造恶意复制目标名称"]
B --> C["将系统目录 pg_catalog 指定为复制目标"]
end
subgraph 协议层 ["逻辑复制协议层 (Replication Protocol Tier)"]
C --> D["WAL 流传输 (Logical Decoding)"]
D --> E["订阅端应用进程 (Apply Worker)"]
end
subgraph 校验层 ["授权校验层 (Authorization Tier)"]
E --> F{"目标对象权限校验"}
F -->|"校验缺失 (漏洞点)"| G["系统目录写入被放行"]
end
subgraph 执行层 ["执行与提权层 (Execution Tier)"]
G --> H["以超级用户订阅进程身份执行"]
H --> I["操作系统层任意代码执行"]
end
subgraph 加固层 ["修复与加固层 (Mitigation Tier)"]
I -.-> J["升级至 18.7 / 17.12 / 16.16 / 15.20 / 14.25"]
J --> K["订阅权限收敛为非超级用户"]
end
二、根因拆解:授权校验为何在复制目标上失效
2.1 逻辑复制的信任模型与订阅端特权
要理解这个 PostgreSQL 逻辑复制漏洞,必须先回到逻辑复制的信任模型。逻辑复制由发布端(publisher)和订阅端(subscriber)构成,发布端通过逻辑解码输出 WAL 变更流,订阅端的 apply worker 进程负责将变更应用到本地表。关键在于:apply worker 以订阅拥有者的身份运行,当订阅由超级用户创建时,apply worker 就继承了超级用户权限,并且由于 PostgreSQL 的进程模型,它同时持有运行数据库服务的操作系统用户身份。
正常的复制流程中,目标表在创建订阅时通过 CREATE SUBSCRIPTION ... PUBLICATION ... 明确绑定,apply worker 只需按绑定关系写入即可。但复制协议在应用变更时,目标对象的解析依赖发布端传来的关系标识,而这一环节的授权校验存在缺口——当发布端把目标指向系统目录时,订阅端未能有效拒绝。
2.2 系统目录作为复制目标为何是致命攻击面
系统目录(system catalog)是 PostgreSQL 的元数据核心,pg_catalog 下的表承载了表结构、权限、函数定义等全部元信息。允许对系统目录执行写入,等价于把数据库的元数据控制权交给攻击者。更严重的是,PostgreSQL 中存在若干以超级用户身份执行的系统函数与过程语言入口,一旦攻击者能够操纵目录内容,就可以构造出在 apply worker 上下文中被调用的执行路径,最终落到操作系统命令执行。
该漏洞的危险性不在于单点写入,而在于「数据库元数据写入 → 特权函数调用 → 操作系统命令执行」这条完整链条。apply worker 进程同时持有数据库超级用户权限与操作系统服务账号身份,是典型的双重特权载体,一旦被利用,影响范围会直接突破数据库边界,波及宿主机上的其他服务与凭据。
2.3 与历史复制类漏洞的横向对照
PostgreSQL 历史上曾多次出现复制相关的权限问题,但多数集中在复制槽权限、pg_read_server_files 类文件读取或 COPY 路径上。本次 PostgreSQL 逻辑复制漏洞的特殊之处在于,它把攻击面从「读取」推进到了「写入系统目录」,并借助 apply worker 的特权身份完成执行。这种「授权缺失 + 特权进程 + 元数据写入」的三元组合,是评级被推到 8.8 的直接原因。
| 评估维度 | 传统复制类权限问题 | CVE-2026-120145 | 架构影响 |
|---|---|---|---|
| 攻击入口 | 复制槽 / 文件读取函数 | 复制目标名称解析 | 入口更隐蔽 |
| 权限要求 | 通常需具备特定角色 | 发布端失陷即可 | 前置门槛低 |
| 影响对象 | 数据读取 / 文件系统读取 | 系统目录写入 | 元数据可控 |
| 最终后果 | 信息泄露 | 操作系统任意代码执行 | 突破数据库边界 |
| CVSS 评分 | 通常 6.0 – 7.5 | 8.8 (High) | 高危 |
三、应急响应:版本升级与订阅权限收敛双轨并行
3.1 第一步:确认受影响版本与订阅归属
应急响应的第一动作不是盲目升级,而是精确盘点。先确认实例版本是否落在受影响区间,再排查是否存在超级用户拥有的订阅。以下 SQL 可直接用于订阅归属审计:
-- 1. 确认当前实例版本
SELECT version();
-- 2. 排查所有订阅及其拥有者,重点标记超级用户拥有的订阅
SELECT s.subname,
s.subowner::regrole AS owner_role,
r.rolsuper AS owner_is_superuser,
s.subenabled,
s.subconninfo
FROM pg_subscription s
JOIN pg_roles r ON r.oid = s.subowner
ORDER BY owner_is_superuser DESC, s.subname;
-- 3. 统计超级用户拥有的订阅数量(结果大于 0 即处于脆弱状态)
SELECT count(*) AS vulnerable_subscriptions
FROM pg_subscription s
JOIN pg_roles r ON r.oid = s.subowner
WHERE r.rolsuper = true;
审计结果应作为修复优先级的直接依据:存在超级用户订阅的实例列为 P0,需在维护窗口内完成升级;仅有普通角色订阅的实例可列为 P1,但仍建议随版本节奏升级,因为发布端失陷的前提在多团队环境中并不罕见。
3.2 第二步:按分支升级到修复版本
官方修复版本已明确:18.7、17.12、16.16、15.20、14.25。升级属于小版本内的安全修复,通常不涉及 catalog 版本变更,但仍建议遵循标准的备份与回滚流程。以下为基于包管理器的升级命令示例,需根据实际发行版调整:
# 以 Debian/Ubuntu + PostgreSQL 16 分支为例 # 升级前务必完成逻辑备份与基础备份 pg_dumpall -U postgres > /backup/pg_all_$(date +%F).sql # 更新软件源并升级到 16.16 apt-get update apt-get install -y postgresql-16=16.16-1.pgdg120+1 # 重启服务并校验版本 systemctl restart postgresql psql -U postgres -c "SELECT version();" # 校验逻辑复制链路是否正常恢复 psql -U postgres -c "SELECT subname, subenabled FROM pg_subscription;" psql -U postgres -c "SELECT * FROM pg_stat_subscription;"
点击展开:RPM 系与容器化环境的升级与回滚备用方案
# RHEL / Rocky / AlmaLinux 场景 dnf update -y postgresql16-server postgresql16-libs systemctl restart postgresql-16 # 容器化场景:更新镜像标签并滚动重建 # 注意:逻辑复制订阅端需保证 apply worker 正常重连 docker pull postgres:16.16 docker stop pg-subscriber && docker rm pg-subscriber docker run -d --name pg-subscriber -e POSTGRES_PASSWORD=*** -v pgdata:/var/lib/postgresql/data postgres:16.16 # 回滚策略:保留旧镜像标签与数据卷快照 # docker run -d --name pg-subscriber-rollback postgres:16.15
3.3 第三步:订阅权限收敛,消除超级用户依赖
升级解决的是代码层校验缺失,但架构层的根本问题是「订阅由超级用户拥有」这一反模式。即便完成升级,也应推动订阅权限收敛,把订阅拥有者从超级用户迁移到具备最小权限的专用角色。这既是纵深防御的要求,也能在下次出现类似 PostgreSQL 逻辑复制漏洞时显著降低爆炸半径。
-- 1. 创建专用复制角色(非超级用户) CREATE ROLE repl_owner LOGIN PASSWORD '***'; GRANT USAGE ON SCHEMA public TO repl_owner; GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO repl_owner; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO repl_owner; -- 2. 迁移订阅拥有者(PostgreSQL 支持 ALTER SUBSCRIPTION OWNER) ALTER SUBSCRIPTION my_sub OWNER TO repl_owner; -- 3. 复核:确认不再存在超级用户拥有的订阅 SELECT s.subname, r.rolname, r.rolsuper FROM pg_subscription s JOIN pg_roles r ON r.oid = s.subowner WHERE r.rolsuper = true;
迁移订阅拥有者前,必须确认目标角色对订阅涉及的全部目标表具备写权限,否则 apply worker 会在应用变更时报权限错误并中断复制。建议先在预发环境完整演练一次订阅切换,并观察 pg_stat_subscription 中的 last_msg_send_time 与 last_msg_receipt_time 是否持续更新。
四、纵深防御:复制链路的生产加固清单
单次 PostgreSQL 逻辑复制漏洞的修复是战术动作,复制链路的长期安全需要体系化加固。结合本次漏洞暴露出的信任边界问题,我建议从网络、身份、对象三个层面构建防线。
- 网络层:发布端与订阅端之间启用 TLS,并在
pg_hba.conf中限定仅允许订阅端 IP 连接复制槽,避免复制端口暴露在宽泛网段。 - 身份层:禁止使用超级用户创建订阅,为复制场景建立专用角色;对发布端账号实施独立的凭据轮换策略,降低发布端失陷后的横向影响。
- 对象层:定期审计订阅绑定的目标表清单,确保复制目标始终是业务表而非系统目录;对
pg_catalog的异常写入尝试配置审计日志告警。
配合上述配置,建议在监控侧对 pg_stat_subscription 的异常中断、apply worker 频繁重启、以及订阅目标表变更事件建立告警规则。复制链路的异常往往先于安全事件出现,监控是成本最低的早期信号来源。
五、架构师视角的复盘与长期启示
这次 PostgreSQL 逻辑复制漏洞给架构师群体的启示,远超一次补丁升级。它再次印证了一个反复出现的规律:特权进程 + 外部可控输入 + 元数据写入,是数据库系统中最危险的组合之一。逻辑复制的 apply worker 天然具备前两者,而系统目录作为复制目标的可能性,恰恰是设计时容易被忽略的第三极。
从工程实践看,我建议把「订阅拥有者是否为超级用户」纳入数据库基线检查项,与「是否开启 TLS」「是否限制 pg_hba」并列。同时,在跨团队复制链路中明确发布端的安全责任边界,把发布端的安全水位纳入整体风险评估——因为本次漏洞的触发前提,正是发布端可以被攻陷。
对于无法立即升级的存量实例,可采取的临时缓解措施包括:暂停非必要的超级用户订阅、收紧发布端网络访问、对订阅目标表清单实施变更审批。但这些都是缓解而非修复,最终仍须落到 18.7 / 17.12 / 16.16 / 15.20 / 14.25 的版本升级上。
最后提醒一点:安全公告中「需要用户交互」这一 CVSS 指标容易被误读为风险较低。在复制场景中,所谓用户交互往往只是订阅端正常应用变更流这一常规动作,攻击者无需诱导任何人工操作。评估风险时,应以实际攻击链路为准,而非机械套用评分指标。
本文核心技术背景源自 GitHub Advisory Database 官方安全通告:
GHSA-j3p3-grqw-h5vf · CVE-2026-120145 (GitHub Advisory Database)
官方安全页面:PostgreSQL Security CVE-2026-120145
