单纯字符串替换(如str_replace、addslashes、'→'')无法防御SQL注入,因其不感知SQL语法上下文,对数字型参数、标识符、注释符等无效,且数据库转义机制不统一;唯一可靠方案是全程使用参数化查询。

不能用 str_replace、addslashes 或单引号替换成双引号(' → '')来防 SQL 注入——不是因为写得不够多,而是这类操作从原理上就失效。
数字型参数拼接时,字符串替换完全不起作用
当 SQL 语句里写的是 WHERE id = $input(没加引号),攻击者传入 1 OR 1=1,结果变成:WHERE id = 1 OR 1=1。所有字符串替换函数对空格、OR、= 都不做处理。
- 必须用参数占位符,比如
WHERE id = ?,再绑定整型值 - 别图省事写
"WHERE id = " . (int)$_GET['id']:非法输入如abc会被静默转成0,掩盖业务异常 - ORM 中手写
raw()或FromSqlRaw()就等于主动放弃防护
注释符(--)、分号(;)、括号等根本不在替换范围内
-- 被删掉看似安全,但攻击者可构造 admin' -- 让后续校验逻辑被注释掉;更危险的是 ; DROP TABLE users--,字符串替换只删 --,却放行了分号和语句终止。
-
str_replace('--', '')会误伤正常数据,比如 URL 中的path--v2或字段值含-- not a comment - MySQL 支持
/*!50000SLEEP(5)*/,这种内联注释多数过滤器直接跳过,但数据库照常执行 - 真正起作用的是参数化查询:此时
--在参数里就是普通字符,不会触发注释行为
不同数据库对“转义”的理解根本不一致
'' 只在 SQL Server 字符串字面量中有效;MySQL 默认用反斜杠(\'),PostgreSQL 用 quote_literal() 或 E'' 语法。'' 在 MySQL 里只是两个连续单引号,解析为字符串拼接,不是转义。
- 宽字节场景下(如 GBK 连接未设 charset),
%df%27解码后变成一个汉字 + 单引号,mysqli_real_escape_string按 latin1 处理会漏掉真实引号 - 预编译语句在服务端编译时结构已固定,后续传参不经过 SQL 解析器,单引号、分号、
--全部失去语法意义 - 检查连接字符集是否生效:执行
SELECT @@character_set_client, @@collation_connection,确保和 PHP 设置一致
标识符(表名、列名、排序方向)无法参数化,替换毫无意义
ORDER BY $_GET['sort'] 或 SELECT * FROM $_GET['table'] 这类位置,连 ? 占位符都不支持。有人拼接 + 转义,但 mysqli_real_escape_string 对空格、分号、点号毫无作用。
- 必须走白名单校验:
in_array($_GET['sort'], ['name', 'email', 'created_at'], true) - 避免动态表名;真要支持多表,用配置映射(如
$tables = ['user' => 'users']),而不是直取用户输入 - 所有用户输入进入 SQL 前,必须走参数化路径,不区分字段类型、不看输入内容
最易被忽略的点是:SQL 注入的本质是上下文混淆——把数据当代码执行。而字符串替换无法感知当前输入落在哪个 SQL 位置(字符串值?数字?标识符?注释区?)。一旦错位,防御就形同虚设。

















