search_path 劫持:你从未听说过的 PostgreSQL 攻击
发布: (2026年1月3日 GMT+8 03:49)
3 min read
原文: Dev.to
Source: Dev.to
大多数开发者都了解 SQL 注入,少有人知晓 search_path 劫持。
它同样危险。
什么是 search_path?
PostgreSQL 的 search_path 决定在引用未限定表名时查找哪个 schema。
-- 当 search_path = public 时,这两条语句等价:
SELECT * FROM users;
SELECT * FROM public.users;
攻击方式
如果攻击者能够控制 search_path,就可以把你的查询重定向到恶意表:
// ❌ 动态 search_path 来自用户输入
const schema = req.query.tenant; // 攻击者可以控制此值
await client.query(`SET search_path TO ${schema}`);
await client.query('SELECT * FROM users'); // 现在查询的是攻击者的 schema
工作原理
- 攻击者创建一个包含恶意
users表的 schema。 - 攻击者将
search_path设置为自己的 schema。 - 你的查询返回伪造的数据。
为什么这很重要
| 攻击类型 | 影响 |
|---|---|
| 数据窃取 | 返回伪造数据,捕获输入 |
| 权限提升 | 替换安全函数 |
| 代码执行 | 恶意触发器、函数 |
正确的写法
// ✅ 静态 search_path
await client.query(`SET search_path TO tenant_${tenantId}`);
// ✅ 对照白名单进行校验
const ALLOWED_SCHEMAS = ['tenant_1', 'tenant_2', 'tenant_3'];
if (!ALLOWED_SCHEMAS.includes(schema)) {
throw new Error('Invalid schema');
}
await client.query(`SET search_path TO ${schema}`);
// ✅ 使用全限定表名
await client.query('SELECT * FROM public.users'); // 明确指定 schema
用 ESLint 捕获此问题
npm install --save-dev eslint-plugin-pg
import pg from 'eslint-plugin-pg';
export default [pg.configs.recommended];
动态 search_path 会被检测到:
src/tenants.ts
8:15 error 🔒 CWE-426 | Dynamic search_path detected
Fix: Use static schema name or validate against allowlist
多租户模式
// ✅ 通过校验的安全多租户实现
async function queryTenant(tenantId, sql, params) {
// 校验租户是否存在
const tenant = await getTenant(tenantId);
if (!tenant) throw new Error('Unknown tenant');
const client = await pool.connect();
try {
// 从可信来源获取 schema 名称,而非用户输入
await client.query(`SET search_path TO tenant_${tenant.id}`);
return await client.query(sql, params);
} finally {
// 重置 search_path
await client.query('SET search_path TO public');
client.release();
}
}
快速安装
npm install --save-dev eslint-plugin-pg
import pg from 'eslint-plugin-pg';
export default [pg.configs.recommended];
别让攻击者劫持你的查询。