本文目录导读:

- 案例1:使用
sqlite3(内置数据库) - 案例2:使用
MySQL(通过mysql-connector-python或PyMySQL) - 案例3:使用
PostgreSQL(通过psycopg2) - 案例4:使用 ORM 框架(如 SQLAlchemy、Django ORM)
- 特别注意事项
- 记住这三条铁律
防止SQL注入是Web开发中最基本的安全要求,在Python中,最核心的原则是:永远不要直接拼接SQL语句,始终使用参数化查询。
下面通过几个常见的Python数据库操作案例,演示如何安全地防止SQL注入。
案例1:使用 sqlite3(内置数据库)
这是最容易出错的场景,演示错误做法和正确做法。
❌ 错误做法(极易被注入)
import sqlite3
conn = sqlite3.connect('test.db')
cursor = conn.cursor()
# 假设用户输入: user_input = "' OR '1'='1"
user_input = input("请输入用户名:")
# 直接拼接 SQL —— 这是高危操作!
sql = f"SELECT * FROM users WHERE username = '{user_input}'"
cursor.execute(sql)
- 如果用户输入
' OR '1'='1,SQL 变成SELECT * FROM users WHERE username = '' OR '1'='1',会返回所有用户数据。
✅ 正确做法(使用参数化查询)
import sqlite3
conn = sqlite3.connect('test.db')
cursor = conn.cursor()
user_input = input("请输入用户名:")
# 使用 ? 占位符,将用户输入作为参数传递
sql = "SELECT * FROM users WHERE username = ?"
cursor.execute(sql, (user_input,)) # 注意:参数必须是元组或列表
- SQL语句和用户数据完全分离,数据库引擎会自动处理转义,
' OR '1'='1只会被当作一个普通字符串去匹配,不会破坏SQL结构。
案例2:使用 MySQL(通过 mysql-connector-python 或 PyMySQL)
原理与sqlite3相同,只是占位符不同(MySQL通常使用 %s)。
✅ 正确做法
import mysql.connector
conn = mysql.connector.connect(
host="localhost",
user="root",
password="yourpassword",
database="testdb"
)
cursor = conn.cursor()
user_input = input("请输入用户名:")
# MySQL 推荐使用 %s 作为占位符
sql = "SELECT * FROM users WHERE username = %s"
cursor.execute(sql, (user_input,))
results = cursor.fetchall()
案例3:使用 PostgreSQL(通过 psycopg2)
PostgreSQL使用 %s 占位符(与MySQL相同,但底层实现不同)。
✅ 正确做法
import psycopg2
conn = psycopg2.connect(
host="localhost",
dbname="testdb",
user="postgres",
password="yourpassword"
)
cursor = conn.cursor()
user_input = input("请输入用户名:")
sql = "SELECT * FROM users WHERE username = %s"
cursor.execute(sql, (user_input,))
案例4:使用 ORM 框架(如 SQLAlchemy、Django ORM)
ORM框架本身已经防止了SQL注入,但前提是你不要执行原生SQL。
✅ SQLAlchemy 正确用法
from sqlalchemy import create_engine, text
engine = create_engine('sqlite:///test.db')
with engine.connect() as conn:
user_input = input("请输入用户名:")
# 使用 bind parameters(绑定参数)
result = conn.execute(
text("SELECT * FROM users WHERE username = :username"),
{"username": user_input}
)
✅ Django ORM 正确用法
from django.contrib.auth.models import User
user_input = request.GET.get('username')
# Django ORM 会自动参数化,无需担心注入
users = User.objects.filter(username=user_input)
特别注意事项
LIKE 查询也需要参数化
即使使用LIKE,也要用参数,不要在SQL里拼接百分号。
# ✅ 正确
sql = "SELECT * FROM users WHERE username LIKE ?"
cursor.execute(sql, (f"%{user_input}%",))
IN 子句的参数化
对于IN查询,不同数据库有不同做法,但不要拼接。
# sqlite3 的一种替代写法(安全)
ids = [1, 2, 3]
placeholders = ','.join('?' * len(ids))
sql = f"SELECT * FROM users WHERE id IN ({placeholders})"
cursor.execute(sql, ids)
存储过程也可能有风险
如果存储过程内部拼接了SQL,同样存在注入风险。
记住这三条铁律
- 绝不拼接:永远不要用
f"SELECT ... {user_input}"或"SELECT " + user_input这种方式。 - 使用占位符:(sqlite3)、
%s(MySQL/PostgreSQL)、name(SQLAlchemy等)。 - 参数作为独立参数传入:
execute(sql, (user_input,)),而不是写进SQL字符串里。
如果你的代码已经用上了ORM(Django ORM、SQLAlchemy ORM),并且只使用ORM的方法(不写原生SQL),那么默认就是安全的,但一旦写了 raw() 或 text(),就需要手动参数化。
安全无小事,一个简单的就能堵住90%的注入漏洞。