Ssrf in Flask with Bearer Tokens
Ssrf in Flask with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Server-Side Request Forgery (SSRF) in Flask APIs that accept and forward bearer tokens can expose internal services and bypass intended access controls. In this combination, an attacker can supply a malicious URL that causes the server to make authenticated requests on behalf of the bearer token provided by the client.
Consider a Flask endpoint that fetches a user profile from a downstream service and expects an Authorization bearer token. If the endpoint builds a request using user-supplied data without proper validation, an attacker can supply a target like http://169.169.254.254/latest/meta-data/iam/security-credentials/ (AWS instance metadata) while providing their own bearer token. The server, using its network position and the forwarded token, may reach internal endpoints not intended for external clients.
from flask import Flask, request, jsonify
import requests
app = Flask(__name__)
@app.route("/fetch-profile")
def fetch_profile():
url = request.args.get("url")
token = request.headers.get("Authorization")
if token and token.startswith("Bearer "):
headers = {"Authorization": token}
else:
headers = {}
# Vulnerable: user-controlled URL with forwarded bearer token
resp = requests.get(url, headers=headers, timeout=5)
return jsonify({"status": resp.status_code, "data": resp.text})
In this example, the bearer token provided in the request to /fetch-profile is forwarded to the user-supplied url. An SSRF vulnerability arises because the server can be coerced into making authenticated requests to internal services, such as metadata endpoints or internal APIs that only expect requests from trusted network zones.
SSRF in this context can also occur when the bearer token is obtained from an internal source and the server uses it to access other internal APIs. Even without direct token leakage, an attacker can leverage the server as a proxy to probe internal networks. The presence of bearer tokens does not mitigate SSRF; it can amplify the impact by allowing the attacker to act with the permissions encoded in the token.
Real-world patterns mirror this: an OpenAPI spec may define an endpoint like /external/data that passes through an Authorization header to a backend. If input validation is weak, the endpoint can be tricked into directing requests to internal URLs, such as http://internal-service:8080/admin, using the caller’s bearer token. This aligns with common OWASP API Top 10 API2:2023 broken object level authorization risks when combined with unchecked redirects or URL parsing.
middleBrick scans detect such patterns by comparing the OpenAPI/Swagger spec definitions with runtime behavior, identifying cases where user-controlled URLs are used in requests that include authorization headers. This helps surface SSRF risks in unauthenticated attack scenarios, where no credentials are required to trigger the vulnerable endpoint.
Bearer Tokens-Specific Remediation in Flask — concrete code fixes
To remediate SSRF in Flask when working with bearer tokens, avoid forwarding user-supplied URLs and strictly validate destinations. When you must call downstream services, use a fixed set of allowed hosts and do not forward client-provided Authorization headers to untrusted endpoints.
Instead of directly using the caller’s bearer token for arbitrary URLs, validate the target host and construct a clean request. Below is a safer pattern that checks the host against an allowlist and does not propagate the client’s bearer token to user-defined endpoints.
from flask import Flask, request, jsonify
import requests
from urllib.parse import urlparse
ALLOWED_HOSTS = {"api.example.com", "data.service.internal"}
app = Flask(__name__)
def is_allowed_host(url):
try:
host = urlparse(url).hostname or ""
return host in ALLOWED_HOSTS
except Exception:
return False
@app.route("/fetch-profile")
def fetch_profile():
url = request.args.get("url")
if not url or not is_allowed_host(url):
return jsonify({"error": "invalid or disallowed URL"}), 400
# Use a fixed token or no external token when calling internal services
headers = {"Authorization": "Bearer "}
resp = requests.get(url, headers=headers, timeout=5)
return jsonify({"status": resp.status_code, "data": resp.text})
If you need to call external APIs on behalf of the user, do not forward the user’s bearer token to arbitrary URLs. Instead, use the token only for specific, pre-approved endpoints and ensure the URL is validated against a strict allowlist. This reduces the attack surface for SSRF and prevents the server from becoming an authenticated proxy for internal resources.
For broader protection, apply input validation libraries and enforce network-level egress controls so that the application cannot reach sensitive internal addresses. These measures complement the scanning provided by tools like middleBrick, which can highlight risky endpoint behaviors in the dashboard and via the CLI (middlebrick scan <url>) and integrate checks into CI/CD with the GitHub Action to fail builds when risky patterns are detected.
Related CWEs: ssrf
| CWE ID | Name | Severity |
|---|---|---|
| CWE-918 | Server-Side Request Forgery (SSRF) | CRITICAL |
| CWE-441 | Unintended Proxy or Intermediary (Confused Deputy) | HIGH |