先说一个许多开发者容易踩的陷阱:正则表达式本身无法真正防御SQL注入攻击。它充其量只能在前端做一层提示,或在日志中筛选异常行为——真正要在数据库执行前拦截恶意输入,唯一可靠的方案是 mysqli::prepare 或 PDO::prepare 参数化查询。下面这张图虽然看起来挺有威慑力,但光靠正则,漏洞依然存在。

为什么用正则过滤SQL注入关键词存在风险
攻击者早已不是当年那个老老实实写 SELECT 的初级黑客了。如今他们会使用 sel/**/ect、%27(URL编码的单引号)、UNI/**/ON,甚至通过双写绕过 SELESELECTCT——这些手法都能轻松规避 /union|select|drop/i 这类简单规则。更棘手的是,用 preg_replace 直接删除匹配内容,会破坏合法字段。比如用户昵称叫“Union Jack”,删除后就变成“Jack”;而删掉 WHERE 后面的空格,还可能触发 MySQL 的 strict mode 报错。
常见错误现象包括:
PREG_BACKTRACK_LIMIT_ERROR:超长输入搭配贪婪匹配(如.*),导致 PCRE 回溯溢出- 中文截断:PHP 中未添加
u修饰符,preg_match在 UTF-8 字符串中直接崩溃 - 大小写漏判:
/i原本是为了兼容,结果让selECT溜过去;不如直接限制该字段
哪些正则场景勉强可用
正则只在两类场景下能派上用场,而且必须配合后端的参数化查询:
- 前端 JS 表单校验:比如拦截明显畸形的输入
' OR 1=1 --、连续5个以上括号(((((、裸露分号结尾; - 日志关键词筛查:从 access.log 或 audit 日志中快速提取疑似攻击片段,比如匹配
/(%27)|(')|(--)|(#)/,用于告警而非阻断
推荐写法示例(PHP):
if (preg_match('/['";--=<>]|--|*//', $input)) { // 记录日志 + 前端提示,但不拒绝请求 error_log("Suspicious input: " . $input); echo "输入含非法符号,请检查";}
注意:['";--=] 中的连字符 - 必须放在最后,否则会被解释为范围符;* 和 / 需要转义。
真正该做的三件事(顺序不能错)
与其纠结“怎么写正则”,不如把精力放在以下三步上,防护效果比任何字符串匹配都强:
- 所有数据库操作统一走
mysqli::prepare或PDO::prepare,变量全部用?占位符替代 - 用户输入进入 SQL 之前,先通过
filter_var($input, FILTER_SANITIZE_SPECIAL_CHARS)(PHP 8.1+ 推荐),或者至少用htmlspecialchars($input, ENT_QUOTES, 'UTF-8')进行过滤 - 数据库账号权限最小化——应用账号只赋予
SELECT、INSERT,绝对不要开放FILE、EXECUTE或DROP
正则唯一不可替代的作用,是帮你发现异常输入模式;但它永远无法定义“合法 SQL”。比如 JSON 字段中嵌入了 SQL 片段,或者 GraphQL 查询体混入了原生语句——这类边界情况只能靠协议层和权限层来兜底。
