Python中防止SQL注入的核心方法是使用参数化查询,其原理是将SQL语句结构与数据内容严格分离,由数据库驱动对参数值进行安全转义或预编译绑定,避免用户输入被解析为SQL代码;字符串拼接(如f-string)必然导致注入,表名、字段名等结构部分须白名单校验。

只要用对 Pydantic 模型校验 + SQLAlchemy 参数化查询,PostgreSQL 的 SQL 注入在 FastAPI 里基本不可能发生。关键不是“防”,而是“根本没机会注入”。
Pydantic 模型必须严格定义字段类型和约束
Pydantic 是第一道防线,但它不会自动过滤恶意字符串——它只按你写的 BaseModel 规则做类型转换和基础校验。如果字段声明为 str 且没加 min_length、max_length 或正则 pattern,攻击者仍可能传入超长或特殊结构的 payload(比如嵌套 JSON 字符串触发下游逻辑漏洞)。
实操建议:
- 所有用户输入字段都显式声明约束,例如:
username: str = Field(min_length=2, max_length=50, pattern=r'^[a-zA-Z0-9_]+$') - 避免裸用
str,优先用语义化类型如EmailStr、HttpUrl(需pydantic[email]) - 对搜索类字段(如
q: str),用Query配合strip_whitespace=True和长度截断
SQLAlchemy 查询必须全程使用参数化,禁用 f-string / .format() 拼接
SQL 注入真正发生的点,永远是把用户数据直接拼进 SQL 字符串。SQLAlchemy 的 select()、filter()、where() 等方法默认就是参数化执行;但一旦你写 text("SELECT * FROM users WHERE name = '" + user_input + "'") 或 f"WHERE name = '{user_input}'",就彻底绕过所有防护。
常见错误场景:
- 用
text()执行原生 SQL 时,手动拼接变量 → 应改用命名参数:text("SELECT * FROM users WHERE status = :status"),再传{"status": "active"} - 动态构建列名或表名(如多租户切换)→ 这些不能参数化,必须白名单校验,例如:
if table_name not in ["users", "orders"]: raise HTTPException(400) - ORM 查询中误用字符串格式化:
session.query(User).filter(f"email = '{email}'")→ 必须写成.filter(User.email == email)
异步场景下 AsyncSession 同样遵循参数化原则
很多人以为异步 ORM(AsyncSession)会改变执行机制,其实不然:它只是把同步阻塞调用换成 await,底层 SQL 构建和参数绑定逻辑与同步版完全一致。只要你没手动拼 SQL,await session.execute(select(User).where(User.email == email)) 就是安全的。
注意两个易忽略点:
-
execute()返回的是Result对象,不是原始结果;取值要用.scalar_one_or_none()或.scalars().all(),别试图用fetchall()后再手动解析 - 批量插入时,
session.add_all()和session.execute(insert(User), [...])都支持参数化,但后者更适合大批量(性能更好,且明确走预编译) - 如果你用了
SQLModel(FastAPI 官方推荐组合),它的select()和update()方法也继承 SQLAlchemy 的参数化行为,无需额外处理
最危险的不是“不知道怎么防”,而是以为加了 Pydantic 就万事大吉,结果在某个 text() 调用或动态表名逻辑里悄悄开了后门。每次写原生 SQL 或反射操作前,先问一句:这个字符串里有没有用户可控内容?有,就必须进白名单或换 ORM 写法。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











