只要参数与SQL结构分离就安全,否则危险;filter()和filter_by()默认安全因构建表达式树并预编译,而f-string拼接、text()配错参数、动态表名列名等均会导致注入。

只要不用字符串拼接构造 SQL,就基本不会被注入;但“用了 ORM”不等于安全,“写了 text()”也不等于危险——关键在参数是否真正分离。
filter() 和 filter_by() 为什么默认安全
它们不生成 SQL 字符串,而是构建表达式树,最终交由数据库驱动以预编译语句(如 WHERE name = %s)执行,用户输入作为独立参数传入,和语句结构完全隔离。
-
filter(User.username == request.args.get("u"))安全:字段名硬编码,值走绑定参数 -
filter_by(status=request.args.get("status"))安全:仅支持等值匹配,列名固定,无法动态指定字段 -
filter(User.name.like(f"%{q}%"))❌ 危险:f""在 Python 层已拼出完整片段,like()不接管字符串插值 -
filter(text("name = '" + q + "'"))❌ 危险:绕过 ORM 解析,直接把拼好的字符串交给数据库
text() 必须配命名参数字典,不能靠 format 或 %
text() 是 SQLAlchemy 的“原生 SQL 接口”,它不解析 SQL 内容,只原样转发。安全与否,全看你怎么传参。
- 错误写法:
text("SELECT * FROM users WHERE id = {}".format(user_id))、text("WHERE name = '" + name + "'") - 正确写法:
text("SELECT * FROM users WHERE name = :name AND status = :status"),然后session.execute(stmt, {"name": name, "status": "active"}) - 占位符必须是
:name形式;?name或%s在text()中无效 - 第二个参数必须是字典;用元组易错位,用
**locals()可能键名不匹配
ORDER BY、表名、列名根本不能参数化
这不是 SQLAlchemy 的限制,是 SQL 协议硬约束:表名、列名、排序字段属于查询结构,在解析阶段就决定执行计划,数据库不允许把它们当参数传。
创建 Flask + Vue.js 全栈项目,采用单端口架构、uv 依赖管理、结构化日志和生产级前端设计。当用户要求以下操作时触发:创建新的 Flask+Vue 项目、搭建 Web 应用、构建全栈 Python+Vue 应用、初始化 Flask Web 项目、设置单端口 Flask 前后端项目。触发词:新建项目、创建项目、搭建项目、Flask项目、Vue项目。
-
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 编码可绕过,盲注也不依赖回显
db.session.execute() 的两种写法风险差异极大
新手常以为用了 Flask-SQLAlchemy 就万事大吉,结果在 db.session.execute("UPDATE users SET status = 'active' WHERE id = {}".format(user_id)) 里翻车——这行代码完全没走 ORM 参数机制,纯字符串拼接。
- 真正安全的写法只有两种:
– 用 ORM 对象操作:user.status = "active"; db.session.commit()
– 用带参数的text():db.session.execute(text("UPDATE users SET status = :status WHERE id = :id"), {"status": "active", "id": user_id}) -
RequestParser或类型转换(如int(request.args.get("id")))不能替代参数化:它只做类型强转,不防1 OR 1=1这类数字型注入
最易被忽略的点是:动态字段(如排序、模糊搜索字段、聚合函数包裹的列)永远无法参数化,白名单不是“加了更安全”,而是“不加就一定不安全”。哪怕你所有值都用了 :param,只要 ORDER BY 是自由输入,整条语句就形同裸奔。










