预处理语句未彻底消灭SQL注入,因落地偏差、ORM误用、错误过滤及新型绕过技术导致非标准上下文注入频发。

因为开发者仍在拼接SQL语句,而攻击者总能找到没被覆盖的过滤盲区。
为什么预处理语句没彻底消灭SQL注入
PDO预处理、JDBC PreparedStatement、Python的sqlite3.execute()带参数调用——这些方案本身没问题,但落地时容易出偏差:
- 只在登录、搜索等“显眼”功能用了预处理,而导出报表、动态排序字段、
ORDER BY子句仍用字符串拼接 - 误以为“用了ORM就绝对安全”,结果在Django中写了
raw()、Flask-SQLAlchemy里用了text(),又回到拼接逻辑 - 把用户输入先做
strip_tags()或htmlspecialchars()再塞进SQL——这两者防XSS,对SQL注入完全无效
新型绕过技术盯上了“合法语法”边界
当传统单引号、--、#被WAF拦截,攻击者转向数据库解析器的合法歧义点:
- MySQL中用
/* */注释包裹关键字:admin' /* foo */ OR /* bar */ '1'='1,部分旧版WAF无法识别嵌套注释 - 利用函数别名绕过关键词检测:
UNION SELECT→UNI/**/ON SEL/**/ECT,空格被注释替代 - 用十六进制或Unicode编码绕过正则匹配:
0x554e494f4e(UNION的hex)或%u0055%u004e%u0049%u004f%u004e - 针对JSON字段的注入:PostgreSQL的
->>操作符若未参数化,' || '1'='1可拼进JSON路径表达式
真正难防的是“非标准上下文”的注入点
SQL注入早已不限于WHERE条件。以下场景常被忽略,且难以统一用预处理覆盖:
- MySQL的
LOAD DATA INFILE语句中,文件路径来自用户输入,INTO TABLE后表名若未校验,可触发任意表写入 - PostgreSQL的
COPY FROM PROGRAM(9.3+),允许执行系统命令,若program参数可控,等于直接命令注入 - Oracle中
UTL_HTTP.REQUEST()或DBMS_XMLQUERY.GETXML()这类PL/SQL函数,参数拼接即导致服务端请求伪造(SSRF)或二次注入 - 动态视图创建:
CREATE VIEW v AS SELECT * FROM+ user_input,表名未白名单校验,可跨库引用
最麻烦的不是技术多难懂,而是每个数据库方言、每种ORM的“例外通道”、每次为兼容老代码做的临时妥协,都在悄悄扩大攻击面。防御必须跟着上下文走,而不是只盯着SELECT ... WHERE这一行。

















