注入如何发生
# ❌ 输入改变查询结构
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 在受害者浏览器中执行。两者都通过分离数据与代码来防御,而不是隐藏错误信息。
陷阱
引号转义在不同字符集和连接模式下会失效。存储过程或报表工具中的字符串拼接仍然可注入。只测正常路径的测试会漏掉漏洞;应包含引号、注释和布尔恒真等输入。