字符型注入需先用单引号闭合原始sql字符串包裹符,再注入逻辑,而数字型无需闭合、直接拼接;前者因引号既是语法必需又易被滥用,防御须按字段场景精细化处理,后者可通过类型转换简单阻断。

字符型注入的闭合逻辑更隐蔽
数字型注入直接拼接在无引号包裹的位置,比如 WHERE id = 1,攻击者加 AND 1=1 就能立刻生效,但防御也直观:只要把输入转成整型、用 intval() 或类型强转,基本就断掉了路径。
字符型注入则不同,它依赖引号闭合——比如原始 SQL 是 WHERE username = 'admin',攻击者必须先输入一个单引号(')来“结束”字符串,再插入自己的逻辑。这个单引号本身不是恶意代码,而是语法必需品;过滤它会破坏正常用户名(如 O'Reilly),不过滤又留了入口。很多开发者卡在这里:该不该删单引号?怎么区分合法引号和攻击引号?
- 手动
str_replace("'", "''")只对 MySQL 有效,SQL Server 要用两个单引号,Oracle 用两个单引号但语义不同 - 用
addslashes()会受magic_quotes_gpc(已废弃)或宽字节编码影响,在 GBK 环境下%df%27可绕过 - 前端 JS 的
escape()或encodeURIComponent()完全无效——后端解析时早已解码
参数化查询在字符场景下更容易被绕过
很多人以为用了 mysqli_prepare() 或 PDO 的 prepare() 就万事大吉,但字符型注入仍可能漏掉——尤其当开发者把用户输入拼进表名、字段名、ORDER BY 子句或 LIKE 模糊匹配的通配符位置时。
例如:SELECT * FROM users WHERE name LIKE '%?%',这里的 % 是硬编码,但问号占位符只覆盖中间内容;如果用户输入的是 %' OR '1'='1,最终拼出来就是 WHERE name LIKE '%\' OR \'1\'=\'1%',引号没被参数化,照样闭合成功。
-
ORDER BY ?不合法:MySQL 不允许参数化列名或排序方向,只能白名单校验 -
GROUP BY ?同理,PDO 会直接报错SQLSTATE[HY093] -
IN (?)不能展开多个值,IN (1,2,3)必须动态生成占位符,易出错
字符型注入常混在业务逻辑里,检测成本高
数字型注入多出现在 URL 参数如 ?id=1、?page=2,规则明确,WAF 或审计工具容易识别异常模式(比如 1 AND 1=1)。字符型注入却藏在登录框、搜索框、昵称、地址栏等一切允许输入文字的地方,合法输入本身就包含空格、引号、连字符、emoji,边界模糊。
比如一个搜索功能:SELECT * FROM articles WHERE title LIKE CONCAT('%', ?, '%')。用户搜 SQL injection 正常,搜 SQL' OR '1'='1 就触发注入——但这两个输入在长度、字符集、HTTP 头、请求频率上几乎无法区分。
- 日志里看不出区别:
username=admin和username=admin'--都是 POST body 的普通字符串 - 数据库审计日志只记录最终执行语句,不记录参数来源,溯源困难
- 基于规则的 WAF 容易误杀(把
don't当攻击)或漏杀(用 Unicode 引号‘替代 ASCII')
真正难的不是技术方案,而是所有字符型上下文都要求「按需防御」:每个字段得单独判断能否参数化、是否需要白名单、要不要二次编码、是否涉及 LIKE / ORDER BY / 动态表名。漏掉任意一个点,前面的防护就形同虚设。











