SQLAlchemy 2.0默认防注入,前提是全程使用参数化查询:select()、filter()等ORM/Core方法安全,text()必须配命名参数字典或bindparam,严禁f-string或字符串拼接;表名列名须白名单校验。

SQLAlchemy 2.0 的参数化查询默认就防注入,但你得用对方式
SQLAlchemy 2.0 默认所有 select()、update()、delete() 和 text()(带参数时)都走参数化绑定,只要不拼接字符串,基本不会中招。关键不是“怎么开启防御”,而是“怎么避免亲手绕过它”。
常见错误是把用户输入直接塞进 f"WHERE name = '{user_input}'" 或 "WHERE name = '%s'" % user_input —— 这种写法哪怕用了 SQLAlchemy,也等于裸奔。
- 永远用
bindparam()或命名参数(如:name)配合text() - ORM 查询一律用属性比较:
User.name == user_input,而不是text("name = '" + user_input + "'") - 原生 SQL 中的表名、列名不能参数化,需白名单校验或
sqlalchemy.sql.quoted_name()转义
text() 中传参必须用 bindparam 或字典,不能用 format/f-string
text() 是最容易翻车的地方:它本身不自动绑定,全靠你手动传参。错用字符串格式化会立刻引入漏洞。
✅ 正确写法:
立即学习“Python免费学习笔记(深入)”;
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
from sqlalchemy import text
stmt = text("SELECT * FROM users WHERE role = :role AND status = :status")
result = conn.execute(stmt, {"role": "admin", "status": "active"})❌ 错误写法(SQL 注入高危):
role = request.args.get("role")
# 千万别这么写:
stmt = text(f"SELECT * FROM users WHERE role = '{role}'") # 拼接!
stmt = text("SELECT * FROM users WHERE role = '%s'" % role) # 格式化!-
text()的占位符必须是:name形式,且只接受字典或bindparam()参数 - 不要试图用
str.replace()或正则“过滤单引号”——规则永远追不上攻击变体 - 如果必须动态列名(如排序字段),先查白名单:
if order_by not in ["created_at", "score"]:再拼接
ORM 查询中 .filter() 和 .where() 都安全,但 raw string 条件不行
SQLAlchemy 2.0 的 ORM 查询链(select(User).where(...))和 Core 查询(select().where(...))在使用表达式对象时,全程走参数化。但一旦退化到字符串条件,就失去保护。
- ✅ 安全:
select(User).where(User.email == email_input) - ✅ 安全:
select(User).where(text("email = :email")).params(email=email_input) - ❌ 危险:
select(User).where(f"email = '{email_input}'")(类型错误,但有人会转成字符串再 exec) - ❌ 危险:
select(User).where("email = '" + email_input + "'")(字符串拼接)
注意:.where() 接字符串时,SQLAlchemy 不报错,但会原样透传给数据库驱动 —— 这时候是否防注入,取决于驱动是否二次处理(通常不会)。
Python 3.12 没新机制,但要注意 asyncio + async_session 的参数传递一致性
Python 3.12 本身不改变 SQLAlchemy 的参数绑定逻辑,但 async 使用场景下,容易因协程混用导致参数丢失或错位。
- 异步执行必须用
await async_session.execute(stmt, params_dict),不能用session.execute() - 避免在
async def里混用同步text()执行;若用run_sync(),仍需保证参数字典传入,而非拼接 - 检查日志:启用
echo=True时,SQLAlchemy 2.0 会明确打印绑定值,如... WHERE role = %(role)s,看到%(...)s就说明参数化生效了
真正难的从来不是“怎么防”,而是当业务要支持模糊搜索、多字段排序、动态 where 条件时,如何守住参数化这条线 —— 白名单、表达式构造、提前 validate,比事后过滤可靠得多。

















