Python案例怎么防止SQL注入?

wen python案例 8

本文目录导读:

Python案例怎么防止SQL注入?

  1. 案例1:使用 sqlite3(内置数据库)
  2. 案例2:使用 MySQL(通过 mysql-connector-pythonPyMySQL
  3. 案例3:使用 PostgreSQL(通过 psycopg2
  4. 案例4:使用 ORM 框架(如 SQLAlchemy、Django ORM)
  5. 特别注意事项
  6. 记住这三条铁律

防止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-pythonPyMySQL

原理与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,同样存在注入风险。


记住这三条铁律

  1. 绝不拼接:永远不要用 f"SELECT ... {user_input}""SELECT " + user_input 这种方式。
  2. 使用占位符:(sqlite3)、%s(MySQL/PostgreSQL)、name(SQLAlchemy等)。
  3. 参数作为独立参数传入execute(sql, (user_input,)),而不是写进SQL字符串里。

如果你的代码已经用上了ORM(Django ORM、SQLAlchemy ORM),并且只使用ORM的方法(不写原生SQL),那么默认就是安全的,但一旦写了 raw()text(),就需要手动参数化。

安全无小事,一个简单的就能堵住90%的注入漏洞。

抱歉,评论功能暂时关闭!