CodeQL 文档

完整服务器端请求伪造

ID: py/full-ssrf
Kind: path-problem
Security severity: 9.1
Severity: error
Precision: high
Tags:
   - security
   - external/cwe/cwe-918
Query suites:
   - python-code-scanning.qls
   - python-security-extended.qls
   - python-security-and-quality.qls

单击以查看 CodeQL 代码库中的查询

在不验证输入的情况下直接将用户输入合并到 HTTP 请求中会导致服务器端请求伪造 (SSRF) 攻击。在这些攻击中,请求可能会被更改、定向到不同的服务器或通过不同的协议。这可能允许攻击者获取敏感信息或执行具有提升权限的操作。

我们区分攻击者可以控制多少 URL。

  • **完整 SSRF**:可以控制完整的 URL。

  • **部分 SSRF**:只能控制 URL 的一部分,例如硬编码域的 URL 路径组件。

部分控制 URL 通常难以利用。因此,我们为每个 URL 创建了单独的查询。

此查询涵盖完整 SSRF,要查找部分 SSRF,请使用 py/partial-ssrf 查询。

建议

为了防止 SSRF 攻击,您应该避免将用户提供的输入直接放入请求 URL 中。相反,要么在服务器上维护一个授权 URL 列表,并根据提供的输入从该列表中进行选择,要么对输入执行适当的验证。

示例

以下示例显示了易受完整 SSRF 攻击的代码,因为它使用不可信输入(HTTP 请求参数)直接构建 URL。通过使用 evil.com# 作为 target 值,请求的 URL 将为 https://evil.com#.example.com/data/。它还显示了如何通过使用用户输入选择已知固定字符串来解决问题。

import requests
from flask import Flask, request

app = Flask(__name__)

@app.route("/full_ssrf")
def full_ssrf():
    target = request.args["target"]

    # BAD: user has full control of URL
    resp = requests.get("https://" + target + ".example.com/data/")

    # GOOD: `subdomain` is controlled by the server.
    subdomain = "europe" if target == "EU" else "world"
    resp = requests.get("https://" + subdomain + ".example.com/data/")

示例

以下示例显示了易受部分 SSRF 攻击的代码,因为它使用不可信输入(HTTP 请求参数)直接构建 URL。通过使用 ../transfer-funds-to/123?amount=456 作为 user_id 值,请求的 URL 将为 https://api.example.com/transfer-funds-to/123?amount=456。它还显示了如何通过验证输入来解决问题。

import requests
from flask import Flask, request

app = Flask(__name__)

@app.route("/partial_ssrf")
def partial_ssrf():
    user_id = request.args["user_id"]

    # BAD: user can fully control the path component of the URL
    resp = requests.get("https://api.example.com/user_info/" + user_id)

    if user_id.isalnum():
        # GOOD: user_id is restricted to be alpha-numeric, and cannot alter path component of URL
        resp = requests.get("https://api.example.com/user_info/" + user_id)

参考文献

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