Web安全

SQL 注入原理与防御:从一行拼接说起

S
system 🌱LV1 新手
2026/6/20 发布 · 1 阅读

一、什么是 SQL 注入

SQL 注入(SQL Injection)是最经典、危害最广的 Web 漏洞之一。它的本质只有一句话:程序把用户输入当成了 SQL 代码,而不是数据。

当应用用字符串拼接的方式构造 SQL 语句时,如果没有对用户输入做严格处理,攻击者就能让自己输入的内容"越界"成为查询逻辑的一部分,从而读取、篡改甚至删除数据库中的数据。

二、漏洞是怎么产生的

设想一个最常见的登录查询,程序这样拼接:

SELECT * FROM users WHERE name = '输入的用户名' AND pass = '输入的密码'

正常情况下,用户名 alice、密码 123 拼出的语句是符合预期的。但如果用户名输入框里填的是一段精心构造的字符串,使得引号闭合后接上 OR 1=1 这类"永真条件",原本的过滤条件就被绕过了 —— 数据库会返回所有用户,登录校验形同虚设。

关键点在于:单引号让"数据"提前结束,后面的内容被当作了"代码"。 这就是注入的核心。

三、注入的几种典型类型

  • 联合查询注入(UNION):利用 UNION 把额外的查询结果拼接到原结果里,常用于直接读取数据。
  • 报错注入:故意构造让数据库报错,从错误信息里带出数据。适用于页面会回显数据库错误的场景。
  • 布尔盲注:页面不回显数据,但会因条件真假呈现不同状态(如"登录成功/失败"),逐位猜测数据。
  • 时间盲注:连真假状态都看不出来时,用 SLEEP 之类的延时函数,通过响应时间长短判断条件真假。

盲注效率虽低,但只要存在注入点,数据迟早能被一位一位"问"出来。

四、为什么过滤关键词不靠谱

很多开发者第一反应是"把 selectunion、单引号都过滤掉"。这是治标不治本的:

  1. 大小写、编码、注释可绕过:SeLeCt、URL 编码、内联注释 /**/ 都能躲过简单的关键词黑名单。
  2. 场景千变万化:数字型注入根本不需要引号;不同数据库语法各异。
  3. 黑名单永远不全:你能想到的过滤规则,攻击者总能找到漏网之鱼。

安全的思路从来不是"猜测并封堵坏输入",而是从根上让数据无法变成代码

五、正确的防御:参数化查询

防御 SQL 注入的根本方案是参数化查询(Prepared Statement / 预编译)

它的原理是:SQL 语句的结构先发给数据库编译好,用户输入只作为参数单独传入,永远被当作纯数据处理,绝不会改变语句结构。

-- 结构先定好,? 是占位符
SELECT * FROM users WHERE name = ? AND pass = ?
-- 再把用户输入作为参数绑定进去

无论用户输入什么内容(哪怕是一堆单引号和 OR 1=1),它都只是 name 字段要匹配的字符串,不可能改变查询逻辑。这是机制层面的免疫,而不是"过滤层面"的封堵。

六、纵深防御:多做一层总没错

参数化查询是地基,但生产环境建议叠加多层防御:

  • 最小权限原则:应用连接数据库的账号只给必要权限,即使被注入,也读不到不该读的库表。
  • 输入校验:对格式明确的字段(邮箱、手机号、数字 ID)做白名单校验。
  • ORM 框架:成熟 ORM 默认使用参数化,大幅降低手写拼接出错的概率。
  • WAF:作为补充防线拦截常见 payload,但不能替代代码层修复。
  • 错误信息脱敏:不要把数据库原始报错暴露给用户。

七、小结

SQL 注入的根因是"数据与代码混淆",防御的根本是"用参数化查询把二者彻底分开"。记住三句话:

  1. 永远不要用字符串拼接构造 SQL。
  2. 永远使用参数化查询 / 预编译。
  3. 最小权限 + 输入校验 + 错误脱敏做纵深防御。

做到这三点,这个困扰了 Web 世界二十多年的漏洞,在你的应用里就基本绝迹了。


本文为 KHack 社区原创教学内容,仅供安全研究与学习。

评论

还没有评论,来说两句。