参数化查询是唯一可靠的防护手段,转义函数如addslashes()、mysqli_real_escape_string()仅为补丁式处理,易被宽字节、多编码等绕过;必须严格分离sql结构与数据,且字符集需显式设为utf8mb4。

参数化查询是唯一可靠的防护手段
转义字符(比如 addslashes()、mysqli_real_escape_string())不能替代参数化查询,它们只是“补丁式”处理,容易被绕过。真正防住 SQL 注入的,只有数据库引擎层面的语句结构与数据分离——也就是预处理语句(PDO::prepare() 或 mysqli_prepare())。一旦你把用户输入拼进 SQL 字符串里,哪怕加了转义,攻击者仍可能利用宽字节、多编码、二阶注入等手法突破防线。
转义函数为什么不可信
常见错误现象包括:mysqli_real_escape_string() 在 GBK 连接下会失效;addslashes() 完全不识别字符集,对 Unicode 编码或 URL 编码输入毫无防御力;用它处理后拼进 ORDER BY 或 LIMIT 子句,照样被注入。
- 它只做字符串替换,不改变 SQL 解析逻辑
- 必须和当前连接字符集严格匹配,否则一个
%df%27就能绕过 - 无法防护动态标识符(如表名、字段名),而这些恰恰是高频翻车点
- ORM 中调用
whereRaw()时若依赖转义,等于主动退出安全区
参数化查询的硬性使用边界
不是所有地方都能直接用 ? 或 :name 占位符。MySQL 明确不支持参数化标识符——SELECT * FROM ?、ORDER BY ?、LIMIT ?, 10(第一个参数)都会报错或行为异常。
-
WHERE条件值、INSERT字段值、UPDATE SET右侧值:放心用?或命名参数 -
IN列表需动态生成占位符:WHERE id IN (?, ?, ?),再逐个绑定 - 排序字段、分组字段、表名、数据库名:必须走白名单校验,例如
in_array($sort, ['created_at', 'status'], true) -
LIMIT偏移量:强转为整型并检查非负,(int)$offset,不能绑定
字符集配置不当会让参数化也失效
即使用了 PDO::prepare(),如果连接未声明 charset=utf8mb4,或者服务端变量 character_set_client、character_set_connection、character_set_results 不一致,某些多字节字符仍可能触发解析歧义,导致预处理失效。
- PDO DSN 必须显式带
;charset=utf8mb4 - mysqli 需在连接后立即调用
mysqli_set_charset($conn, 'utf8mb4') - 建表语句中字段也要用
utf8mb4_unicode_ci,否则索引可能不生效
最容易被忽略的是服务端默认字符集没改,只改了应用层——这时 SHOW VARIABLES LIKE 'character_set%' 查出来的三者很可能还是 latin1。










