单纯靠转义特殊字符无法彻底根除sql注入,因其仅作用于字符串上下文,对数字参数、标识符、order by、limit、多语句、注释符等无效,且受字符集、数据库差异、配置失误影响,而预处理语句能从机制上隔离数据与代码。

单纯靠转义特殊字符无法彻底根除SQL注入,是因为它只在“字符串上下文”中有效,而SQL语句的结构远不止字符串拼接一种形式。
转义函数(如 mysql_real_escape_string)只处理单引号、反斜杠等有限字符
这类函数默认只对 SQL 字符串字面量中的引号、空格、NUL 等做转义,但完全不干预数字、标识符、ORDER BY 子句、LIMIT 参数等非字符串位置。比如:
SELECT * FROM users WHERE id = $_GET['id']
当 $_GET['id'] 是 1 OR 1=1 时,没有单引号,转义函数根本不会出手——结果就是直接执行恶意逻辑。
- 转义函数不校验输入类型,
int型参数仍可能被传入表达式 - 对 MySQL 中的标识符(如表名、列名)完全无能为力,这些位置不能加引号,也无法用字符串转义覆盖
- 不同数据库的转义规则不一致(如 PostgreSQL 的
quote_literal和 MySQL 的行为差异),跨库迁移时极易失效
多字节编码与宽字符绕过(如 GBK 下的 %A1%BF)
在旧版 PHP + MySQL 组合中,若连接字符集设为 gbk,攻击者可用形如 %A1%BF' OR '1'='1 的双字节序列让转义函数漏掉真正的单引号——因为 %A1%BF 被解析为一个汉字,其后的 ' 就成了独立的、未被转义的 SQL 引号。
- 该漏洞在
mysql_set_charset('gbk')且使用addslashes()时稳定复现 -
mysql_real_escape_string()本应防御此问题,但前提是连接字符集设置正确且被调用;一旦遗漏或错配,防线即崩 - 现代 PDO 或 mysqli 默认使用
utf8mb4,但遗留系统或手动配置失误仍常见
堆叠查询和注释符让转义彻底失效
当后端使用 mysqli_multi_query() 或允许分号执行多语句时,攻击者可直接闭合当前语句并追加新命令,例如:
1; DROP TABLE users; --
此时无论你如何转义单引号,分号已触发第二条语句执行。注释符(--、#、/* */)还能截断后续校验逻辑。
- 转义函数从不处理分号、括号、注释符,它们不是“字符串内容”,而是 SQL 语法符号
- 只要应用层允许执行多语句(如某些管理后台、调试接口),转义就退化为装饰性操作
- 即便关闭
multi_query,UNION SELECT或ORDER BY (SELECT ...)这类结构也不依赖引号,转义毫无意义
真正可靠的防线是把“数据”和“代码”从机制上隔离——用 prepare() + bind_param() 让数据库自己区分变量位置,而不是靠 PHP 层拼命修补字符串。哪怕一个参数来自不可信来源,只要走预处理流程,数据库引擎就不会把它当 SQL 解析。这点,任何转义函数都做不到。











