HIGH ssrfflaskapi keys

Ssrf in Flask with Api Keys

Ssrf in Flask with Api Keys — how this specific combination creates or exposes the vulnerability

Server-Side Request Forgery (SSRF) in a Flask application that uses API keys creates a distinct risk pattern because the server-side code often holds long-lived or overly permissive credentials. An attacker can trick the backend into making outbound requests to internal services that are protected only by an API key, bypassing any perimeter network controls. For example, a Flask route that fetches external user profile data might accept a URL parameter and forward it using a requests call that includes an API key stored in an environment variable. If input validation is weak, an attacker can supply an internal address like http://169.254.169.254/latest/meta-data/iam/security-credentials/ and the server will use its API key to query the cloud metadata service, leading to credential exposure.

SSRF in this context leverages the trust placed in server-side API keys. Unlike client-side keys, server keys often have broader scopes and are less frequently rotated. A vulnerable Flask route that does not validate or restrict the target host can allow an attacker to probe internal networks, reach services without public DNS, or exploit metadata endpoints. The presence of API keys does not mitigate SSRF; it can amplify impact by giving the attacker a higher-privileged identity when the request reaches internal systems. This aligns with the OWASP API Top 10 A05: Broken Function Level Authorization when parameter handling allows bypassing intended access boundaries.

When scanning a Flask API with middleBrick, the tool checks for SSRF as part of its 12 security checks, including input validation and network reachability analysis. The scanner can detect whether user-controlled input can influence outbound requests and whether sensitive internal endpoints are accessible from the server. middleBrick also supports OpenAPI/Swagger spec analysis, cross-referencing spec definitions with runtime findings to highlight mismatches between declared and observed behavior. For example, if the spec describes only external domains but runtime probing reaches internal IP ranges, the scanner surfaces this discrepancy with severity and remediation guidance.

Api Keys-Specific Remediation in Flask — concrete code fixes

To remediate SSRF in Flask when API keys are used, enforce strict allowlists for target hosts and paths, and avoid forwarding user input directly to requests. Do not rely on API keys as a boundary; treat user input as untrusted regardless of authentication or key usage. Below are concrete code examples that demonstrate a vulnerable pattern and a secure alternative.

Vulnerable pattern

import os
import requests
from flask import Flask, request, jsonify

app = Flask(__name__)
API_KEY = os.environ.get('EXTERNAL_API_KEY')

@app.route('/fetch')
def fetch():
    url = request.args.get('url')
    if not url:
        return jsonify({'error': 'url parameter required'}), 400
    headers = {'Authorization': f'Bearer {API_KEY}'}
    resp = requests.get(url, headers=headers, timeout=5)
    return jsonify({'status': resp.status_code, 'body': resp.text[:200]})

This pattern is unsafe because url is user-controlled and can point to internal services. An API key in the header does not prevent the request from reaching unintended endpoints.

Secure remediation with host allowlist

import os
import re
import requests
from flask import Flask, request, jsonify
from urllib.parse import urlparse

app = Flask(__name__)
API_KEY = os.environ.get('EXTERNAL_API_KEY')
ALLOWED_HOSTS = {'api.example.com', 'cdn.example.com'}

def is_allowed_host(url: str) -> bool:
    try:
        parsed = urlparse(url)
        return parsed.hostname in ALLOWED_HOSTS and parsed.scheme in {'https'}
    except Exception:
        return False

@app.route('/fetch')
def fetch():
    url = request.args.get('url')
    if not url:
        return jsonify({'error': 'url parameter required'}), 400
    if not is_allowed_host(url):
        return jsonify({'error': 'host not allowed'}), 403
    headers = {'Authorization': f'Bearer {API_KEY}'}
    resp = requests.get(url, headers=headers, timeout=5)
    return jsonify({'status': resp.status_code, 'body': resp.text[:200]})

This secure version validates the hostname against an explicit allowlist and enforces HTTPS. The API key remains in the header for authentication, but the server no longer makes requests to arbitrary or internal destinations. For additional safety, you can add path-level allowlisting, strict timeouts, and disable redirects to avoid SSRF chains.

middleBrick’s CLI and Web Dashboard can help validate that such controls are effective by testing whether user-supplied input can reach unintended endpoints. The GitHub Action can enforce a minimum security score before merges, and the MCP Server can provide in-IDE guidance when implementing these patterns.

Related CWEs: ssrf

CWE IDNameSeverity
CWE-918Server-Side Request Forgery (SSRF) CRITICAL
CWE-441Unintended Proxy or Intermediary (Confused Deputy) HIGH

Frequently Asked Questions

Does using API keys prevent SSRF in Flask?
No. API keys provide authentication for outbound requests but do not restrict where a request can go. SSRF mitigation requires strict input validation and network-level controls such as host allowlists.
How does middleBrick detect SSRF in Flask APIs that use API keys?
middleBrick performs black-box testing by submitting controlled inputs and observing whether the server can reach internal or sensitive endpoints. It cross-references findings with OpenAPI/Swagger definitions to identify mismatches between declared and actual behavior.