直接拼接字符串危险因sql注入,如f"where name='{user_input}'"遇' or 1=1 --可绕过条件;应使用?或命名占位符参数化查询,动态字段须白名单校验。

为什么直接拼接字符串是危险的
因为 SQL 注入。比如用 f"WHERE name = '{user_input}'",用户输入 ' OR 1=1 -- 就能绕过条件查出全部数据。sqlite3 的参数化查询只接受占位符(? 或命名占位符),不接受表名、列名或操作符作为参数——这些必须由代码逻辑严格控制,不能来自外部输入。
如何正确写 WHERE 多条件 AND 查询
用 ? 占位符一一对应参数,按顺序传入元组;命名占位符更清晰但需用字典传参。WHERE 子句中每个条件独立写,不要试图把多个条件塞进一个占位符里。
- 推荐用命名占位符:SQL 可读性强,参数顺序无关紧要
- 参数必须是
tuple或dict,不能是 list(除非显式转成 tuple) - 空值处理要主动判断:
IS NULL不能用= ?传None来匹配(虽然None在= ?中会被当 NULL 比较,但语义不明确,建议显式写IS NULL或IS NOT NULL)
sql = "SELECT * FROM users WHERE age >= :min_age AND status = :status AND created_at > :since"
params = {"min_age": 18, "status": "active", "since": "2023-01-01"}
如何动态构建 WHERE 条件(避免 SQL 拼接)
核心原则:SQL 字符串静态写死,只留占位符;条件逻辑在 Python 层判断,再决定传什么参数。不要用字符串拼接生成 WHERE 子句。
- 收集有效条件字段和值到字典,跳过
None或空字符串 - 用
" AND ".join(keys)拼接 WHERE 部分(仅限键名,不拼值) - 确保最终 SQL 中每个
:key都有对应字典键,否则抛sqlite3.ProgrammingError - 如果所有条件都为空,应单独处理(比如返回空列表或加
WHERE 1=0)
conditions = []
params = {}
if min_age is not None:
conditions.append("age >= :min_age")
params["min_age"] = min_age
if status:
conditions.append("status = :status")
params["status"] = status
where_clause = " AND ".join(conditions)
sql = f"SELECT * FROM users WHERE {where_clause}" if conditions else "SELECT * FROM users WHERE 1=0"
IN 查询怎么安全传多个值
IN (?) 只能匹配单个值,不能直接传 list。必须动态生成等长的 ?, ?, ? 占位符,再展开参数。
- 用
",".join(["?"] * len(ids))构建占位符串 - 参数必须是 tuple,所以用
tuple(ids)转换 - 空列表要特殊处理(
IN (NULL)无效,应跳过该条件或设WHERE 1=0) - 注意 SQLite 有参数数量上限(默认 999),大数据量时需分批
ids = [1, 5, 12]
placeholders = ",".join(["?"] * len(ids))
sql = f"SELECT * FROM orders WHERE user_id IN ({placeholders})"
cursor.execute(sql, tuple(ids))
实际用的时候,最易错的是把动态字段名(比如排序字段、搜索列)也当成参数传进去——这是语法错误,sqlite3 不允许。这类内容必须白名单校验后硬编码进 SQL 字符串。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











