filter()和filter_by()默认安全,因其走参数化查询;但误用如拼接sql、混用text()、未校验动态字段名则引发注入风险,须严格白名单与bindparam。

filter() 和 filter_by() 本身是安全的,但容易被误用
filter() 和 filter_by() 默认走参数化查询,只要不手动拼字符串就安全。
常见误用场景包括:
- 把用户输入直接塞进
f"SELECT * FROM users WHERE name = '{request.args.get('q')}'"这类原生 SQL 字符串里 - 在
filter()中混用text(),比如filter(text("name = '" + q + "'"))—— 这等于绕过所有防护 - 用
getattr(User, request.args.get('field'))动态取字段再参与比较,字段名未白名单校验
正确做法是:
- 所有等值查询优先用
filter_by(username=request.args.get('username')) - 复杂条件用
filter(User.status == status, User.created_at > start_time),值直接传变量 - 字段名动态时必须硬编码白名单:
if sort_field not in ["id", "created_at"]: raise ValueError
text() 是高危区,不是“高级用法”,而是安全断点
text() 不做任何转义或参数绑定,它只负责把字符串原样发给数据库。
一旦你写 text("ORDER BY " + sort_field) 或 text(f"WHERE name LIKE '%{q}%'"),就等于敞开大门。
安全写法只有两条:
- 所有变量必须通过
bindparam()注入,例如text("WHERE name LIKE :pattern").bindparams(pattern=f"%{q}%") - 排序、分组、字段别名等无法参数化的部分,必须严格白名单控制,不能靠正则或黑名单过滤
db.session.execute() 的调用方式决定风险等级
华为云 Python SDK 查询工具。自动发现已安装 SDK 的服务包、Client类、API 方法、入参详情、类信息(__init__ 参数、属性、类型、描述),支持关键字搜索,快速定位目标 API 使用方法。
db.session.execute() 本身不自动参数化,它只是执行器。
危险写法(完全不安全):
-
db.session.execute("UPDATE users SET status = '{}'".format(status)) -
db.session.execute(f"DELETE FROM logs WHERE id IN ({','.join(ids)})")
安全写法(必须满足其一):
- 使用
text()+bindparams():db.session.execute(text("UPDATE users SET status = :status"), {"status": status}) - 使用原生驱动参数占位符(如 PostgreSQL 的
%s,MySQL 的%s或?):db.session.execute("INSERT INTO users (name) VALUES (%s)", (name,))
模糊匹配和批量操作要用 ORM 原生方法,别自己拼
很多人为了“灵活”手写 LIKE 或 IN 条件,结果引入漏洞。like()、ilike()、in_() 这些方法专为此设计,且全程参数化:
-
User.query.filter(User.name.like(f"%{q}%"))✅ 安全 -
User.query.filter(User.id.in_(request.json.get("ids", [])))✅ 安全 -
User.query.filter(text("name LIKE '%" + q + "%'"))❌ 危险
注意:空列表传给 in_() 会生成 IN () 报错,需提前判断;ilike() 在 PostgreSQL 中区分大小写,SQLite 中不生效,跨库时要留意。
真正的风险不在 ORM 方法本身,而在于你主动跳出它的安全边界——比如拼字符串、信任用户控制的字段名、忽略 bindparam 的必要性。这些地方没标准答案,只有白名单和参数化两条铁律。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










