宽字节SQL注入必须从字符集源头根治:PHP+MySQLi需显式调用mysqli_set_charset($conn, 'utf8mb4'),表结构、字段、连接DSN、响应头均须统一为utf8mb4,缺一不可。

宽字节SQL注入漏洞不能靠“多加一层转义”解决,必须从字符集源头切断%df%5c被解析为单个汉字的路径。只改HTML或PHP文件编码没用,关键在数据库连接层与存储层的字符集对齐。
PHP + MySQLi 必须显式设置 utf8mb4 字符集
很多项目只在连接后执行 SET NAMES utf8 或依赖配置文件默认值,这实际启用的是 MySQL 的 utf8(即 utf8mb3),不支持 emoji 且仍存在宽字节边缘风险。
- 连接建立后立即调用
mysqli_set_charset($conn, 'utf8mb4')—— 这步不可省略,mysql_set_charset已废弃 - DSN 中显式声明字符集:如使用
new mysqli(...),应在构造后立刻set_charset;若用 PDO,则 DSN 中必须含;charset=utf8mb4 - 避免写
utf8、UTF8、UTF-8—— mysql2(Node.js)和 PHP MySQLi 都会将其映射为utf8mb3,不是真正安全的 UTF-8
表结构与字段必须用 utf8mb4 COLLATION
SHOW CREATE TABLE users 返回结果里若出现 DEFAULT CHARSET=gbk 或 COLLATE=gbk_chinese_ci,说明存储层仍是宽字节,前面所有连接层努力都会失效。
- 修改表:执行
ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci - 新建表时明确指定:
CREATE TABLE ... ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci - 检查字段级定义:即使表是 utf8mb4,某个
VARCHAR字段若手动指定了CHARSET gbk,它仍会触发宽字节解析
不要依赖 character_set_client=binary 作为主要修复手段
虽然 SET character_set_client=binary 能让 MySQL 把输入当二进制流处理,绕过宽字节组合逻辑,但它只是“掩盖症状”,不是根治方案。
- 它会让
LIKE、ORDER BY、全文索引等依赖字符语义的功能异常或失效 - 如果后续代码中用了
CONVERT(... USING gbk)或iconv('utf-8', 'gbk', $str),依然可能重新引入宽字节路径 - 它无法防止其他组件(如中间件、ORM、日志模块)在字符转换环节出错,属于高风险补丁
Web 响应头与前端必须同步声明 utf-8
浏览器若误判页面编码为 GBK,即使后端已全量 utf8mb4,用户提交的 URL 编码(如 %A1%5C)仍会被按 GBK 解析,再传给 PHP —— 此时 PHP 再转义也晚了。
- PHP 中输出前加
header('Content-Type: text/html; charset=utf-8'); - HTML 中必须有
<meta charset="utf-8">,且放在<head>最前面 - 检查 Nginx/Apache 是否覆盖了响应头,例如 Nginx 的
charset off;或charset gbk;配置会直接冲掉 PHP 设置
最容易被忽略的是字段级字符集和 Web 服务器响应头——它们不像连接设置那样显眼,但只要其中一处漏掉,%df%5c%27 就仍可能在某条路径上重组为有效单引号。

















