URL参数不应做SQL注入过滤,真正防护应在数据库操作层使用参数化查询;中间件统一清洗无效且危险,仅可对数字ID、排序字段等做白名单校验,ORM的raw调用会绕过所有中间件防护。

URL参数本身不需要、也不应该被“SQL注入过滤”——这是个危险的误解。对QueryString做统一清洗(比如全局替换'、UNION、OR 1=1),既不能防注入,还会破坏合法数据。
真正该做的,是在数据进入数据库前那一层,用参数化查询切断语义解析路径。中间件里搞字符串清洗,属于在错误的位置用力。
为什么中间件统一过滤QueryString是徒劳的
攻击者早就不靠裸关键词了。你过滤SELECT,他写SeLeCt;你拦',他用%27编码再解码;你删--,他换#或/* */注释。正则黑名单在真实对抗中基本失效。
- URL编码/解码只是传输层行为,
decodeURIComponent('%27')结果还是',拼进SQL照样触发注入 - 同一个参数,在不同接口用途完全不同:可能是ID(数字)、搜索词(含单引号)、排序字段(需白名单)——没法用一套规则通杀
- 清洗逻辑若放在中间件,容易漏掉异步任务、定时脚本、CLI命令等非HTTP入口,防线不一致
哪些场景下你确实会看到QueryString被“处理”,但目的不是防SQL
这些操作常被误认为是“SQL防护”,其实目标完全不同:
-
encodeURIComponent()用于前端构造跳转URL,防止&或=被截断,和数据库无关 - 后端
decodeURIComponent()只是还原原始输入,还原完的数据仍要走参数化查询,不能直接拼SQL - 某些WAF产品会对
QueryString做模式匹配并拦截,但这属于边界防御,不可替代应用层参数化
如果非要在中间件加一层校验,只允许这三件事
中间件可以承担轻量级、高确定性的检查,但必须满足:可预测、无副作用、失败即拒绝。
- 对已知只接受数字的参数(如
id),用parseInt()或正则/^\d+$/强校验,非法值直接400 - 对动态排序字段(如
order_by),只放行白名单:['created_at', 'score', 'updated_at'],其余拒掉 - 对IN查询的多个ID,不尝试“过滤”,而是拆成数组后逐个
parseInt(),再生成对应数量的?占位符
最易被忽略的一点:ORM的.raw()、.extra()、text()调用,会绕过所有中间件清洗逻辑——因为它们在DAO层才拼SQL。防御不在URL怎么来,而在cursor.execute()那行怎么写。

















