Cors Wildcard in Fastapi with Dynamodb
Cors Wildcard in Fastapi with Dynamodb — how this specific combination creates or exposes the vulnerability
A CORS wildcard (Access-Control-Allow-Origin: *) in a FastAPI service that also interacts with DynamoDB can unintentionally expose sensitive data and amplify authorization flaws such as BOLA/IDOR. When allow_origins=["*"] is set together with credentials or custom headers, browsers may send authenticated requests on behalf of a user. If the FastAPI endpoint uses the caller’s identity (e.g., a Cognito-derived subject or API key) to query DynamoDB using a direct key like user_id = event["requestContext"]["authorizer"]["claims"]["sub"], the wildcard does not block the request server-side, but it widens the attack surface by allowing any origin to trigger those authenticated calls. An attacker-controlled site can invoke the endpoint via JavaScript, potentially leaking DynamoDB data that would otherwise be protected by authentication and scoped IAM policies. Because DynamoDB enforces IAM permissions per request, the service must ensure the identity it uses to sign requests is not derived from an untrusted origin; otherwise the wildcard circumvents intended origin-based controls.
Consider a typical pattern where FastAPI uses an HTTP event with a Cognito authorizer:
import json
def lambda_handler(event, context):
user_id = event["requestContext"]["authorizer"]["claims"]["sub"]
# Build DynamoDB client and query using user_id as partition key
dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table("UserProfiles")
response = table.get_item(Key={"user_id": user_id})
return {"statusCode": 200, "body": json.dumps(response.get("Item", {}))}
If FastAPI’s CORS configuration sets allow_origins=["*"] and the response includes credentials, a malicious page can make authenticated requests to this Lambda-backed endpoint. The DynamoDB query still executes with the authorizer’s subject, but the browser’s cross-origin request may expose the data to the attacker. This combination does not change DynamoDB’s permissions, yet it enables a broader cross-origin attack chain, especially when combined with misconfigured authorization or overly permissive IAM roles. The risk is not a DynamoDB misconfiguration per se, but rather how an unrestricted CORS policy interacts with authenticated data access patterns that rely on identity claims passed from the API layer to the database layer.
Dynamodb-Specific Remediation in Fastapi — concrete code fixes
Remediation centers on tightening CORS and ensuring DynamoDB access is bound to the authenticated subject without relying on the origin. In FastAPI, avoid the wildcard and instead specify origins explicitly or use a controlled allowlist. Combine this with validating the authenticated identity before constructing the DynamoDB request. Below are two concrete approaches, including a secure FastAPI route with a DynamoDB resource using boto3.
Approach 1: Restricted CORS with explicit origins
from fastapi import FastAPI, Request, Depends, HTTPException
from fastapi.middleware.cors import CORSMiddleware
import boto3
import json
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["https://trusted.example.com"],
allow_credentials=True,
allow_methods=["GET"],
allow_headers=["Authorization", "Content-Type"],
)
def get_db():
return boto3.resource("dynamodb")
@app.get("/profiles/{user_id}")
def read_profile(
user_id: str,
request: Request,
db = Depends(get_db)
):
# Derive subject from a validated auth dependency in production
subject = request.state.subject # set by an auth dependency
if not subject:
raise HTTPException(status_code=401, detail="Unauthorized")
table = db.Table("UserProfiles")
response = table.get_item(Key={"user_id": subject})
item = response.get("Item")
if not item or item.get("user_id") != subject:
raise HTTPException(status_code=403, detail="Forbidden")
return item
Approach 2: Lambda with controlled identity mapping
When using AWS Lambda behind API Gateway with Cognito authorizers, map the claim to the DynamoDB key explicitly and avoid any permissive CORS settings in the Lambda response headers. FastAPI can be served behind a Lambda adapter (e.g., Mangum) with strict CORS handled at the API Gateway level.
import json
import boto3
def lambda_handler(event, context):
# Validate authorizer and extract subject
if "requestContext" not in event or "authorizer" not in event["requestContext"]:
return {"statusCode": 401, "body": json.dumps({"error": "Unauthorized"})}
claims = event["requestContext"]["authorizer"].get("claims", {})
subject = claims.get("sub")
if not subject:
return {"statusCode": 401, "body": json.dumps({"error": "Unauthorized"})}
# Enforce ownership at DynamoDB level using subject as partition key
dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table("UserProfiles")
response = table.get_item(Key={"user_id": subject})
item = response.get("Item", {})
# Double-check that the item belongs to the subject
if item.get("user_id") != subject:
return {"statusCode": 403, "body": json.dumps({"error": "Forbidden"})}
return {"statusCode": 200, "body": json.dumps(item)}
These examples emphasize that the identity used for DynamoDB access must be validated independently of CORS. Do not rely on the origin header for authorization; instead bind the DynamoDB key directly to the authenticated subject and enforce ownership checks in your code. This approach mitigates risks even if CORS policy changes, and aligns with checks run by scanners that test for CORS misconfigurations alongside DynamoDB access patterns.
Related CWEs: dataExposure
| CWE ID | Name | Severity |
|---|---|---|
| CWE-200 | Exposure of Sensitive Information | HIGH |
| CWE-209 | Error Information Disclosure | MEDIUM |
| CWE-213 | Exposure of Sensitive Information Due to Incompatible Policies | HIGH |
| CWE-215 | Insertion of Sensitive Information Into Debugging Code | MEDIUM |
| CWE-312 | Cleartext Storage of Sensitive Information | HIGH |
| CWE-359 | Exposure of Private Personal Information (PII) | HIGH |
| CWE-522 | Insufficiently Protected Credentials | CRITICAL |
| CWE-532 | Insertion of Sensitive Information into Log File | MEDIUM |
| CWE-538 | Insertion of Sensitive Information into Externally-Accessible File | HIGH |
| CWE-540 | Inclusion of Sensitive Information in Source Code | HIGH |