set names 'gbk'会触发宽字节注入,因其仅设置character_set_client等会话变量,未同步连接层编码上下文,导致addslashes()按单字节转义('→%5c%27),而mysql用gbk解析时将%df%5c组合成汉字,“吃掉”反斜杠使%27逃逸;必须改用mysqli_set_charset()或set character_set_client=binary等方案对齐编码。

为什么 set names gbk 会触发宽字节注入
因为 SET NAMES 'gbk' 只修改了 MySQL 的 character_set_client、character_set_results 和 character_set_connection,但没真正绑定连接层的字符集语义。PHP 调用 addslashes() 时按单字节处理(比如把 ' → \',即 %27 → %5c%27),而 MySQL 在解析时把前面一个高位字节(如 %df)和 %5c 拼成一个 GBK 宽字符(如 “運”),导致 %5c 被“吃掉”,%27 逃逸成裸单引号。
mysql_set_charset() 和 mysqli_set_charset() 是必须替换的点
这两个函数不仅发 SET 命令,还会同步设置连接层的字符集上下文,让 MySQL 在接收数据时严格按指定编码解析,避免宽字节“吞字”行为。不替换 SET NAMES 就等于没修。
-
mysql_set_charset("gbk", $conn)(已废弃,仅用于遗留代码) -
mysqli_set_charset($conn, "gbk")(推荐,需在mysqli_real_escape_string()前调用) - 如果用 PDO,必须显式设置
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4",且不能只靠 DSN 中的charset=gbk
mysql_real_escape_string() 不能替代预编译,但它是宽字节场景下的兜底手段
该函数内部会读取当前连接的实际字符集(由 mysqli_set_charset() 设置),再做符合该编码规则的转义——比如对 GBK,它知道 %df%5c 是合法双字节,不会盲目拼接反斜杠。而 addslashes() 完全无视编码,纯字节流操作,必然出问题。
- 必须确保
mysqli_real_escape_string()调用前,连接已通过mysqli_set_charset()设定好字符集 - 若传入空连接或未初始化连接,函数返回
false,容易被忽略导致绕过 - 它只防字符串型注入,数字型参数仍需
intval()或参数化
character_set_client=binary 是最硬核的防御方式
在查询前执行 mysqli_query($conn, "SET character_set_client = binary"),会让 MySQL 把客户端输入当作原始字节流处理,不再尝试按 GBK 解码。此时 %df%5c%27 不会被识别为“運'”,而是三个独立字节,MySQL 直接报错或截断,无法构成有效 SQL 语法。
- 这个设置只影响当前 query,需每次查询前重设(或封装进统一 query 函数)
- 它不解决乱码问题,但能彻底阻断宽字节注入链路
- 注意:不能和
SET NAMES混用,否则后者会覆盖前者
SET NAMES gbk + addslashes(),就等于门没锁死。










