CodeQL 文档

不完整的 URL 子字符串清理

ID: py/incomplete-url-substring-sanitization
Kind: problem
Security severity: 7.8
Severity: warning
Precision: high
Tags:
   - correctness
   - security
   - external/cwe/cwe-20
Query suites:
   - python-code-scanning.qls
   - python-security-extended.qls
   - python-security-and-quality.qls

点击查看 CodeQL 仓库中的查询

清理不可信的 URL 是防止诸如请求伪造和恶意重定向等攻击的常见技术。通常,这是通过检查 URL 的主机是否在允许的主机集中来完成的。

然而,将 URL 视为字符串并检查允许的主机是否为 URL 的子字符串,很容易出错。恶意 URL 可以通过将允许的主机嵌入到意外的位置来绕过这种安全检查。

即使子字符串检查没有在安全关键的上下文中使用,当检查意外成功时,不完整的检查仍然会导致不良行为。

建议

在对主机值进行检查之前解析 URL,并确保检查正确处理任意子域序列。

示例

以下示例代码检查 URL 重定向是否将到达 example.com 域名。

from flask import Flask, request, redirect
from urllib.parse import urlparse

app = Flask(__name__)

# Not safe, as "evil-example.net/example.com" would be accepted

@app.route('/some/path/bad1')
def unsafe1(request):
    target = request.args.get('target', '')
    if "example.com" in target:
        return redirect(target)

# Not safe, as "benign-looking-prefix-example.com" would be accepted

@app.route('/some/path/bad2')
def unsafe2(request):
    target = request.args.get('target', '')
    if target.endswith("example.com"):
        return redirect(target)



#Simplest and safest approach is to use an allowlist

@app.route('/some/path/good1')
def safe1(request):
    allowlist = [
        "example.com/home",
        "example.com/login",
    ]
    target = request.args.get('target', '')
    if target in allowlist:
        return redirect(target)

#More complex example allowing sub-domains.

@app.route('/some/path/good2')
def safe2(request):
    target = request.args.get('target', '')
    host = urlparse(target).hostname
    #Note the '.' preceding example.com
    if host and host.endswith(".example.com"):
        return redirect(target)

前两个示例显示了容易被绕过的不安全检查。在 unsafe1 中,攻击者可以在 url 中的任何位置添加 example.com。例如,http://evil-example.net/example.com

unsafe2 中,攻击者必须使用以 example.com 结尾的主机名,但这很容易做到。例如,http://benign-looking-prefix-example.com

后两个示例显示了安全的检查。在 safe1 中,使用了白名单。虽然相当不灵活,但这很容易做对,并且最有可能安全。

safe2 中,urlparse 用于解析 URL,然后检查主机名以确保它以 .example.com 结尾。

参考资料

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