最有效方法是用grep -r定位execute()和query()调用。SQL注入风险集中在原生SQL执行点,需重点检查参数化是否正确、动态标识符是否白名单校验、参数类型是否匹配驱动要求。

grep -r 找出所有 execute() 和 query() 调用
直接定位风险入口最有效。SQL注入几乎都发生在原生 SQL 执行点,而不是 ORM 方法调用处。
- 运行
grep -r "execute(" . --include="*.py"和grep -r ".query(" . --include="*.py",重点关注cursor.execute()、connection.execute()、db.session.execute()等 - 特别留意带
f"、.format(、%或+拼接的 SQL 字符串——只要用户输入进了字符串,就已失守 - 忽略
session.query(User).filter(...)这类纯 ORM 链式调用;它们本身安全,但要注意后面是否跟了.extra()或.raw()
检查参数传入方式是否匹配驱动要求
参数化不是写了占位符就万事大吉,传参格式错也会导致降级为字符串拼接。
-
sqlite3只认?或:name,传%s会原样当字面量处理,不转义 -
psycopg2只接受%s,且第二参数必须是tuple(如(user_id,))或dict;传[user_id]报TypeError: not all arguments converted -
pymysql默认只支持%s,不支持%()s命名格式;若要用命名参数,得显式设named=True - 常见错误:
cursor.execute("WHERE id = %s", [user_id])—— 列表在 psycopg2 中不被识别为参数,等价于拼接
筛查无法参数化的动态结构部分
ORDER BY、表名、字段名、LIMIT 数值这些不能用 ? 或 %s 替代,是高频盲区。
- 搜索
ORDER BY、GROUP BY、FROM、INSERT INTO后紧跟变量的代码行 - 检查是否对
request.args.get("sort")或request.json.get("table")做了白名单校验,例如:if sort_field not in ["name", "email", "created_at"]: - 分页参数
limit/offset是否强制转int()并限范围?错误写法:"LIMIT " + str(limit);正确应是"LIMIT %s"配合整型转换+范围截断 - Django 的
filter(**{field_name: value})或 Flask-SQLAlchemy 的text(f"SELECT ... {condition}")是典型陷阱,必须人工确认字段/条件来源是否可信
验证 raw()/extra()/text() 是否真正参数化
这些接口常被误认为“ORM 里写 SQL 很安全”,其实只要字符串里有变量插值,就等于开门揖盗。
立即学习“Python免费学习笔记(深入)”;
- Django
extra()或raw():必须用params=关键字传参,绝不能写成extra(where=["name = '{}'".format(name)]) - SQLAlchemy
text():内部 SQL 必须是静态字符串,所有动态值只能通过第二参数(dict或tuple)传入,例如:text("SELECT * FROM user WHERE status = :s"), {"s": status} - 危险模式:
text(f"SELECT * FROM {table_name} WHERE ...")或text("..." + condition)—— 占位符根本没机会生效 - 哪怕只有一处漏掉,整个查询链就失效
实际排查时,最容易被忽略的是动态标识符校验和参数类型匹配——前者常被当成“小问题”跳过,后者在换数据库驱动时才暴露。别信“看起来像参数化”,要看执行时到底有没有进预编译流程。


















