django 的 redirect() 本身不安全,因为它只发 302 响应而不校验 url,需配合 is_safe_url(url, allowed_hosts={request.get_host()}) 或白名单机制防御开放重定向。

直接把 request.GET.get('next') 或 request.GET.get('url') 塞进 HttpResponseRedirect,就是开放重定向漏洞的典型写法——它会让攻击者构造 /login?next=https://evil.com,用户一登录就被劫持到钓鱼站。
为什么 Django 的 redirect() 本身不安全?
Django 的 redirect() 和 HttpResponseRedirect 只负责发 302 响应,不做任何 URL 校验。它信任你传给它的每一个字符串。哪怕你传的是 javascript:alert(1)、//evil.com、\x0d\x0aLocation: https://evil.com(CRLF 注入),它都照发不误。
- 旧版 Django(如 1.10 之前)甚至允许协议相对 URL(
//evil.com)和伪协议(javascript:)通过校验 - 即使你用
urlparse解析再取netloc,也容易忽略javascript:、data:等非 HTTP 协议,或被空字节、URL 编码绕过 -
is_safe_url()是 Django 自带的校验函数,但它默认只检查是否为“同域”或“绝对路径”,不自动启用白名单——必须显式传入allowed_hosts
用 django.utils.http.is_safe_url() 做基础校验
这是最轻量、最推荐的第一道防线。但注意:它不是开箱即用的安全开关,必须传参才生效。
- 必须传
allowed_hosts参数,否则默认只校验是否为相对路径(/foo)或同域绝对路径(https://yoursite.com/bar),不拦https://evil.com - 正确用法:
is_safe_url(url, allowed_hosts={request.get_host()})—— 注意是set,不是字符串 - 如果支持多域名(如
www.example.com和example.com),需把所有合法 host 加入 set,不能只靠request.get_host() - 对
next=/admin/这类相对路径,它返回True;对next=https://google.com,返回False(前提是allowed_hosts设置正确)
更严格的方案:白名单驱动的跳转控制
当业务明确知道合法跳转目标时(比如只允许跳到 /dashboard/、/settings/、/orders/),硬编码白名单比依赖 host 更可靠,也防住了子域名劫持和 DNS 污染。
- 把
next参数值当作 key 查字典:WHITELISTED_REDIRECTS = {'dashboard': '/dashboard/', 'settings': '/settings/'} - 前端表单里不传原始 URL,而是传一个预定义的 key:
<input type="hidden" name="next" value="dashboard"> - 后端校验:
target = WHITELISTED_REDIRECTS.get(request.POST.get('next')),查不到就 fallback 到'/' - 避免用正则匹配路径(如
^/user/.*$),易被/user/../etc/passwd或编码绕过
别忘了 POST 场景下的参数来源问题
很多人修复了 GET 中的 next,却在登录视图里继续从 request.GET 读取——而表单是 POST 提交的,request.GET 为空。这会导致跳转失效,但开发者可能误以为“没漏洞”,其实只是没触发。
- 登录表单必须把
next作为隐藏字段提交:<input type="hidden" name="next" value="{{ request.GET.next }}"> - 视图中统一从
request.POST.get('next')或request.POST.get('next') or request.GET.get('next')获取 - 校验逻辑必须放在取值之后、重定向之前,且对 POST 和 GET 一视同仁
- 如果用了 Django 的
LoginView,它内部已处理好这个逻辑,但你要确认没覆盖get_redirect_url()方法并绕过校验
最常被忽略的点是:is_safe_url() 不传 allowed_hosts 就等于没用;而白名单方案里,前端传的 key 如果没严格限制字符集(比如允许 .. 或空格),后端查字典前还得做清洗。安全不是加个函数就完事,是每个环节都要对输入保持怀疑。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











