转义函数对数字型参数完全无效;必须区分上下文:字符串值才需转义,数字/布尔值应强转类型,动态标识符须白名单校验,预处理语句才是根本防护。

转义函数对数字型参数完全无效
当 SQL 语句中拼接的是无引号包裹的数字字段,比如 WHERE id = $id,攻击者传入 1 OR 1=1 就不会触发单引号——而所有常见转义函数(addslashes()、mysql_real_escape_string())只处理引号、反斜杠等字符串边界符,对纯逻辑表达式毫无反应。
这类场景下,转义不仅没用,还可能干扰类型解析:比如把 (int)$id 强转后拼进 SQL 更安全,但若先过一遍 addslashes() 再拼,反而可能让整数变成带反斜杠的非法字符串。
- 必须区分上下文:字符串值才需转义,数字/布尔值必须强转类型
- 不要“统一过一遍”转义函数,那是掩耳盗铃
- PHP 中推荐用
(int)或intval()处理 ID 类参数
宽字节编码可绕过 addslashes()
addslashes() 完全不感知数据库连接字符集,它只是机械地在单引号前加反斜杠。在 MySQL 使用 gbk 字符集时,攻击者发送 %df%27(即 運'),MySQL 会把 %df%5c(運\)识别为一个汉字,导致后续的 %27(单引号)逃逸出来,绕过转义。
mysqli_real_escape_string() 能规避此问题,但它依赖正确的连接字符集设置;一旦漏掉 mysqli_set_charset($conn, 'utf8mb4'),防线就形同虚设。
- 旧系统若仍用 GBK,
addslashes()等价于没防护 - 即使换用
mysqli_real_escape_string(),也必须显式设置字符集 - 现代应用应默认使用
utf8mb4,并确保 DSN 或连接初始化中包含;charset=utf8mb4
ORDER BY、LIMIT、表名等位置无法转义
SQL 中很多地方根本不能加引号,比如排序字段:ORDER BY $_GET['sort'];或分页偏移:LIMIT $_GET['offset'], 10。这些位置既不是字符串也不是数字字面量,而是语法结构的一部分。
转义函数对此类输入完全无能为力。攻击者传入 sort=id; DROP TABLE users-- 或 offset=1 UNION SELECT password FROM users,数据库照单全收。
- 动态标识符(列名、表名、数据库名)必须走白名单校验,例如
in_array($sort, ['name', 'created_at'], true) -
LIMIT的第一个参数必须强转为非负整数:max(0, (int)$_GET['offset']) - 任何涉及语法结构的位置,都不能靠字符串处理来“修补”
多语句与注释符让转义彻底失效
如果后端启用多语句执行(如 mysqli_multi_query()),攻击者直接用分号分割语句:1; DROP TABLE users;。转义函数从不处理分号、括号或注释符——它们不是字符串内容,而是 SQL 解析器的语法符号。
同样,--、#、/* */ 可截断后续校验逻辑,让防御代码“看不见”后面的内容。
- 除非业务明确需要,否则禁用多语句执行(PDO 默认关闭,mysqli 需手动关)
- 错误信息不要回显到前端,避免泄露表结构或字段名
- 即便用了预处理语句,也要确保服务端
character_set_client、character_set_connection、character_set_results三者一致,否则预处理也可能被绕过
真正卡住 SQL 注入的,从来不是“怎么转义”,而是“哪里不能拼”。只要还存在字符串拼接,就永远有遗漏的上下文和绕过的路径。











