文章目录
-
- 一、先共用一张脑图
- 二、宽字节注入:当"反斜杠"被吃掉
-
- [1. 它在解决攻击者的什么问题](#1. 它在解决攻击者的什么问题)
- [2. 为什么说它是"年代感"很强的洞](#2. 为什么说它是“年代感”很强的洞)
- [3. 授权测试时怎么意识到"可能是宽字节"](#3. 授权测试时怎么意识到“可能是宽字节”)
- [4. 怎么修(比背 payload 重要)](#4. 怎么修(比背 payload 重要))
- [5. 一句收束](#5. 一句收束)
- 三、二次注入:安全地走进去,危险地走出来
-
- [1. 这是高级题里最"业务"的一种](#1. 这是高级题里最“业务”的一种)
- [2. 常见真实长相](#2. 常见真实长相)
- [3. 为什么自动化容易漏](#3. 为什么自动化容易漏)
- [4. 授权测试怎么挖](#4. 授权测试怎么挖)
- [5. 怎么修](#5. 怎么修)
- [6. 一句话](#6. 一句话)
- [四、堆叠注入:一句请求,多句 SQL](#四、堆叠注入:一句请求,多句 SQL)
-
- [1. 它到底是什么](#1. 它到底是什么)
- [2. 为什么有的库"怎么都堆叠不了"](#2. 为什么有的库“怎么都堆叠不了”)
- [3. 授权测试里怎么证明"支持堆叠"](#3. 授权测试里怎么证明“支持堆叠”)
- [4. 危害为什么常被高估或低估](#4. 危害为什么常被高估或低估)
- [5. 和存储过程、批处理的关系](#5. 和存储过程、批处理的关系)
- 五、三专题对照:怎么记才不容易混
- 六、实战中的组合拳(认识即可)
- 七、防御工程:把专题落进日常
-
- [1. 代码审计加三枚探针](#1. 代码审计加三枚探针)
- [2. 测试用例别只砸"当前参数"](#2. 测试用例别只砸“当前参数”)
- [3. 配置基线](#3. 配置基线)
- [4. 监控](#4. 监控)
- 八、收尾
上一篇把 SQL 注入从手工到 sqlmap 走了一遍。有人读完会觉得:参数化一上,注入不就死了吗?话是没错,可真实项目里总有"历史包袱"和"聪明人自己造的轮子"------编码处理不一致、入库一次出库再拼一次、驱动居然允许多语句执行。于是就有了这三类更刁钻、也更容易在代码评审里漏掉的东西:宽字节注入、二次注入、堆叠注入。
一、先共用一张脑图
普通注入:用户输入 → 拼进 SQL → 当场爆炸。
高级一点的,缝隙往往在别处:
text
宽字节: "转义"和"数据库编码"不是同一套尺子,逃逸字符被吃掉
二次注入: 第一次像安全地存进去了,第二次再拼 SQL 时才爆炸
堆叠注入: 一条请求里塞多句 SQL,数据库驱动居然肯挨个执行
三者可以叠在一起出现,但学习时最好分开想清楚。
二、宽字节注入:当"反斜杠"被吃掉
1. 它在解决攻击者的什么问题
很多老 PHP 代码喜欢用 addslashes()、魔法引号、或自己在单引号前加 \,以为这样字符型注入就废了。想法很朴素:' 变成 \',字符串闭合不了。
宽字节注入盯的是另一处:若连接数据库时用了某些多字节编码 (经典讨论最多的是 GBK ),而转义又按"单字节思维"加反斜杠,攻击者可以构造一种两字节组合------前一个字节把后面的 \"吃进"一个合法汉字(或其它多字节字符)里,于是 ' 重新露出原形,重新具备闭合字符串的能力。
人话版:
你以为你给引号穿了防弹衣(前面加了
\),编码一搅合,防弹衣和别人拼成了别的字,引号又光着了。
2. 为什么说它是"年代感"很强的洞
它高度依赖:
- 应用层按字节做转义;
- 数据库链路仍是 GBK / GB2312 一类;
- 字符集在连接、库、表、客户端之间还不统一。
今天大量系统默认 UTF-8 / utf8mb4,宽字节这条路会窄很多。但别得意:内网老 OA、老电商、政府项目、十年不升级的 PHP,依旧能撞见。供应链里一个老组件也能把你拖回 GBK 时代。
3. 授权测试时怎么意识到"可能是宽字节"
线索通常很"运维":
- 响应头或页面暗示国产老栈、GBK;
- 数据库/连接配置里能看到
GBK、GB2312; - 普通
'被转义看起来没戏,但团队怀疑转义只做在应用层; - 白盒直接搜:
addslashes、自制 escape、却未见真正参数化。
黑盒下乱撞宽字节 payload,既吵又容易误判。更稳的是:先确认编码链路,再在测试环境验证。能白盒就白盒。
4. 怎么修(比背 payload 重要)
按优先级:
- 彻底参数化------转义游戏直接退场;
- 全链路统一 UTF-8 / utf8mb4,连接串显式指定;
- 不要用
addslashes当防注入方案; - 对遗留库迁移编码时做好数据评估,别只改半截。
看到有人说"我们加了宽字节 WAF 规则",我会问:编码统一了吗?预编译了吗?没有的话,规则只是多一层运气。
5. 一句收束
宽字节注入教你的不是更花的攻击技巧,而是:
任何"在字符串层面手工逃逸"的方案,都要和字符编码绑在一起审查;编码一错,逃逸就可能失效。
三、二次注入:安全地走进去,危险地走出来
1. 这是高级题里最"业务"的一种
二次注入(Second-Order Injection)的剧本常常是:
- 攻击者提交一段看起来无害、甚至通过了当下校验与转义的输入;
- 应用把它存进数据库;
- 之后另一次查询------可能是别的接口、定时任务、管理端功能、触发器------把库里的值取出来,又拼进 SQL;
- 这时候才发生注入。
第一次请求的日志可能完全干净;安全测试如果只砸"当前参数",也会漏。
我特别想强调:二次注入不是因为攻击者更聪明,而是因为开发把"存进去"和"再拿出来用"当成了两个世界。存的时候当脏数据防;拿的时候当可信数据用。数据库不是信任根。
2. 常见真实长相
- 用户改昵称 / 地址 / 备注,管理端按昵称搜索,拼了 SQL;
- 注册用户名写进 profile,某处
WHERE user='...'又拼了一次; - 工单系统、CMS 标签、导入的 Excel 字段;
- 安全设备或审计系统把"原始攻击串"存下,再在报表里二次查询(是的,安全产品也干过这种事)。
有时候第一次输入还有长度限制、过滤关键字;但存进去的是"半成品",二次拼接时过滤策略并不一致,照样爆。
3. 为什么自动化容易漏
sqlmap 默认强在"当前请求的参数 → 当前响应"。二次注入需要:
- 找到写入点 和触发点;
- 两者可能隔着角色、时间、甚至异步队列;
- 触发点不一定回显给你这个会话。
所以它更吃业务理解。工具可以辅助,替代不了脑。
4. 授权测试怎么挖
我自己的习惯步骤:
(1)画数据生命周期
这个字段从哪里进、存在哪张表、哪些功能会读、读完干什么。像跟账一样跟字段。
(2)特意埋探针
在测试环境用可控账号,把昵称/备注改成带特殊字符的"探针串"(注意别破坏生产数据)。然后去所有会用到该字段的管理功能、搜索、导出点一点。
(3)看第二次查询是否参数化
白盒最爽:搜对该字段的二次拼接。黑盒就看触发时是否出现 SQL 异常、时间差、或业务结果异常。
(4)别只测"当前用户能看的页面"
二次触发经常在管理员视角、批处理、消息消费者里。测试账号体系要够。
5. 怎么修
说起来简单,做起来要改习惯:
- 任何时候拼 SQL 都不信任"来自数据库的字符串"------它昨天可能来自用户;
- 二次查询同样参数化;
- 对业务上必须进 SQL 结构的字段(极少见)做白名单;
- 写入侧过滤不能替代读出侧安全;
- 代码审计 checklist 里加一条:"取出再拼 SQL 的点"。
有团队觉得"入库前已经 escape 了"。请回想宽字节:escape 本身就不牢;更何况二次使用时 escape 可能被解掉、或根本没带上。
6. 一句话
二次注入是信任边界画错了:把"库里的"当成"干净的"。
四、堆叠注入:一句请求,多句 SQL
1. 它到底是什么
堆叠(Stacked Queries)指在一次数据库执行里塞多条语句,例如概念上的:SELECT ...; UPDATE ...; --
若驱动/接口允许一次执行多语句,攻击者在注入点不仅能读,还可能改数据、调存储过程,危害面明显变宽。
注意:这和"UNION 注入"不是一路------UNION 是把查询拼进同一条 SELECT 的结果集;堆叠是多条语句依次执行。
2. 为什么有的库"怎么都堆叠不了"
能不能堆叠,很大程度取决于:
- 数据库类型与连接方式;
- 是否显式打开多语句选项(历史上某些 PHP 连接方式对此很敏感);
- 中间件、ORM、预编译接口是否根本不支持一次丢多句。
所以你会看到教材里写"MySQL 也可以堆叠",真打目标却纹丝不动------不一定是你菜,可能是链路不允许。老手先试探是否支持多语句,再谈利用。
3. 授权测试里怎么证明"支持堆叠"
在授权环境,目标通常是证明风险,而不是把库搅烂。优先用:
- 相对无害的时间差异、会话变量、或只读类确认;
- 严格避免随手
DROP、大规模UPDATE; - 与客户事先约定可执行边界。
若手工不好证明,sqlmap 等工具在授权下也有对应探测技术选项------仍要控速、控范围,避免生产抖动。
4. 危害为什么常被高估或低估
高估: 以为有注入就一定能堆叠。不能。
低估: 一旦能堆叠,而账号权限又大,危害从"拖库"跃迁到"改账、插后门用户、清数据"。
修复时两刀一起砍:
- 堵住注入(参数化);
- 连接配置关闭多语句(若业务不需要);
- 数据库账号最小权限。
即使将来又写出拼接,也少一道"多语句"的翅膀。
5. 和存储过程、批处理的关系
业务合法地使用多语句 / 批处理时,要分清入口:管理任务用单独高权限连接,面向用户的 Web 连接绝不打开多语句。别为了图省事共用一个 DSN。
五、三专题对照:怎么记才不容易混
| 宽字节 | 二次注入 | 堆叠注入 | |
|---|---|---|---|
| 核心缝隙 | 编码与转义不一致 | 信任了库中数据再拼接 | 驱动允许多语句 |
| 何时爆 | 当前请求拼 SQL 时 | 往往在后续请求/任务 | 当前执行点支持多句 |
| 现代系统频率 | 相对少(UTF-8 普及) | 仍常见 | 看配置与驱动 |
| 测试重点 | 编码链路、伪转义 | 字段生命周期 | 是否允许多语句 |
| 主修复 | 参数化 + 统一编码 | 全程参数化、不信任库字段 | 参数化 + 关闭多语句 + 权限 |
六、实战中的组合拳(认识即可)
真实漏洞偶尔会"套娃":宽字节让转义失效 → 当前请求注入成功 → 若可堆叠则危害升级;或第一次用宽字节写入恶意昵称 → 二次在管理端爆发。
分析时按层剥:
- 输入怎么进;
- 有没有伪安全转义;
- 编码是什么;
- 最终哪一次
query拼了它; - 那次执行允许多句吗;
- 账号能干什么。
能画完这六步,报告会非常漂亮,开发也没法用"我们有 addslashes"糊弄你。
七、防御工程:把专题落进日常
1. 代码审计加三枚探针
在 Code Review / 半自动审计里专门搜:
- 手工 escape /
addslashes/ 自制safe_sql; - 取出数据库字段后的字符串拼接;
- 连接配置里的多语句开关、过时驱动用法。
2. 测试用例别只砸"当前参数"
加两类用例:
- 编码相关(遗留 GBK 系统);
- 业务字段写入后再在管理端/批处理触发。
3. 配置基线
- 连接字符集显式 utf8mb4;
- 应用账号禁止多余权限;
- 非必要关闭多语句;
- 生产禁止 SQL 明细回显。
4. 监控
短时间大量语法错误、异常多语句模式、管理端搜索功能报错飙升------都可能是二次注入被触发的侧写。和 WAF 日志对着看。
八、收尾
宽字节注入提醒你:逃逸不是安全边界,编码才可能让边界破产。
二次注入提醒你:数据库里的未必干净,第二次拼接一样要命。
堆叠注入提醒你:语句执行器的能力边界,也是安全边界;能关的多语句就别开着。
高级 SQLi 专题写到这里,其实都在重复上一篇的那句老话------只是缝隙更隐蔽:
让用户数据永远只能当"值",不能当"语法";
让信任只来自明确的白名单与绑定,不来自"我曾经过滤过"。
你若今晚只做一件事:打开你们最老的那个业务库连接配置,看字符集和是否多语句;再找一个"用户可改、管理员可搜"的字段,问开发第二次查询绑参了没有。
这两眼,比再背十个高级 payload 更值回票价。