最立竿见影的防御手段是停用addslashes+宽字节字符集组合,改用参数化查询;因mysql_real_escape_string在GBK下依赖连接字符集设置,若未显式绑定或版本老旧,仍会按单字节转义,导致%df%5c被解析为合法GBK字符而失效。

最立竿见影的防御手段只有一条:立刻停用 addslashes + 宽字节字符集(如 gbk)的组合,改用参数化查询。
为什么 mysql_real_escape_string 在 GBK 下也不可靠
很多人误以为 mysql_real_escape_string 比 addslashes 更安全,但它依赖 MySQL 连接层的字符集设置。如果连接已执行 SET NAMES 'gbk',而函数内部未显式指定字符集(或版本老旧),它仍会按单字节逻辑转义,导致 %df%5c 被解析为一个合法 GBK 字符,反斜杠失效。实测中,PHP 5.6+ 的 mysql_real_escape_string 在未绑定连接字符集时,行为与 addslashes 几乎一致。
关键点:
- 该函数不自动同步
SET NAMES的设定,需手动传入连接资源并确保其字符集正确 - PHP 已废弃
mysql_*系列函数,继续使用等于主动放弃维护支持 - 即使生效,它也仅防单引号、反斜杠等,无法覆盖
ORDER BY、LIMIT等非引号上下文的注入
强制 UTF-8 并不等于免疫宽字节注入
把数据库、连接、HTML 页面全设成 utf8mb4 是必要动作,但不是充分条件。因为:
-
%df%5c在 UTF-8 中确实不是合法字符,会被 MySQL 直接截断或报错——但这只对「显式提交该 payload」有效 - 攻击者可转向其他绕过路径,比如利用
CONVERT(... USING gbk)在 SQL 内部临时切换编码,再配合注入 - 若应用层仍用
addslashes处理输入,而前端或中间件(如 Nginx、CDN)做了 URL 解码重写,就可能在到达 PHP 前已触发二次解码,制造出意外的宽字节上下文
参数化查询为何是唯一治本方案
参数化查询(Prepared Statements)从设计上就切断了“数据被当代码解析”的路径。MySQL 驱动将 SQL 模板与参数值分开传输,服务端先编译语句结构,再以二进制协议绑定参数值,完全规避词法解析阶段的混淆。
实操要点:
- 必须用
PDO或mysqli的原生预处理接口,例如$pdo->prepare("SELECT * FROM users WHERE name = ?"),而非字符串拼接后调用query() - 避免“伪预处理”:像
"SELECT * FROM users WHERE name = '" . $pdo->quote($name) . "'"这种仍是拼接,quote()本质还是转义,不是预处理 - 对于动态表名、字段名、
ORDER BY子句等无法参数化的位置,必须白名单校验,不可依赖任何转义或编码转换
真正容易被忽略的点在于:很多团队修复时只改了出问题的那个接口,却没清理掉全局共用的 addslashes 封装函数、或遗留的 mysql_query("SET NAMES 'gbk'") 调用。只要这些代码还存在于任何执行路径中,就等于在系统里埋了一颗随时可被触发的宽字节雷。防御不是加补丁,是拔根。

















