flask项目sql注入源于绕过sqlalchemy安全机制;filter()和filter_by()通过表达式树生成参数化语句防注入,而text()、字符串拼接或format()会彻底失效防护,必须用占位符+bindparams()及白名单校验。

Flask项目里出现SQL注入,不是框架有问题,而是你绕过了SQLAlchemy的安全机制——只要用了 text()、字符串拼接或 format() 构造查询,ORM的防护就彻底失效。
filter() 和 filter_by() 为什么能防注入
它们不拼字符串,而是构建表达式树,最终生成带占位符的参数化语句,比如 SELECT * FROM users WHERE username = ?,再把值作为独立参数传给数据库驱动。
-
filter_by(username=request.args.get("u"))只支持硬编码字段名,天然防动态字段注入 -
filter(User.username == request.args.get("u"), User.status == 1)所有值都走参数绑定,包括None、日期、数字 - 哪怕用户输入的是
"admin' --",也会被当作纯字符串传入,不会破坏 SQL 结构
text() 是最常踩的坑
text() 不做任何安全检查,只负责把字符串原样发给数据库。一旦你写 text("WHERE name LIKE '%{}%'".format(q)) 或 text(f"ORDER BY {sort_field}"),就等于亲手拆掉防火墙。
- 正确做法:用占位符 +
bindparams(),例如text("WHERE name LIKE :pattern").bindparams(pattern=f"%{q}%") - 排序字段等无法参数化的部分,必须白名单校验:
if sort_field not in ["created_at", "username"]:raise ValueError -
text()应仅用于固定结构 SQL(如 CTE、窗口函数),所有变量必须通过bindparam()注入
db.session.execute() 的两种调用方式风险悬殊
新手常误以为用了 Flask-SQLAlchemy 就自动免疫,结果在 db.session.execute("UPDATE users SET status = 'active' WHERE id = {}".format(user_id)) 里中招——这行根本没走 ORM 安全逻辑,纯字符串拼接。
- 安全写法:
db.session.execute(text("UPDATE users SET status = :status WHERE id = :id"), {"status": "active", "id": user_id}) - 绝对禁止:
db.session.execute(sql_string.format(...))、db.session.execute(f"...{user_input}...")、db.session.execute("%s" % ...) - 模糊匹配别自己拼
LIKE,用ilike()或like()方法:filter(User.name.ilike(f"%{q}%"))
真正危险的从来不是“会不会写”,而是“以为写了就安全”。filter() 安全的前提是你没主动用字符串操作把它变成 unsafe;text() 从不承诺安全,它只承诺“你给啥,我就发啥”。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











