CodeQL 文档

从远程源进行 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

点击查看 CodeQL 代码库中的查询

在未验证输入的情况下直接将用户输入合并到 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.comhttps:///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 正确处理反斜杠,因此不需要额外的处理。

参考

  • ©GitHub, Inc.
  • 条款
  • 隐私