Ssrf Server Side in Fastapi with Basic Auth
Ssrf Server Side in Fastapi with Basic Auth — how this specific combination creates or exposes the vulnerability
Server-side request forgery (SSRF) in a FastAPI service that uses HTTP Basic Authentication can occur when user-controlled input is used to drive outbound HTTP requests without adequate validation or network segregation. In this combination, an authenticated or unauthenticated attacker may supply a malicious URL that causes the backend to open connections to internal services, cloud metadata endpoints, or restricted internal networks that the server can reach.
FastAPI does not inherently protect against SSRF; it relies on how you implement HTTP clients and access controls. When Basic Auth is involved, two risk patterns emerge:
- Authentication bypass via SSRF: If the endpoint performing outbound requests also validates credentials via Basic Auth, an attacker may supply an internal target that triggers the server to send credentials to an attacker-controlled service, or they may probe internal endpoints that leak authentication material or session tokens.
- Credential exposure and trust assumptions: Basic Auth credentials are often reused across internal services or between environments. An SSRF that reaches metadata services (e.g., cloud instance metadata at 169.254.169.254) can expose those credentials if they are injected into headers or if the server uses default or static credentials.
Consider an endpoint that accepts a URL to fetch or convert resources:
from fastapi import FastAPI, Depends, HTTPException, Header
import httpx
app = FastAPI()
def get_basic_auth_token(authorization: str = Header(None)):
# Expects: Basic base64(username:password)
if not authorization or not authorization.startswith("Basic "):
return None
return authorization.split(" ", 1)[1]
@app.get("/fetch")
def fetch(url: str, auth: str = Depends(get_basic_auth_token)):
if not auth:
raise HTTPException(status_code=401, detail="Missing Basic Auth")
headers = {"Authorization": f"Basic {auth}"} # Reusing provided credentials to reach target
resp = httpx.get(url, headers=headers, timeout=5.0)
return {"status": resp.status_code, "body": resp.text[:200]}
This pattern is vulnerable because the attacker can supply an SSRF target such as http://169.254.169.254/latest/meta-data/iam/security-credentials/ to reach the cloud metadata service. The server forwards the request and may inadvertently include credentials in headers, exposing them. Even when the client supplies credentials for the target, the server’s network position means it can reach internal endpoints that a direct client cannot, magnifying the impact.
middleBrick detects SSRF by correlating OpenAPI/Swagger specifications (including $ref resolution) with runtime behavior. For example, if an operation marked as external or with a host parameter is invoked with an internal IP or cloud metadata host, the scan flags it as a finding. The scan also tests for common bypasses like SSRF via DNS rebinding or alternative ports, and reports findings aligned with OWASP API Top 10 and other compliance frameworks.
Basic Auth-Specific Remediation in Fastapi — concrete code fixes
To mitigate SSRF when Basic Auth is used in FastAPI, apply defense-in-depth: strict input validation, network-level isolation, and credential handling that avoids forwarding user-controlled credentials.
1) Validate and restrict targets
Maintain an allowlist of permitted hosts or schemas. Reject URLs that point to private IP ranges, cloud metadata addresses, or localhost.
import ipaddress
from urllib.parse import urlparse
def is_safe_url(target: str) -> bool:
parsed = urlparse(target)
if parsed.scheme not in ("http", "https"):
return False
try:
host = parsed.hostname
if host is None:
return False
# Block private and loopback ranges
ip = ipaddress.ip_address(host)
if ip.is_private or ip.is_loopback or ip.is_link_local:
return False
# Explicitly block cloud metadata
if host == "169.254.169.254":
return False
except ValueError:
return False
return True
Use this function in your route:
@app.get("/fetch")
def fetch(url: str, auth: str = Depends(get_basic_auth_token)):
if not auth:
raise HTTPException(status_code=401, detail="Missing Basic Auth")
if not is_safe_url(url):
raise HTTPException(status_code=400, detail="URL not allowed")
# Do NOT forward the client’s auth to the target
resp = httpx.get(url, timeout=5.0)
return {"status": resp.status_code, "body": resp.text[:200]}
2) Avoid forwarding Basic Auth credentials
Do not reuse the client-supplied Basic Auth token when calling the target. Instead, use a server-side credential or no authentication if the target does not require it. If the target does require auth, store server-side secrets and inject them securely.
import os
from fastapi import Security
from fastapi.security import HTTPBasic
security = HTTPBasic()
def get_server_creds():
user = os.getenv("TARGET_USER", "svc")
pwd = os.getenv("TARGET_PASS", "")
return (user, pwd)
@app.get("/fetch")
def fetch(url: str, creds: tuple = Security(security)):
if not is_safe_url(url):
raise HTTPException(status_code=400, detail="URL not allowed")
# Use server-side credentials, not the client-provided ones
resp = httpx.get(url, auth=creds, timeout=5.0)
return {"status": resp.status_code, "body": resp.text[:200]}
3) Network controls
Deploy the service in a network zone with egress restrictions. Use egress firewalls or service meshes to prevent connections to sensitive internal addresses. Combine this with outbound proxy configurations if applicable.
4) Dependency hygiene
Keep httpx and related libraries up to date to avoid known vulnerabilities that could aid SSRF chains. Scan your dependencies regularly using tools that integrate with your CI/CD pipeline; the middleBrick GitHub Action can add API security checks to your CI/CD pipeline and fail builds if risk scores exceed your threshold.
These steps reduce the attack surface for SSRF while preserving Basic Auth for your intended use case. The dashboard can be used to track your API security scores over time, and the CLI tool (middlebrick scan