安全写法是全程使用ORM接口(如filter_by、get)或text()配字典参数;拼接SQL、过滤单引号、类型转换不彻底均无效,动态字段名须白名单校验。

直接用 User.query.filter_by() 就安全,别手写 SQL
Flask 自身不带 ORM,但绝大多数项目都会用 Flask-SQLAlchemy。只要全程走模型(Model)的查询接口,比如 filter_by()、get()、filter()(注意是带参数的写法),SQLAlchemy 会自动做参数化绑定,根本不会拼接字符串。
常见错误是看到 URL 参数就手动拼 SQL:
username = request.args.get('u')
# ❌ 危险!
db.session.execute(f"SELECT * FROM user WHERE username = '{username}'")
正确做法是交给 ORM 处理:
-
User.query.filter_by(username=username).first()—— 简单等值查,最常用 -
User.query.filter(User.username == username).first()—— 支持更复杂条件,如like、in_ - 所有这类调用底层都生成预编译语句,用户输入进的是绑定参数,不是 SQL 文本
text() + 字典参数才是原生 SQL 的安全写法
真有必须写原生 SQL 的场景(比如复杂聚合、跨表 join 逻辑 ORM 不好表达),不能用 f-string 或 % 拼接,必须用 sqlalchemy.text() 配合字典传参。
错误示范:
# ❌ 即使加了引号也危险:攻击者可闭合引号后注入
sql = f"SELECT * FROM user WHERE name = '{name}'"
db.session.execute(sql)
正确写法:
from sqlalchemy import text
sql = text("SELECT * FROM user WHERE name = :name AND status = :status")
result = db.session.execute(sql, {"name": name, "status": "active"}).fetchall()
关键点:
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 占位符用
:name这种命名风格,不是?或%s - 第二个参数必须是字典,键名要和占位符一致
- 不要用元组传参(
(name, status)),容易错位;也不要用**locals()这类模糊方式
别信“过滤单引号”这种土办法
有人试图用 replace("'", "''") 或正则删掉 ;、-- 来防注入,这完全无效。
原因很直接:
- 攻击者可以用十六进制编码、Unicode 编码绕过字符过滤
- 盲注不需要回显,靠布尔响应或延时就能读库,过滤字符毫无意义
- ORM 和参数化查询之所以可靠,是因为数据库驱动在协议层就把值和结构分开传输,不是靠“删掉坏字符”
真正该做的是:对非字符串字段(如 ID)直接转类型 —— int(request.args.get('id', 0)),类型不符就抛错,比任何字符串清洗都硬核。
开发期最容易忽略的两个坑
即使用了 ORM,仍有两处高频翻车点:
-
动态表名/字段名:ORM 不支持运行时拼表名。比如
getattr(User, request.args['sort_by'])如果sort_by是用户可控的,就可能被设成__class__.__mro__[1].__subclasses__()这类链式属性(虽然不直接 SQL 注入,但会引发 SSTI)。表名/字段名必须来自白名单字典 -
raw query +
db.engine.execute():老版本 Flask-SQLAlchemy 中这个方法不强制参数化,传字符串就执行。新版已弃用,务必改用db.session.execute(text(...), {...})
最后提醒一句:参数化查询只保“查询语句结构不变”,它不管你的业务逻辑是否合理。比如用用户输入控制 ORDER BY 字段,哪怕参数化了,也可能导致数据排序泄露或 DoS,这种得靠业务校验兜底。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










