addslashes失效根本原因是字符编码不一致:PHP按单字节处理字符串,而MySQL以GBK解析时将%df%5c合并为汉字(如「運」),使后续%27裸露为单引号。

宽字节注入怎么让 addslashes() 失效
根本原因是字符编码不一致:数据库用 GBK,而 PHP 字符串处理默认按单字节(如 UTF-8)看待。当输入含 %df%27(即 GBK 下的「運」+ 单引号)时,addslashes() 会在 %27 前加反斜杠变成 %df%5c%27;但 MySQL 在 GBK 模式下会把 %df%5c 合并识别为一个汉字(如「運」),剩下的 %27 就裸奔成了未转义的单引号。
常见触发条件:
-
SET NAMES gbk或连接时指定charset=gbk - PHP 没做
mb_internal_encoding('UTF-8')统一内部编码 - 过滤函数(如
addslashes())在编码转换(如iconv('UTF-8', 'GBK', $input))之前或之后调用顺序错误
urldecode() 二次解码如何吃掉转义
典型链式漏洞代码:$id = addslashes($_GET['id']); $id = urldecode($id);。攻击者提交 id=1%2527(即 URL 编码两次的 1%27),第一次 urldecode() 后变成 1%27,addslashes() 对这个字符串操作——它只看到 % 和 27,不会加反斜杠;第二次 urldecode()(可能在框架或扩展中隐式发生)才还原出真实的单引号 '。
关键点:
立即学习“PHP免费学习笔记(深入)”;
- 只要任意一层解码发生在
addslashes()之后,就可能漏掉真实引号 -
rawurldecode()、base64_decode()、json_decode()同理,本质是「数据形态变化 → 过滤失效 → 执行前还原」 - 这种绕过不依赖数据库编码,纯属 PHP 层逻辑错位
为什么 mb_convert_encoding() 和 iconv() 是高危函数
这两个函数常被用来“统一编码”或“修复乱码”,但它们本身不校验输入合法性,且转换失败时可能静默截断或替换字符。例如:iconv('UTF-8', 'GBK//IGNORE', $input) 遇到无法映射的 UTF-8 字符会丢弃,导致原始字符串结构被破坏,后续过滤基于错误长度或偏移进行,结果不可信。
更危险的是配合宽字节场景:
- 先用
iconv('UTF-8', 'GBK', $input)转换,再用addslashes()—— 此时已进入 GBK 上下文,%df%27类 payload 就能生效 - 若转换前后字符数不等(如 UTF-8 中 3 字节字符转 GBK 成 2 字节),
strlen()判断、正则匹配、甚至 PDO 的参数绑定都可能出偏差
真正防不住的只有参数化查询
所有基于字符转义的方案(addslashes()、mysql_real_escape_string()、自定义 str_replace())都依赖上下文正确——编码一致、无二次解码、无类型混淆。一旦环境失控,防护就形同虚设。
而 PDO::prepare() 或 mysqli_prepare() 把 SQL 模板和参数分两路发给 MySQL,服务端自行完成拼接与执行,用户输入永远不参与语法解析。这意味着:
- 不管输入是
%df%27还是%2527,它只是个字符串值 - 不用管当前连接用的是 UTF-8、GBK 还是 Big5
- 即使你写了
$stmt->bindValue(':id', $_GET['id'], PDO::PARAM_STR),数据库也只把它当数据,不解释
最易被忽略的一点:预处理语句必须全程启用,不能只在部分查询里用,也不能在拼接表名、字段名、ORDER BY 子句时退化回字符串拼接——那些地方只能靠白名单校验,没有“安全转义”这回事。



















