从远程源进行 URL 重定向¶
ID: py/url-redirection
Kind: path-problem
Security severity: 6.1
Severity: error
Precision: high
Tags:
- security
- external/cwe/cwe-601
Query suites:
- python-code-scanning.qls
- python-security-extended.qls
- python-security-and-quality.qls
在未验证输入的情况下直接将用户输入合并到 URL 重定向请求中,会助长网络钓鱼攻击。在这些攻击中,毫不知情的用户可能会被重定向到恶意网站,该网站看起来与他们打算访问的真实网站非常相似,但实际上由攻击者控制。
建议¶
为了防范不受信任的 URL 重定向,建议避免将用户输入直接放入重定向 URL 中。相反,在服务器上维护一个授权重定向列表;然后根据提供的用户输入从该列表中进行选择。
如果这不可行,则应以其他方式验证用户输入,例如,验证目标 URL 是否不包含显式主机名。
示例¶
以下示例展示了一个 HTTP 请求参数直接用于 URL 重定向,而没有验证输入,这会助长网络钓鱼攻击。
from flask import Flask, request, redirect
app = Flask(__name__)
@app.route('/')
def hello():
target = request.args.get('target', '')
return redirect(target, code=302)
如果您知道一组有效的重定向目标,您可以在服务器上维护一个列表,并检查用户输入是否在该列表中。
from flask import Flask, request, redirect
VALID_REDIRECT = "http://cwe.mitre.org/data/definitions/601.html"
app = Flask(__name__)
@app.route('/')
def hello():
target = request.args.get('target', '')
if target == VALID_REDIRECT:
return redirect(target, code=302)
else:
# ignore the target and redirect to the home page
return redirect('/', code=302)
通常这不可行,因此另一种方法是检查目标 URL 是否没有指定显式主机名。例如,您可以使用 Python 标准库中的 urlparse
函数解析 URL,并检查 netloc
属性是否为空。
但是,请注意,某些情况并非像我们希望的那样可以被 urlparse
原样处理,因此我们需要调整两件事,如下例所示。
许多浏览器接受反斜杠字符 (
\
) 等同于正斜杠字符 (/
) 在 URL 中,但urlparse
函数则不接受。输入错误的 URL,例如
https:/example.com
或https:///example.com
,会被解析为具有空netloc
属性,而浏览器仍然会重定向到正确的站点。
from flask import Flask, request, redirect
from urllib.parse import urlparse
app = Flask(__name__)
@app.route('/')
def hello():
target = request.args.get('target', '')
target = target.replace('\\', '')
if not urlparse(target).netloc and not urlparse(target).scheme:
# relative path, safe to redirect
return redirect(target, code=302)
# ignore the target and redirect to the home page
return redirect('/', code=302)
对于 Django 应用程序,您可以使用 url_has_allowed_host_and_scheme
函数检查 URL 是否安全重定向,如下例所示。
from django.http import HttpResponseRedirect
from django.shortcuts import redirect
from django.utils.http import url_has_allowed_host_and_scheme
from django.views import View
class RedirectView(View):
def get(self, request, *args, **kwargs):
target = request.GET.get('target', '')
if url_has_allowed_host_and_scheme(target, allowed_hosts=None):
return HttpResponseRedirect(target)
else:
# ignore the target and redirect to the home page
return redirect('/')
请注意,url_has_allowed_host_and_scheme
正确处理反斜杠,因此不需要额外的处理。
参考¶
OWASP:XSS 未验证重定向和转发备忘单.
Python 标准库:urllib.parse.
常见弱点枚举:CWE-601.