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' 只修改了 MySQL 的 character_set_client、character_set_results 和 character_set_connection,但没真正绑定连接层的字符集语义。PHP 调用 addslashes() 时按单字节处理(比如把 ' → ',即 %27 → %5c%27),而 MySQL 在解析时把前面一个高位字节(如 %df)和 %5c 拼成一个 GBK 宽字符(如 “運”),导致 %5c 被“吃掉”,%27 逃逸成裸单引号。
这两个函数不仅发 SET 命令,还会同步设置连接层的字符集上下文,让 MySQL 在接收数据时严格按指定编码解析,避免宽字节“吞字”行为。不替换 SET NAMES 就等于没修。
mysql_set_charset("gbk", $conn)(已废弃,仅用于遗留代码)mysqli_set_charset($conn, "gbk")(推荐,需在 mysqli_real_escape_string() 前调用)PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4",且不能只靠 DSN 中的 charset=gbk
该函数内部会读取当前连接的实际字符集(由 mysqli_set_charset() 设置),再做符合该编码规则的转义——比如对 GBK,它知道 %df%5c 是合法双字节,不会盲目拼接反斜杠。而 addslashes() 完全无视编码,纯字节流操作,必然出问题。
mysqli_real_escape_string() 调用前,连接已通过 mysqli_set_charset() 设定好字符集false,容易被忽略导致绕过intval() 或参数化在查询前执行 mysqli_query($conn, "SET character_set_client = binary"),会让 MySQL 把客户端输入当作原始字节流处理,不再尝试按 GBK 解码。此时 %df%5c%27 不会被识别为“運'”,而是三个独立字节,MySQL 直接报错或截断,无法构成有效 SQL 语法。
SET NAMES 混用,否则后者会覆盖前者SET NAMES gbk + addslashes(),就等于门没锁死。