黑名单过滤无法防御sql注入,因其仅匹配原始字符串而数据库执行语法树,存在语义鸿沟;绕过方式多样且规则维护成本高,参数化查询才是根本解决方案。

黑名单匹配和数据库解析根本不在同一层
数据库执行的是语法树,而黑名单只看到原始字符串。比如 UNION SELECT 被拦了,但 UnIoN/**/SeLeCt 在字符串层面不匹配任何规则,MySQL 却会忽略注释、不区分大小写,最终照样执行为标准查询。这种语义鸿沟不是调参能填平的,是设计原理上的错位。
绕过方式太多,维护成本远超收益
攻击者常用组合变形:URL 编码(%75nion)、内联注释(/*!50000UNION*/)、空白符替换(%09、%0a、/**/)、Unicode 编码(%u0055%u004e%u0049%u004f%u004e)。每加一条正则,就可能误杀合法输入(如用户昵称含 UNION JACK),同时漏掉新变体。你永远追不上绕过节奏。
过滤时机错位导致“先解码后匹配”失效
常见错误是直接对原始 request body 做正则扫描,但 URL 解码、字符集转换、中间件转义往往发生在过滤之后。结果就是:id=1%20UNION%20SELECT 过滤时还是编码串,放行;到数据库执行前才被解成空格+关键词。更麻烦的是,不同语言/框架/中间件对 %00、%a0(NBSP)等处理不一致,同一条规则在 Nginx、PHP、Java 层表现完全不同。
双写、嵌套、上下文逃逸让简单替换彻底失效
用 str_replace('union', '', $input) 这类逻辑,遇到 ununionion 就变成 union;用 preg_replace('/union/i', '', $input) 遇到 SEL/**/ECT 或 SELE%0bCT(换行符插入)就完全失效。而真实攻击往往多层叠加——比如 %27%20OR%201%3D1%23 + 内联注释 + 宽字节拼接,单靠字符串层过滤毫无意义。











