sql注入主因是用户输入参与字符串拼接,正确做法是严格使用参数化查询并校验表名、字段名等结构化内容。

cursor.execute 传参错用列表或字符串拼接,是绝大多数 SQL 注入漏洞的直接成因。只要用户输入进了 format()、% 或 + 拼出的 SQL 字符串,就已失守。
psycopg2 / pymysql / sqlite3 的占位符语法不能混用
不同驱动对占位符的支持差异极大,硬套一种写法在换库时必然报错或失效:
-
sqlite3只认?(位置)或:name(命名),传%s会原样当字面量处理 -
psycopg2只认%s,且第二参数必须是tuple或dict;传[user_id]会抛TypeError: not all arguments converted -
pymysql默认支持%s,但不支持%()s命名格式;若要用命名参数,得显式开启named=True
正确示例(psycopg2):cursor.execute("SELECT * FROM user WHERE id = %s AND status = %s", (user_id, "active"))
错误示例:cursor.execute("SELECT * FROM user WHERE id = %s", [user_id]) —— 列表不被接受cursor.execute(f"SELECT * FROM user WHERE name = '{name}'") —— 字符串拼接,高危
Django ORM 的三个“安全假象”场景
Django 查询集本身安全,但以下三处常被误认为“用了 ORM 就万事大吉”:
-
extra()和raw():它们绕过 ORM 层,直接执行 SQL;参数必须走params=关键字传入,不能拼进字符串里 -
filter(**{field_name: value}):若field_name来自用户请求(如搜索字段),攻击者可传__dict__或is_staff等字段名,触发非预期查询甚至数据泄露 -
Q对象构造时用了eval()或format():比如Q(eval(request.GET.get("q_expr"))),等于把控制权交给了攻击者
安全做法是白名单校验:
allowed_fields = {"id", "name", "email", "created_at"}<br>field = request.GET.get("sort_by", "id")<br>if field not in allowed_fields:<br> field = "id"<br>queryset = User.objects.order_by(field)
SQLAlchemy 的 text() 不是免检通行证
text() 常被当作“ORM 里写原生 SQL 的安全出口”,但它只在满足两个条件时才真正防注入:
-
text()内部 SQL 必须是纯静态字符串,不含任何用户输入 - 所有动态值必须通过第二参数(
dict或tuple)传入,且占位符只能是:name(命名)或?(位置)
危险写法:db.session.execute(text(f"SELECT * FROM user WHERE name = '{name}'"))db.session.execute(text("SELECT * FROM user WHERE " + condition))
这两者中,变量已提前插进 SQL 字符串,text() 完全失效。
真正起作用的写法:db.session.execute(text("SELECT * FROM user WHERE name = :name"), {"name": name})
表名、字段名、ORDER BY 子句无法参数化
数据库驱动的参数化机制只保护“值”,不保护“结构”。这意味着 table_name、column_name、ORDER BY xxx 这类内容,永远不能靠 ? 或 %s 保护。
- 必须用白名单硬编码或映射校验,例如:
allowed_tables = {"users", "orders", "products"} - 字段排序方向(ASC/DESC)也不能拼接,应转为布尔判断后映射:
order_dir = "DESC" if desc else "ASC",再拼进 SQL(前提是字段名已白名单校验) - 避免用用户输入决定 JOIN 表或子查询结构——这类逻辑应由业务层拆解,而非 SQL 动态生成
最易被忽略的一点:哪怕 ORM 查询看似安全,只要涉及动态表/字段/排序,就回到了“代码即配置”的风险区,此时白名单不是可选项,是唯一可行路径。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











