一、SQL 注入(SQL Injection)
1. 漏洞原理
SQL 注入是由于 用户输入未经过滤直接拼接到 SQL 语句中,导致攻击者可以构造恶意 SQL 代码,窃取、篡改或删除数据库数据。FastAPI 中若使用原生 SQL 语句且未做参数化,极易触发该漏洞。
2. 攻击场景(FastAPI 漏洞代码)
假设 FastAPI 接口通过用户 ID 查询用户信息,使用原生 SQL 且直接拼接用户输入:
from fastapi import FastAPI
import sqlite3
app = FastAPI()
# 漏洞接口:直接拼接用户输入到SQL语句
@app.get("/user/{user_id}")
def get_user(user_id: str):
# 连接数据库(示例用SQLite)
conn = sqlite3.connect("test.db")
cursor = conn.cursor()
# 危险!未过滤用户输入,直接拼接SQL
sql = f"SELECT * FROM users WHERE id = {user_id};"
cursor.execute(sql) # 攻击者可构造恶意user_id注入SQL
result = cursor.fetchone()
conn.close()
return {"user": result}
攻击步骤:
- 攻击者访问正常接口:
/user/1,返回用户 ID=1 的信息。 - 构造恶意输入,窃取所有用户数据:访问
/user/1 OR 1=1,此时 SQL 语句变为:SELECT * FROM users WHERE id = 1 OR 1=1;由于1=1恒成立,会返回数据库中所有用户的信息。 - 进阶攻击:删除数据(若权限足够):访问
/user/1; DROP TABLE users;,SQL 语句变为:SELECT * FROM users WHERE id = 1; DROP TABLE users;导致users表被删除(破坏性攻击)。
3. 防护方案(修复代码 + 最佳实践)
核心原则:避免原生 SQL 拼接,使用参数化查询或 ORM 框架。
修复方案 1:使用参数化查询(原生 SQL 安全写法)
@app.get("/user/{user_id}")
def get_user(user_id: str):
conn = sqlite3.connect("test.db")
cursor = conn.cursor()
# 安全!使用参数化查询(?为占位符,避免拼接)
sql = "SELECT * FROM users WHERE id = ?;"
cursor.execute(sql, (user_id,)) # 第二个参数为元组,传入用户输入
result = cursor.fetchone()
conn.close()
return {"user": result}
修复方案 2:使用 ORM 框架(推荐,如 SQLAlchemy)
ORM 框架会自动处理参数化,彻底杜绝 SQL 注入:
python
运行
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 1. 初始化数据库连接(SQLite)
engine = create_engine("sqlite:///test.db")
Base = declarative_base()
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# 2. 定义User模型
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String, unique=True, index=True)
email = Column(String, unique=True, index=True)
# 3. 安全接口:使用ORM查询
@app.get("/user/{user_id}")
def get_user(user_id: int): # 注意:这里用int类型,进一步限制输入
db = SessionLocal()
# ORM自动生成参数化SQL,无注入风险
user = db.query(User).filter(User.id == user_id).first()
db.close()
return {"user": user}
额外防护:
- 输入校验:用 FastAPI 的 Pydantic 模型限制输入类型(如
user_id: int,拒绝字符串输入)。 - 最小权限原则:数据库账号仅授予必要权限(如查询权限,无删除 / 修改权限)。