filter()和filter_by()安全因构造表达式树生成预编译语句;f""插值、text()字符串拼接、动态列名/排序字段未白名单校验均导致sql注入。

SQLAlchemy 的 filter() 和 filter_by() 本身安全,但只要出现字符串拼接、f"" 插值或未校验的动态字段,防护就立刻失效。
filter() 和 filter_by() 怎么用才真安全
它们安全不是因为“用了 ORM”,而是因为底层不拼字符串,而是构造表达式树,最终生成带占位符的预编译语句(如 WHERE name = %s),用户输入作为独立参数传给驱动。
-
filter_by(username=request.args.get("u"))安全:字段名硬编码,只支持等值匹配,天然防动态列名 -
filter(User.status == request.args.get("status"), User.created_at > now)安全:所有值(包括None、datetime、字符串)都走绑定参数 -
filter(User.name.like(f"%{q}%"))❌ 危险:f""在 Python 层已拼出完整 SQL 片段,like()不接管参数化 -
filter(text("name = '" + q + "'"))❌ 危险:绕过 ORM,直接把拼好的字符串交给数据库
text() 原生 SQL 必须配命名参数字典
text() 是安全边界断裂点——SQLAlchemy 不再解析语法,只原样转发。安全与否,全看你怎么传参。
Python 3.14.2是Python编程语言在2025年12月5日发布的稳定版本,属于3.14系列的第二个维护更新。该版本包含了18项修复,重点解决了多进程、数据类及正则表达式等模块的回归问题,并修复了CVE-2025-12084等安全漏洞。此版本标志着自由线程模式(移除GIL)正式获得官方支持,是Python发展的重要里程碑。
- 错误写法:
text(f"SELECT * FROM users WHERE id = {user_id}")、text("WHERE name = '" + name + "'"),攻击者输admin' OR '1'='1就查全表 - 正确写法:
text("SELECT * FROM users WHERE name = :name AND status = :status"),然后session.execute(stmt, {"name": name, "status": "active"}) - 占位符必须是
:name形式,?name或%s在text()中无效 - 第二个参数必须是字典,不能是元组(易错位)或
**locals()(键名可能不匹配)
ORDER BY、表名、列名这些根本不能参数化
SQL 协议规定:表名、列名、ORDER BY 字段属于查询结构,在解析阶段就决定执行计划,数据库根本不允许把它们当参数传——这不是 SQLAlchemy 的限制,是协议硬约束。
- 翻车典型:
order_by(request.args.get("sort")),前端传id ASC; DROP TABLE users--就执行注入 - 白名单校验示例:
if sort_field not in ["id", "created_at", "username"]: raise ValueError("Invalid sort field") - 动态列查询:
getattr(User, field)前必须检查field in User.__table__.columns.keys() - 别信
replace("'", "''")或正则删--,十六进制/Unicode 编码可绕过,盲注也不依赖回显
RequestParser 或类型转换不能替代参数化
RequestParser 只负责提取、转类型、基础校验(比如把 id 强转为 int),它不碰数据库层,更不阻止你后续拿这个 int 去拼 SQL。
- 即使
id被 parser 强转为int,仍要走filter(User.id == user_id),不能写"WHERE id = " + str(user_id) - 攻击者可绕过 parser:删掉
Content-Type、发 raw body、改请求头,让校验形同虚设 - 真正防护只发生在 DB 操作层:参数化查询或 ORM 安全接口,和上层校验是两件事
最易被忽略的点是模糊搜索和排序字段——它们看起来像“值”,实则是 SQL 结构的一部分;like() 方法本身不参数化字符串内容,text() 里任何变量插值都是高危操作。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










