addslashes()在GBK下会“吃掉”反斜杠,因其仅机械替换字节流,而MySQL将高位字节与%5c拼成汉字导致%5c被吞、%27裸露;mysqli_real_escape_string()依赖字符集设置生效,若未调用mysqli_set_charset()则退化为addslashes()行为,且不防无引号上下文及中间件二次解码;唯一可靠方案是参数化查询,动态字段名须白名单校验,遗留系统可临时设character_set_client = binary阻断。

addslashes()在GBK下为何会“吃掉”反斜杠
因为addslashes()完全不感知字符集,它只对字节流做机械替换:遇到'就插一个\(即%5c%27)。但在GBK编码下,MySQL会把前一个高位字节(如%df、%bb、%81–%fe中任意一个)和紧随其后的%5c拼成一个合法汉字(比如「運」或「籠」),导致%5c被“吞掉”,%27重新变成裸单引号——转义彻底失效。
为什么mysqli_real_escape_string()也靠不住
mysqli_real_escape_string()本身比addslashes()强,但它依赖连接层字符集设置是否真正生效:
- 若只执行
mysqli_query($conn, "SET NAMES gbk"),而没调用mysqli_set_charset($conn, "gbk"),该函数内部仍按单字节逻辑转义,行为退化为addslashes() - PHP 5.6+ 中,未显式绑定字符集的
mysqli_real_escape_string()返回false,但很多代码没检查返回值,直接拼接进SQL - 它只处理字符串上下文,对
ORDER BY name、LIMIT 10这类无引号位置完全无效
SET NAMES gbk 是主动打开攻击通道
SET NAMES gbk只是批量设置character_set_client等三个会话变量,并不改变连接底层的字节解析逻辑。问题在于:
- 它常被写在
addslashes()之后,等于先转义、再切换宽字节解析环境,完美制造逃逸条件 - 它无法阻止中间件(如Nginx、CDN)对URL二次解码,导致
%df%255c%27在到达PHP前已变成%df%5c%27 - 即使数据库表是
utf8mb4,只要连接层是gbk,宽字节注入依然成立
真正能切断漏洞链的唯一做法
参数化查询不是“更推荐”,而是唯一能从协议层隔绝数据与代码混淆的方式:
- 必须用
$pdo->prepare("SELECT * FROM users WHERE name = ?")这类原生预处理,而非"WHERE name = '" . $pdo->quote($name) . "'"——后者仍是字符串拼接 - 动态字段名(如
ORDER BY ?)无法参数化,必须用白名单校验:in_array($sort, ['id', 'name', 'created_at'], true) - 如果遗留系统暂时无法全量改造,至少在每个查询前加
mysqli_query($conn, "SET character_set_client = binary"),让MySQL把输入当原始字节处理,直接报错阻断
最容易被忽略的是全局封装函数——搜一遍代码里有没有function safe_sql($s) { return addslashes($s); },以及所有mysql_query("SET NAMES gbk")调用点,这些都得清干净。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











