在Django项目里,全局请求参数清洗这件事,真正落地的核心从来不是写个中间件就完事了,而是你如何设计一个既不过度影响性能、又能覆盖绝大多数注入攻击面的清洗链路。很多人上来就写一个中间件,把request.GET和request.POST遍历一遍,用正则替换掉危险字符,这种做法在低并发场景看着没问题,一旦请求量上来,每次请求都全量遍历和正则匹配的开销会直接拉高响应延迟。更合理的做法是分层清洗:先做类型约束,再做模式过滤,最后才上规则引擎。
请求参数注入的入口比你想象的多别只盯着GET和POST参数。Django的request对象里,能携带用户输入的地方至少有五处:request.GET、request.POST、request.COOKIES、request.META里的HTTP头,以及URL路径中的命名参数。攻击者完全可以在User-Agent、Referer甚至自定义HTTP头里塞入SQL片段或XSS向量,如果你的中间件只处理查询参数,那等于把大门敞开了一半。全局清洗必须覆盖request.META中以HTTP_开头的所有键,同时处理Cookie值,因为Session ID以外的自定义Cookie经常被业务代码直接读进数据库查询。
先做类型约束,再谈清洗很多注入漏洞的根源不是没过滤,而是参数类型被意外转换。比如一个本应是整数的id参数,业务代码里直接用字符串拼接进SQL,这时候即便你过滤了单引号,攻击者仍然可以通过数值型注入绕过。在中间件层面,如果你能拿到参数的预期类型定义,就先把参数强制转换:整数参数用int()转换失败直接拦截,浮点数同理,布尔值只接受明确的true/false或1/0。对于没有类型定义的通用清洗,至少要做一层判断——如果参数值看起来完全由数字组成,就不要走字符串过滤逻辑,直接放行,减少不必要的正则开销。
清洗规则要分层,别一把梭把SQL注入防护和XSS防护混在同一套正则里是常见的错误。SQL注入关注的是单引号、双引号、反斜杠、注释符、UNION关键字等;XSS关注的是尖括号、script标签、事件处理器、javascript伪协议。这两类攻击的特征完全不同,混在一起不仅正则效率低,还容易产生误杀。建议在中间件里维护两个独立的规则链:SQL注入规则链和XSS规则链,每个链内部按优先级排序,命中即停止。对于文件上传场景,还要单独处理文件名参数,防止路径穿越和命令注入。
中间件的实现骨架下面给出一个生产可用的中间件实现思路,核心是请求入口统一清洗、响应出口不做处理,避免二次编码问题。中间件在process_request阶段拦截,对request.GET、request.POST、request.COOKIES、request.META进行遍历清洗。注意request.GET是QueryDict,它是不可变的,需要先拷贝再赋值。
import re
import copy
from django.utils.deprecation import MiddlewareMixin
from django.http import HttpResponseForbidden
class GlobalParamCleanMiddleware(MiddlewareMixin):
# SQL注入特征规则,按危险程度排序
SQL_RULES = [
(re.compile(r"(?i)(\bUNION\b.*\bSELECT\b)"), "SQL_UNION"),
(re.compile(r"(?i)(\bSELECT\b.*\bFROM\b)"), "SQL_SELECT"),
(re.compile(r"(\bOR\b\s+\d+\s*=\s*\d+)"), "SQL_OR_INJECT"),
(re.compile(r"(--[^\n]*$)"), "SQL_COMMENT"),
(re.compile(r"(;\s*(DROP|DELETE|UPDATE|INSERT)\b)"), "SQL_DANGER"),
(re.compile(r"('|\")\s*(OR|AND)\s*('|\")"), "SQL_QUOTE"),
]
# XSS特征规则
XSS_RULES = [
(re.compile(r"(?i)(