psycopg3 本身不防注入,安全取决于写法:必须用 sql.Identifier 处理标识符(列名、表名、别名),用 %(param)s 参数化传入值(如 JSON 键名),二者不可混用或字符串拼接,否则导致 SQL 注入。

psycopg3 本身不防注入,防注入靠你写法是否合规——只要用对 sql.Identifier 和参数化占位符,就基本安全;一旦混用或拼接字符串,立刻破防。
区分标识符和值:sql.Identifier 只用于列名/表名/别名
PostgreSQL 的 JSON 提取语法 feature -> 'key' 中,feature 是列名(标识符),'key' 是 JSON 键的字面量(值),二者必须分开处理:
-
feature这类字段名、表名、别名,必须用sql.Identifier("feature")包裹,不能直接插进 SQL 字符串 -
'key'这类运行时传入的 JSON 键名、用户 ID、时间范围等,必须走参数化,即%(json_key)s占位符 + 字典传参 - 错误示例:
f"feature -> '{user_input}'"或sql.SQL(f"feature -> '{user_input}'")—— 这是典型注入入口 - 正确组合示例:
sql.SQL("{col} -> %(json_key)s AS ").join([sql.Identifier("feature"), sql.Identifier("temperature")])
多语句删除必须拆开执行,不能用分号拼接
psycopg3 明确禁止单次 execute() 执行多个命令,否则报错:psycopg.errors.SyntaxError: cannot insert multiple commands into a prepared statement:
- 错误做法:把两个
DELETE用分号连成一句,再传给cursor.execute() - 正确做法:拆成独立语句,循环调用
cursor.execute(text(stmt), params),并包裹在事务中 - 务必显式
conn.commit()或conn.rollback(),不能依赖自动提交 - 更优方案:优先设外键
ON DELETE CASCADE,让数据库自己级联删,省去应用层多语句协调
IN 子句参数化必须用数组,不能展开为多个 %s
当要查 WHERE id IN (1,2,3) 时,不能手动拼 %s, %s, %s 占位符,否则无法动态适配长度:
立即学习“Python免费学习笔记(深入)”;
- 错误:写死三个
%s,然后传(id1, id2, id3)—— 列表长度一变就崩 - 正确:用单个
%(ids)s占位符,传入 Python 列表(如['id100', 'id200']),PostgreSQL 原生支持数组参数用于IN - 注意:空列表会出错,需提前校验
if not identifiers:并跳过查询或抛业务异常 - 若用 SQLAlchemy Core,推荐直接用
in_()表达式,它底层自动处理数组绑定
最容易被忽略的是别名校验——哪怕用了 sql.Identifier(alias),如果 alias 来自前端输入,仍需确认它只含字母、数字、下划线且不以数字开头,否则 Identifier 会抛异常或生成非法 SQL。


















