node-postgres 中的 SQL 注入:大家都误解的模式
发布: (2025年12月31日 GMT+8 13:50)
3 min read
原文: Dev.to
Source: Dev.to
易受攻击的示例
// ❌ 看起来还不错,对吧?
async function getUser(userId) {
const query = `SELECT * FROM users WHERE id = '${userId}'`;
const result = await pool.query(query);
return result.rows[0];
}
攻击演示
// 攻击者输入:
const userId = "'; DROP TABLE users; --";
// 生成的查询语句:
// SELECT * FROM users WHERE id = ''; DROP TABLE users; --'
运行上述查询会删除整个 users 表,导致数据丢失并可能造成重大业务影响。
为什么它很危险
- 输入验证可以被绕过 – 依赖客户端检查或简单的正则表达式并不足够。
- 内部 API 仍然暴露 – 被攻击的服务可以调用内部端点。
- 模板字面量只是字符串拼接 – 它们不会自动对 SQL 进行转义。
- 使用原始查询时 ORM 并不能保护你 – 安全性仅在让 ORM 处理参数绑定时才有效。
安全的参数化查询(唯一可靠的模式)
// ✅ 参数化查询
async function getUser(userId) {
const query = 'SELECT * FROM users WHERE id = $1';
const result = await pool.query(query, [userId]);
return result.rows[0];
}
$1 占位符告诉 PostgreSQL 将提供的值视为数据,而不是可执行代码。任何恶意输入都无法突破参数的限制。
使用 eslint-plugin-pg 进行 lint 检查
安装插件
npm install --save-dev eslint-plugin-pg
ESLint 配置
// eslint.config.js
import pg from 'eslint-plugin-pg';
export default [pg.configs.recommended];
示例 lint 错误
src/users.ts
4:17 error 🔒 CWE-89 OWASP:A03 CVSS:9.8 | 检测到不安全的查询
修复: 使用参数化查询: client.query('SELECT * FROM users WHERE id = $1', [userId])
安全处理动态表名
// ❌ 不安全 – 由用户控制的表名
const table = userInput;
pool.query(`SELECT * FROM ${table}`);
// ✅ 安全 – 白名单限定的表名
const ALLOWED_TABLES = ['users', 'orders', 'products'];
if (!ALLOWED_TABLES.includes(table)) throw new Error('Invalid table');
pool.query(`SELECT * FROM ${table}`);
使用可选过滤条件构建查询
易受攻击的做法
let query = 'SELECT * FROM users WHERE 1=1';
if (name) query += ` AND name = '${name}'`; // 注入!
await pool.query(query);
使用参数的安全做法
const params = [];
let query = 'SELECT * FROM users WHERE 1=1';
if (name) {
params.push(name);
query += ` AND name = $${params.length}`;
}
await pool.query(query, params);
资源
- npm 包: eslint-plugin-pg – 包含 13 条规则,覆盖 PostgreSQL 安全、连接管理和查询优化。
- 规则文档:
no-unsafe-query(详见插件文档)。