你的系统里还藏着什么?——从一块 90 年代 PostgreSQL 扩展的缓冲区溢出说起

2026-05-27 29 预计阅读时间:1 分钟
来源:postgr.es AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:9 分钟

2026 年,一个诞生于 1990 年代的 PostgreSQL 扩展被检出高危缓冲区溢出漏洞。这件事本身不算罕见——老代码有老毛病,修了就好。真正让人不安的是另一个事实:大多数团队根本说不清自己系统里到底装了哪些扩展、哪些依赖、哪些已经没人维护的陈旧组件。

漏洞不是最可怕的,看不见才是。

一块 90 年代的代码,为什么到 2026 年还在跑?

PostgreSQL 的扩展生态从 90 年代就开始生长。很多扩展最初由社区成员随手写就,解决某个具体需求,然后被打包进发行版、被运维脚本自动安装、被 Docker 镜像默默继承。原作者早已转去做别的事,维护权无人接手,代码躺在磁盘上,生产环境天天加载它——但没人记得它为什么在那里。

缓冲区溢出是 C 扩展的经典缺陷。输入长度未校验、栈上固定大小缓冲区被溢出写入,攻击者可以构造恶意输入劫持控制流。这类漏洞在 2026 年的 CVE 数据库里依然频繁出现,不是因为技术太难防,而是因为没人去看那些代码

你看不见的东西,正在替你做决定

Christophe Pettus 在演讲中点出了一个更广泛的症状:团队对自身软件栈的认知存在巨大盲区。这不仅仅是 PostgreSQL 扩展的问题——

  • 系统包管理器aptyum 安装的包列表,有多少人能完整列出来?
  • Python/Node 依赖树pip installnpm install 拉进来的间接依赖,你审查过几层?
  • Docker 镜像继承链:基础镜像里预装了什么,apt list --installed 的输出你看过吗?
  • Kubernetes 集群附加组件:Helm chart 默认启用的 sidecar、CRD、admission webhook,你逐个确认过吗?

每一层都有可能藏着一块 90 年代的代码。它不报错、不报警、不出现在监控面板上,但它在运行,它在处理输入,它在暴露攻击面。

先把家底摸清:盘点你的 PostgreSQL 扩展

解决"看不见"的问题,第一步是强制自己看见。下面是一套可以直接在 PostgreSQL 生产环境上运行的盘点脚本。

列出所有已安装扩展及其版本

-- 在每个数据库上执行,列出已安装扩展
SELECT extname AS extension_name,
       extversion AS version,
       n.nspname AS schema
  FROM pg_extension e
  JOIN pg_namespace n ON e.extnamespace = n.oid
 ORDER BY extname;

输出类似:

 extension_name | version | schema
----------------+---------+--------
 plpgsql        | 1.0     | pg_catalog
 pgcrypto       | 1.3     | public
 pg_stat_statements | 1.9 | public
 some_old_ext   | 0.4     | public

some_old_ext 版本 0.4——你记得它是什么吗?谁装的?还在用吗?

检查扩展的共享库文件来源

# 列出所有扩展对应的 .so 文件路径
for ext in $(psql -At -c "SELECT extname FROM pg_extension"); do
  control_file="/usr/share/postgresql/extension/${ext}.control"
  if [ -f "$control_file" ]; then
    module=$(grep -E '^module_pathname' "$control_file" | cut -d= -f2 | tr -d "'")
    echo "$ext -> $module"
  else
    echo "$ext -> [control file missing]"
  fi
done

这段脚本遍历每个扩展的 .control 文件,提取其指向的共享库路径。如果某个扩展指向一个你从未见见的 .so 文件,这就是一个需要追问的线索。

扫描扩展代码中的危险模式

如果你有扩展的源码(或者能从包里提取出来),可以用简单模式扫描高风险写法:

# 在扩展源码目录中扫描常见危险模式
scan_dir="/usr/share/postgresql/extension"

echo "=== 固定大小缓冲区 (strcpy / sprintf) ==="
grep -rn 'strcpy\|sprintf\|gets' "$scan_dir" --include='*.c' || echo "未发现"

echo "=== 未校验长度的 memcpy ==="
grep -rn 'memcpy' "$scan_dir" --include='*.c' | \
  grep -v 'sizeof' || echo "需人工复查"

echo "=== 硬编码路径或权限 ==="
grep -rn '/tmp/\|chmod 777\|0700' "$scan_dir" --include='*.c' || echo "未发现"

这不是替代专业安全审计,而是用最低成本把最显眼的风险标记出来。strcpysprintf 在 2026 年的 C 代码里出现,几乎等于在说"这段代码上次被认真审查是在 1998 年"。

超出 PostgreSQL:给整个依赖栈做一次 X 光

PostgreSQL 扩展只是冰山一角。下面是一套跨层的盘点命令,可以直接在服务器或 CI 环境中运行:

#!/bin/bash
# dependency-xray.sh — 依赖栈 X 光扫描
set -euo pipefail

echo "========== 系统包 =========="
apt list --installed 2>/dev/null | wc -l
echo "(总数)"

echo "========== Python 依赖树 =========="
if command -v pip &>/dev/null; then
  pip audit 2>/dev/null || echo "pip-audit 未安装,运行: pip install pip-audit"
fi

echo "========== Node 依赖 =========="
if [ -f package-lock.json ]; then
  npm audit --production 2>/dev/null || true
fi

echo "========== Docker 镜像层 =========="
if command -v docker &>/dev/null; then
  for img in $(docker images --format '{{.Repository}}:{{.Tag}}' | head -5); do
    echo "--- $img ---"
    docker run --rm "$img" apt list --installed 2>/dev/null | wc -l || true
  done
fi

echo "========== PostgreSQL 扩展 =========="
for db in $(psql -At -c "SELECT datname FROM pg_database WHERE datistemplate = false"); do
  echo "--- 数据库: $db ---"
  psql -d "$db" -At -c \
    "SELECT extname || ' ' || extversion FROM pg_extension ORDER BY extname"
done

运行前确保你有只读权限,不需要 root。把输出存下来,和三个月前的版本做 diff——新增的任何条目都应该有一个明确的"谁批准的、为什么需要"的答案。

每个看不见的组件,都要有一个退役计划

Pettus 的演讲最终指向的不是某个具体漏洞,而是运维纪律的缺失。以下是一份可以直接采纳的检查清单:

检查项 做法 频率
扩展清单 在每个数据库运行 pg_extension 查询,存档 每月
无人认领的扩展 对每个扩展追问:谁装的?哪个功能在用?没有就卸载 季度
依赖树深度 pip audit / npm audit / apt list,关注间接依赖 每次部署
基础镜像内容 docker run --rm <image> apt list --installed 镜像更新时
扩展源码审查 对自定义或低维护扩展做 strcpy/sprintf 模式扫描 引入时 + 年度
CVE 订阅 订阅 PostgreSQL 及关键扩展的安全公告 持续

关键原则:装上去的东西必须有退出路径。 如果一个扩展没有维护者、没有发布历史、没有安全响应渠道,它就不应该出现在生产环境里——哪怕它"一直没出过问题"。"一直没出过问题"只是"还没人去查过"的另一种说法。


1990 年代的代码在 2026 年爆出缓冲区溢出,这不是意外,这是必然。代码不会因为被遗忘而变得安全,只会因为被遗忘而变得危险。第一步不是修补漏洞,而是看见你到底装了什么。上面的 SQL 和脚本用五分钟就能跑完,但它们给你的信息,可能比你过去五年对自身依赖栈的认知还多。


相关推荐