sqlparse不能直接拦截SQL执行,它仅是纯Python词法语法解析器,不连接数据库、不参与执行;拦截需由开发者在execute前主动调用其解析结果做结构化校验。

sqlparse能直接拦截SQL执行吗
不能。sqlparse 是纯解析器,不连接数据库,也不参与执行流程。它只做词法/语法分析,把字符串拆成Statement、Token等结构。所谓“拦截”,必须由你主动在执行前插入校验逻辑——比如在 ORM 的 execute() 调用前、或 DB API 的 cursor.execute() 包装层里调用 sqlparse.parse() 做检查。
如何识别常见恶意SQL模式
靠正则匹配关键词(如 UNION SELECT)容易误杀或绕过;用 sqlparse 解析后遍历 AST 才可靠。关键看 Token 类型和子树结构:
-
Token.ttype为Keyword.DML且值是DELETE或UPDATE,但没带WHERE子句 → 高危 - 存在
Token.ttype == Keyword.CTE(即WITH)且后续含SELECT+UNION→ 可能堆叠注入 -
IdentifierList中出现非白名单字段名(如password,token),且父节点是SELECT→ 敏感字段泄露风险 - 任意
Function节点的name是LOAD_FILE、SLEEP、BENCHMARK→ 明确恶意函数调用
实际拦截代码怎么写
下面是一个轻量包装函数,适用于手动拼接 SQL 的场景(不推荐,但现实存在):
import sqlparse
from sqlparse.sql import Statement, IdentifierList, Function
from sqlparse.tokens import Keyword, DML, CTE, Name
<p>def is_malicious_sql(sql: str) -> bool:
parsed = sqlparse.parse(sql)
for stmt in parsed:
if not isinstance(stmt, Statement):
continue</p><h1>检查无 WHERE 的 DELETE/UPDATE</h1><pre class="brush:php;toolbar:false;"> if stmt.get_type() in ("DELETE", "UPDATE"):
where_token = stmt.token_first_match(Keyword, "WHERE")
if where_token is None:
return True
# 检查危险函数
for func in stmt.flatten():
if isinstance(func, Function):
name_token = func.get_name()
if name_token and name_token.upper() in ("LOAD_FILE", "SLEEP", "BENCHMARK"):
return True
# 检查 UNION 在 WITH 后(简化判断:找 WITH 后紧跟 UNION)
tokens = list(stmt.flatten())
for i, t in enumerate(tokens):
if t.match(CTE, "WITH") and i + 2 < len(tokens):
if tokens[i + 2].match(Keyword, "UNION"):
return True
return False使用示例
if is_malicious_sql("DELETE FROM users;"): raise ValueError("Detected dangerous SQL without WHERE")
注意:sqlparse 不做语义推断(比如不会告诉你 WHERE 1=1 是恒真),它只信语法结构。所以这类规则必须结合业务上下文补全——例如你的应用根本不该执行 DELETE,那就直接禁止所有 DELETE 类型语句。
立即学习“Python免费学习笔记(深入)”;
为什么不能只依赖sqlparse做SQL注入防护
因为绕过太容易:
- 大小写混淆:
SeLeCt不会被Keyword类型匹配(sqlparse默认不归一化关键字大小写)→ 必须先sqlparse.format(sql, keyword_case="upper") - 注释干扰:
SEL/*foo*/ECT会被拆成多个Token,SELECT关键字被切开 → 需要合并相邻Name和Comment后再判断 - 编码绕过:URL 编码、宽字节、十六进制字符串(如
0x73656c656374)根本不在sqlparse处理范围内 → 这类必须在解析前做解码和规范化 - 真正的防护应该优先用参数化查询,
sqlparse只适合做第二道防线或审计日志分析
最常被忽略的一点:解析失败的 SQL(比如语法错误)会被 sqlparse 返回空列表或异常,如果你没处理 sqlparse.exceptions.SQLParseError,拦截逻辑就直接跳过了。


















