addslashes()在GBK环境下失效,因它不识别字符集,对%df\'转义后被MySQL解码为汉字导致单引号裸露;mysqli_real_escape_string()按连接字符集转义更安全,但仍属拼接式防御;首选预处理语句实现SQL与数据分离。

addslashes() 为什么在GBK环境下会失效
它根本不知道数据库用什么字符集,只机械地在 '、"、\、\0 前加反斜杠。当 MySQL 连接设为 gbk 时,%df\' 这样的输入会被拼进 SQL 后,由 MySQL 解码成一个合法汉字(如「運」),而原本用来转义的 \ 就被“吞掉”了——单引号重新裸露出来,注入成立。
常见错误现象:
- 测试输入
%df',页面报错或返回异常数据,但addslashes()日志显示已转义 - 数据库里查不到
\字符,说明它没进库,但注入却成功了
mysqli_real_escape_string() 比 addslashes() 强在哪
它依赖已建立的 MySQL 连接,会读取当前连接的 character_set_client,按实际字符集做转义。对 gbk 连接,它知道 %df 是双字节头,不会让后续的 ' 被误判为独立字符;对 utf8mb4,它也只按 UTF-8 规则处理。
使用前提和限制:
- 必须先调用
mysqli_connect()或mysqli_init() + mysqli_real_connect(),否则会警告 - 不能用于 PDO(PDO 不提供对应函数),也不能跨连接复用结果
- 若连接字符集未显式设置(比如靠 MySQL 默认值),仍可能出问题
为什么连 mysqli_real_escape_string() 都不该作为首选
它只是“更安全的字符串拼接”,不是“安全的查询方式”。只要还在拼 SQL 字符串,就存在逻辑漏洞风险:
- 数字型参数漏判类型,比如
id=1 OR 1=1,mysqli_real_escape_string()对空格、OR完全不处理 - 动态表名/列名/排序方向(
ORDER BY ?)无法参数化,有人会退化为拼接 + 转义,这就又回到原点 - 多个转义函数混用(比如先
addslashes()再mysqli_real_escape_string())会导致双重转义,破坏原始语义
真正该做的:跳过所有转义,直奔预处理
预处理语句把 SQL 模板和数据彻底分离。MySQL 在服务端解析模板时就锁定了结构,后续传入的参数只当纯数据处理,'、;、-- 全部失去语法意义。
实操建议:
- 用
PDO::prepare()或mysqli_prepare(),不要用mysql_query("SELECT * FROM t WHERE id = '".mysqli_real_escape_string($c, $_GET['id'])."'") - 数字参数也走占位符,别图省事写成
"WHERE id = ".(int)$_GET['id']——这能防注入,但类型强制可能掩盖业务逻辑错误 - 如果 ORM 框架支持原生 SQL 查询(如 Laravel 的
DB::select()),确认它底层是否调用预处理;否则手动换PDO
最易被忽略的一点:预处理只保参数,不保 SQL 结构。表名、字段名、GROUP BY 子句这些动态部分,必须白名单校验,不能靠任何转义函数兜底。











