Python防止SQL注入_参数化查询原理

4次阅读

python 防止 sql 注入的核心方法是使用参数化查询,其原理在于将 sql 语句结构与数据内容严格分离——数据库驱动(如 sqlite3、psycopg2 或 pymysql)会把参数值作为独立的数据单元传递给数据库服务器,由数据库引擎在执行前进行安全转义或直接绑定到预编译语句中,从而避免用户输入被当作 sql 代码解析执行。

python 防止 sql 注入的核心方法是使用参数化查询,其原理在于将 sql 语句结构与数据内容严格分离——数据库驱动(如 sqlite3psycopg2pymysql)会把参数值作为独立的数据单元传递给数据库服务器,由数据库引擎在执行前进行安全转义或直接绑定到预编译语句中,从而避免用户输入被当作 sql 代码解析执行。

参数化查询如何隔离 SQL 逻辑与数据

数据库在接收到参数化语句(如 "SELECT * FROM users WHERE name = ?")后,会先对 SQL 语法进行编译,生成执行计划;参数值不参与编译过程,仅在执行阶段以二进制或类型化方式传入。这意味着即使参数含单引号、分号或 UNION SELECT,也不会改变原有 SQL 结构。

  • 字符串拼接(危险):query = "SELECT * FROM users WHERE name = '" + user_input + "'" → 输入 O'Reilly 会导致语法错误,输入 admin'-- 可能绕过认证
  • 参数化写法(安全):cursor.execute("SELECT * FROM users WHERE name = ?", (user_input,)) → 数据库把 O'Reilly 当作纯文本值处理,自动加引号并转义

不同数据库驱动的参数占位符规范

各驱动对参数占位符的语法要求不同,必须匹配使用,否则参数不会被识别为绑定变量,仍可能触发注入。

  • sqlite3:用 ?(问号)或命名占位符 :name,例如 cursor.execute("INSERT INTO t VALUES (?, ?)", (a, b))
  • psycopg2(PostgreSQL):只支持 %s(注意不是 Python 字符串格式化),例如 cursor.execute("SELECT * FROM t WHERE id = %s", (123,))
  • pymysql / mysql-connector-python:用 %s,不可用 ?:name,否则报错或降级为字符串拼接
  • 切勿混用:cursor.execute(f"WHERE name = %s AND age > {user_age}") 中的 {user_age} 是 Python f-string 拼接,已破坏参数化机制

哪些操作不能靠参数化保护

参数化仅适用于 ** 数据值 **(WHERE 条件、INSERT 字段值、ORDER BY 中的值等),无法用于动态 SQL 结构本身。

  • 表名、列名、排序字段(如 ORDER BY ?)不被支持,需通过白名单校验或硬编码控制
  • 查询限制数(LIMIT ?)在部分驱动中受限,SQLite 支持,MySQL 需用 int() 转换后拼接到 SQL 中(前提是可信来源)
  • 动态 IN 列表(如 WHERE id IN (?, ?, ?))需按实际参数个数构造占位符,不能传入元组直接替换

额外建议:组合防御更可靠

参数化查询是防 SQL 注入的基石,但配合其他措施可进一步降低风险。

  • 最小权限原则:数据库连接账号仅授予必要表的 CRUD 权限,禁用 DROPEXECUTE 等高危权限
  • 输入校验前置:对 ID 类字段用 int() 强转,邮箱用正则初筛,不依赖 SQL 层过滤
  • 开启数据库日志与 WAF 规则:捕获异常 SQL 模式(如含 UNION SELECT@@version 的请求)
  • 避免自定义 ORM 拼接:如用 f"SELECT {fields} FROM {table}" 构造查询,应改用框架提供的查询接口(Django ORM、SQLModel 等默认参数化)
text=ZqhQzanResources