addslashes无法防御宽字节sql注入,根本原因是其纯字节级转义不感知字符集,当mysql以gbk解析时,%df%5c被合成为汉字(如「運」),导致%27裸露成单引号,转义被“吃掉”。

addslashes 无法防御宽字节 SQL 注入,根本原因在于它完全不感知字符集,只做无脑字节替换。 它看到 ' 就加 ,变成 '(即 %5c%27),但 MySQL 在 GBK 连接下会把前面一个高位字节(如 %df)和这个 %5c 合起来解析成一个汉字(如「運」),导致 %27 裸露成真正的单引号——转义被“吃掉”了。
addslashes 的字节级操作 vs MySQL 的多字节解析
这是两种不同层级的处理逻辑冲突:
-
addslashes()工作在 PHP 字符串的原始字节层,对%df%27输入,输出是%df%5c%27 - MySQL 在
character_set_client=gbk下收到该字节流后,按双字节规则扫描:%df%5c→ 合法 GBK 字符(如「運」),%27→ 独立单引号 → SQL 解析器直接执行WHERE id='1運'',语法错误或逻辑逃逸 - 这种错位不是函数 bug,而是设计定位错误:addslashes 本就不是为防注入而生,它只保证字符串字面量语法不出错
为什么 %df 最常见,而不是其他高位字节?
并非所有高位字节都稳定有效,实际可用性取决于 MySQL 版本、GBK 实现细节和连接配置:
-
%df兼容性最高:在绝大多数 GBK 环境下都能与%5c组合成合法宽字符,且不会触发连接中断或警告 -
%a1–%a9在部分 MySQL 5.5+ 中可能被识别为控制字符,导致 query 失败 -
%5c自身不能单独出现:若输入里本来就有反斜杠,addslashes 会生成%5c%5c,再拼%df就变成%df%5c%5c%27,MySQL 可能解析为「籠\'」,依然漏掉单引号
mysqli_real_escape_string 也未必安全?
它比 addslashes 强,但仍有前提条件:
- 必须确保连接字符集已正确设置,例如调用
mysqli_set_charset($conn, 'gbk')或执行SET NAMES gbk——否则它默认按 latin1 转义,%df%27仍会被 GBK 解析逃逸 - 如果先调用
addslashes(),再执行SET NAMES gbk,等于主动把转义后的%5c暴露给宽字节解析器,风险更高 - 它仍属于“上下文无关转义”,对数字型参数(如
WHERE id = $id)完全无效,攻击者可直接传1 OR 1=1
真正该检查的三个硬性条件
只要缺一,%df%27 类 payload 就是废码:
- MySQL 连接层字符集必须是 GBK / GB2312 / BIG5(查
SELECT @@character_set_client) - PHP 层用了
addslashes()或mysql_escape_string()这类无字符集感知的函数 - 表字段实际存储编码也得支持宽字节(如
CHARSET=gbk),否则 server 层内部转换时可能二次截断或报错
现代开发中还在踩这个坑,往往是因为沿用了废弃的 mysql_* 扩展,或者把 SET NAMES gbk 当成万能配置,却忽略了 client 层解码行为本身就在打开攻击通道。











