python.flask.security.injection.ssrf-requests.ssrf-requests

Verifed by r2c
Community Favorite
profile photo of returntocorpreturntocorp
Author
129,048
Download Count*

Data from request object is passed to a new server-side request. This could lead to a server-side request forgery (SSRF). To mitigate, ensure that schemes and hosts are validated against an allowlist, do not forward the response to the user, and ensure proper authentication and transport-layer security in the proxied request.

Run Locally

Run in CI

Defintion

rules:
  - id: ssrf-requests
    languages:
      - python
    severity: ERROR
    message: Data from request object is passed to a new server-side request. This
      could lead to a server-side request forgery (SSRF). To mitigate, ensure
      that schemes and hosts are validated against an allowlist, do not forward
      the response to the user, and ensure proper authentication and
      transport-layer security in the proxied request.
    metadata:
      cwe: "CWE-918: Server-Side Request Forgery (SSRF)"
      owasp: "A1: Injection"
      references:
        - https://owasp.org/www-community/attacks/Server_Side_Request_Forgery
      category: security
      technology:
        - flask
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
    pattern-either:
      - patterns:
          - pattern: requests.$FUNC(...)
          - pattern-either:
              - pattern-inside: |
                  @$APP.route($ROUTE, ...)
                  def $ROUTE_FUNC(..., $ROUTEVAR, ...):
                    ...
                    requests.$FUNC(..., <... $ROUTEVAR ...>, ...)
              - pattern-inside: |
                  @$APP.route($ROUTE, ...)
                  def $ROUTE_FUNC(..., $ROUTEVAR, ...):
                    ...
                    $INTERM = <... $ROUTEVAR ...>
                    ...
                    requests.$FUNC(..., <... $INTERM ...>, ...)
      - pattern: requests.$FUNC(..., <... flask.request.$W.get(...) ...>, ...)
      - pattern: requests.$FUNC(..., <... flask.request.$W[...] ...>, ...)
      - pattern: requests.$FUNC(..., <... flask.request.$W(...) ...>, ...)
      - pattern: requests.$FUNC(..., <... flask.request.$W ...>, ...)
      - patterns:
          - pattern-inside: |
              $INTERM = <... flask.request.$W.get(...) ...>
              ...
              requests.$FUNC(<... $INTERM ...>, ...)
          - pattern: requests.$FUNC(...)
      - patterns:
          - pattern-inside: |
              $INTERM = <... flask.request.$W[...] ...>
              ...
              requests.$FUNC(<... $INTERM ...>, ...)
          - pattern: requests.$FUNC(...)
      - patterns:
          - pattern-inside: |
              $INTERM = <... flask.request.$W(...) ...>
              ...
              requests.$FUNC(<... $INTERM ...>, ...)
          - pattern: requests.$FUNC(...)
      - patterns:
          - pattern-inside: |
              $INTERM = <... flask.request.$W ...>
              ...
              requests.$FUNC(<... $INTERM ...>, ...)
          - pattern: requests.$FUNC(...)

Examples

ssrf-requests.py

import flask
import requests

app = flask.Flask(__name__)

@app.route("/route_param/<route_param>")
def route_param(route_param):
    print("blah")
    # ruleid: ssrf-requests
    return requests.get(route_param)

@app.route("/route_param_ok/<route_param>")
def route_param_ok(route_param):
    print("blah")
    # ok: ssrf-requests
    return requests.get("this is safe")

@app.route("/get_param", methods=["GET"])
def get_param():
    param = flask.request.args.get("param")
    # ruleid: ssrf-requests
    requests.post(param, timeout=10)

@app.route("/get_param_ok", methods=["GET"])
def get_param_ok():
    param = flask.request.args.get("param")
    # ok: ssrf-requests
    requests.post("this is safe", timeout=10)

@app.route("/get_param_inline_concat", methods=["GET"])
def get_param_inline_concat():
    # ruleid: ssrf-requests
    requests.get(flask.request.args.get("param") + "/id")

@app.route("/get_param_concat", methods=["GET"])
def get_param_concat():
    param = flask.request.args.get("param")
    # ruleid: ssrf-requests
    requests.get(param + "/id")

@app.route("/get_param_format", methods=["GET"])
def get_param_format():
    param = flask.request.args.get("param")
    # ruleid: ssrf-requests
    requests.get("{}.csv".format(param))

@app.route("/get_param_percent_format", methods=["GET"])
def get_param_percent_format():
    param = flask.request.args.get("param")
    # ruleid: ssrf-requests
    requests.get("%s/id" % (param,))

@app.route("/post_param", methods=["POST"])
def post_param():
    param = flask.request.form['param']
    if True:
        # ruleid: ssrf-requests
        requests.get(param)

@app.route("/subexpression", methods=["POST"])
def subexpression():
    param = "{}".format(flask.request.form['param'])
    print("do things")
    # ruleid: ssrf-requests
    requests.post(param, data={"hello", "world"})

@app.route("/ok")
def ok():
    requests.get("https://www.google.com")