web学习笔记编程技术学习与分享

SQL 注入的演变与参数化查询

2026 年 08 月 08 日约 3 分钟阅读 网络与安全

SQL 注入有二十年历史,防御方案早已成熟,但新代码仍在不断引入老漏洞——因为拼接的诱惑始终存在。

漏洞的最小形态

// 危险:用户输入直接进入 SQL 文本
$sql = "SELECT * FROM users WHERE name = '" . $_GET['name'] . "'";

输入 ' OR '1'='1 改变了语句结构,'; DROP TABLE users; -- 更是能执行任意语句。本质是数据与代码的边界消失:数据库无法知道哪部分是程序员的意图、哪部分是用户数据。

演变出的对抗

防御方引入过滤(关键字黑名单)→ 攻击方用注释绕过(SEL/**/ECT)、编码变换、大小写。黑名单注定失败。参数化查询(预处理)从协议层建立边界,才是终结方案。

具体原理见上一篇《PDO 预处理到底防住了什么》:参数走独立通道,永远不会被解析为 SQL 语法。

现代代码里它还长什么样

ORM 让拼接变得隐蔽:

# 危险:f-string 进 ORM 的 raw 查询
User.objects.raw(f"SELECT * FROM users WHERE name = '{name}'")

# 安全:参数化
User.objects.raw("SELECT * FROM users WHERE name = %s", [name])

动态排序、动态表名、报表系统的动态条件拼接,是注入在新代码里的主要藏身处。评审规则:值用参数化,标识符用白名单,没有例外。

已有系统怎么鉴定

  1. 全局搜索字符串拼接 SQL 的模式(+ $ . f" 紧邻 SELECT/INSERT);
  2. 用 sqlmap 对外部接口做一次只读探测(注意授权与影响,只在测试环境);
  3. 开启数据库审计,观察异常语句特征(恒真条件、注释符、UNION)。

纵深防御

参数化是第一道。之后还应该:

  • 数据库账号最小权限:业务账号不给 DROP/FILE 权限;
  • 不同应用独立账号,出事可定位可止损;
  • 错误信息不回显 SQL——报错页泄露表结构是注入的加速器;
  • WAF 作为外层补充,但不能依赖:编码绕过与 WAF 的攻防永无止境,参数化才是釜底抽薪。

一句话总结:注入防御没有新故事,故事只有一句——不要把不可信数据拼进 SQL 文本。

SQL 注入安全数据库