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(详见插件文档)。
Back to Blog

相关文章

阅读更多 »