← 中文资源

简体中文版本

SQL 注入

SQL 注入发生在不可信输入改变查询结构时。参数化查询把代码与数据分离,是跨语言、跨数据库的首要防御。

阅读英文原文

注入如何发生

# ❌ 输入改变查询结构
query = "SELECT * FROM users WHERE email = '" + email + "'"
cursor.execute(query)

输入 ' OR '1'='1 会把条件变成恒真。数据库无法知道该值本应是数据。

参数化查询

# ✅ 驱动分别发送模板与值
cursor.execute("SELECT * FROM users WHERE email = ?", (email,))
// ✅ Node.js pg 示例
await pool.query('SELECT * FROM users WHERE email = $1', [email]);

多数数据库无法参数化表名和列名。把用户选择映射到固定的已知标识符白名单,而不是直接拼接。

纵深防御

  • 对所有值使用预处理语句,包括数字和日期。
  • 动态过滤使用 ORM 查询构造器,并审查原始查询逃生口。
  • 使用最小权限数据库账号,使成功注入也无法读取无关 schema。
  • 在边界校验输入形状,但绝不能把校验当作参数化的替代。

何时重要

每个包含外部输入的查询都是候选,包括后台搜索、导出和定时任务。与 XSS 相比,SQL 注入针对数据库,常影响其他用户的数据;XSS 在受害者浏览器中执行。两者都通过分离数据与代码来防御,而不是隐藏错误信息。

陷阱

引号转义在不同字符集和连接模式下会失效。存储过程或报表工具中的字符串拼接仍然可注入。只测正常路径的测试会漏掉漏洞;应包含引号、注释和布尔恒真等输入。