CodeQL 文档

部分服务器端请求伪造

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

点击查看 CodeQL 仓库中的查询

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

我们区分攻击者可以控制的 URL 的程度

  • 完整 SSRF:可以完全控制 URL。

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

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

此查询涵盖部分 SSRF,要查找完整 SSRF,请使用 py/full-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)

参考

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