宽字节注入本质是字符集错配而非转义函数失效,关键在于php与mysql间字节流在gbk等双字节编码下被错误解析;必须端到端统一使用utf8mb4,涵盖连接层(mysqli_set_charset/pdo dsn/set names)、服务端配置(character_set_server)、数据库/表/字段字符集,缺一不可。

宽字节注入不是转义函数没用,是字符集错配
宽字节注入根本不是 addslashes 或 mysql_real_escape_string 写得不够好,而是 PHP 传给 MySQL 的字节流,在 GBK 等双字节编码下被错误解析。比如攻击者发 %df%27(即字节 0xDF 0x27),MySQL 若按 GBK 解析,0xDF 是合法高字节,会把紧随其后的 0x27(单引号)吞掉,变成一个无效汉字,导致后面的单引号逃逸——\ 没了,' 就自由了。
UTF-8 没这个问题:0x5C(反斜杠)和 0x27 都是独立字节,不会组合。所以关键不是“加不加转义”,而是“让整个链路都按 UTF-8 解析”。
PHP + MySQLi 连接层必须显式设 utf8mb4
只改 HTML 的 <meta charset="utf-8">、只在 PHP 文件头写 header("Content-Type: text/html; charset=utf-8"),完全无效。漏洞发生在数据库连接解析请求的那一刻。
以下三处缺一不可:
-
mysqli_set_charset($conn, 'utf8mb4')必须在连接建立后立即调用 - DSN 中带
charset=utf8mb4(如 PDO 构造时) - 执行
SET NAMES utf8mb4命令(但不如set_charset可靠,因可能被中间件拦截)
特别注意:utf8 不行——MySQL 的 utf8 实际是 utf8mb3,不支持 emoji 和部分生僻汉字,且某些旧驱动仍会 fallback 到不安全行为;必须用 utf8mb4。
MySQL 服务端配置也要对齐
即使 PHP 连接设对了,如果 MySQL 服务端默认字符集仍是 latin1 或 gbk,客户端未显式声明时,连接仍可能降级。检查并修正以下几项:
-
character_set_server = utf8mb4(my.cnf 中) collation_server = utf8mb4_unicode_ci- 建库时显式指定:
CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci - 已有表需批量修复:
ALTER TABLE tbl CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
漏掉任一环,比如表字段仍是 CHARACTER SET gbk,那即使连接层是 utf8mb4,MySQL 在存储/比较时仍可能触发隐式转换,埋下隐患。
别再用 mysql_* 函数或 magic_quotes_gpc
mysql_connect、mysql_query 等函数在 PHP 7.0+ 已移除,它们缺乏对字符集的精细控制,set names gbk 这类语句极易让整个连接滑向宽字节上下文。而 magic_quotes_gpc(已废弃)不仅逻辑混乱,还会导致双重转义,反而干扰真实防护逻辑。
正确路径只有一条:用 mysqli 或 PDO,禁用所有自动转义机制,把输入校验、参数绑定、字符集设置全部收归自己可控范围内。预处理语句(prepare + bind_param)虽不能替代字符集对齐,但加上它,等于给 SQL 结构加了一道隔离墙——哪怕字符集某处漏了,至少注入点难进语句主体。
最常被忽略的是:开发环境和生产环境的 MySQL 配置不一致。本地跑着 utf8mb4 没问题,上线后 DBA 给你配了个 latin1 默认值,漏洞就回来了——字符集对齐必须端到端可验证,不能只信代码里写了 set_charset。











