SQLAlchemy的text()配合:param参数映射安全,因数据库驱动将参数值作为纯数据传入,不参与SQL语法解析,避免结构篡改;错误拼接f-string则直接导致注入。

SQLAlchemy的text() + :param参数映射为什么比字符串拼接安全
因为数据库驱动会把:param后的值当作纯数据传入,不参与SQL语法解析。哪怕用户输入的是"admin' --"或"1; DROP TABLE users; --",最终也只被当做一个字符串值处理,不会改变查询结构。
常见错误是误用f-string或%格式化拼接text()内容:
- ❌ 错误写法:
text(f"SELECT * FROM users WHERE name = '{name}'") - ✅ 正确写法:
text("SELECT * FROM users WHERE name = :name"),再通过.execute(..., {"name": name})传参
什么时候必须用text()而不是ORM原生查询
当你需要动态控制SQL结构本身——比如用户指定排序字段、分组维度、或联合多个不确定的表——ORM的filter_by()或order_by()无法直接支持这些场景。
但注意:text()只安全于「值」,不安全于「标识符」(表名、列名、函数名)。所以:
立即学习“Python免费学习笔记(深入)”;
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 允许用户决定
WHERE status = :status中的:status值 → 安全 - 允许用户决定
ORDER BY :sort_col中的:sort_col→ 不安全,会被当成字符串字面量,不是列名 - 若真需动态列名,必须白名单校验:
if sort_col not in ["created_at", "score", "name"]:→ 抛异常
session.execute()和connection.execute()在参数绑定上的关键区别
两者都支持text() + 命名参数,但生命周期和事务控制不同:
-
session.execute()自动绑定到当前Session事务,适合与ORM对象混合操作(如查完再add()) -
connection.execute()绕过Session,更底层,适合批量原始SQL或跨事务边界操作 - 无论哪种,参数都必须用字典(命名)或元组(位置),不能混用;例如
execute(stmt, {"id": 123})合法,execute(stmt, [123])在位置占位符%s下才合法
别忽略驱动差异:PostgreSQL用%s,SQLite用?,但text()统一用:name,由SQLAlchemy翻译,这是推荐做法。
爬虫入库时最易忽略的“非值”注入点:表名、字段名、INSERT的列列表
爬虫常按站点/频道分表存储,比如news_sina、news_ifeng,如果用用户输入或URL路径片段直接构造表名,就等于开了后门。
解决方式不是“加个转义”,而是彻底剥离控制权:
- 用配置映射代替自由输入:
source_map = {"sina": "news_sina", "ifeng": "news_ifeng"},再取source_map.get(user_source) - INSERT语句中列名列表(如
INSERT INTO t (title, url, pub_time))也不能来自爬虫字段名,必须硬编码或从预定义schema读取 - 哪怕字段值是JSON解析来的,也要检查键名是否在白名单内:
allowed_keys = {"title", "url", "content"},丢弃__table_name__这类可疑键
真正难防的从来不是' OR 1=1,而是开发者自己把__import__或eval塞进SQL生成逻辑里——这种不属于SQL注入,但危害更大。

















