Cors Wildcard in Flask with Dynamodb
Cors Wildcard in Flask with Dynamodb — how this specific combination creates or exposes the vulnerability
A CORS wildcard in a Flask application that uses DynamoDB as a backend data store can unintentionally expose both data and administrative operations to any origin. When Access-Control-Allow-Origin: * is set for routes that perform DynamoDB operations, browsers allow any website to make authenticated requests on behalf of a user if credentials or tokens are included. This intersects with DynamoDB permissions because the backend uses a single IAM role or user key, and those credentials are effectively delegated to any caller the browser trusts.
Consider a Flask route that queries DynamoDB based on a path parameter without verifying the requester's ownership of the resource:
from flask import Flask, request, jsonify
import boto3
from botocore.exceptions import ClientError
app = Flask(__name__)
dynamodb = boto3.resource('dynamodb', region_name='us-east-1')
table = dynamodb.Table('UserProfiles')
@app.route('/profile/', methods=['GET'])
def get_profile(user_id):
# Vulnerable: no ownership check, CORS wildcard allows any origin
response = table.get_item(Key={'user_id': user_id})
item = response.get('Item', {})
return jsonify(item)
If this route sets Access-Control-Allow-Origin: *, a malicious site can embed a script that calls /profile/attacker_chosen_id. Because the route does not validate that the authenticated user matches user_id, the request succeeds using the backend’s AWS credentials, leading to unauthorized data access. This pattern is common in applications that treat the API as a public endpoint while relying on CORS for browser-level control, which is insufficient for authorization.
The risk is amplified when combined with insecure DynamoDB resource policies or when IAM credentials with broad permissions are embedded in the Flask app. An attacker can enumerate user IDs through timing differences or error messages, then read or attempt to modify other users’ data. This maps to OWASP API Top 10 2023 A1: Broken Object Level Authorization and A5: Security Misconfiguration, with potential compliance implications under GDPR and HIPAA if personal data is exposed.
In a real assessment using middleBrick, such a configuration would be flagged with a high severity for CORS misconfiguration combined with missing ownership validation, and the LLM/AI Security checks would confirm whether prompt-injection or data-exfiltration probes can leverage the wildcard to reach backend endpoints.
Dynamodb-Specific Remediation in Flask — concrete code fixes
Remediation centers on removing the CORS wildcard for authenticated routes and enforcing strict ownership checks at the application layer before any DynamoDB operation. The following example demonstrates a secure pattern where the authenticated user's identity is verified against the requested resource, and CORS is scoped to trusted origins.
from flask import Flask, request, jsonify, g
import boto3
from functools import wraps
app = Flask(__name__)
dynamodb = boto3.resource('dynamodb', region_name='us-east-1')
table = dynamodb.Table('UserProfiles')
# Mock auth: in production, integrate with your identity provider
def authenticate(f):
@wraps(f)
def wrapper(*args, **kwargs):
token = request.headers.get('Authorization')
if not token or not token.startswith('Bearer '):
return jsonify({'error': 'unauthorized'}), 401
# Validate token and extract user_id; set g.user_id
g.user_id = 'user-123' # derived from token validation
return f(*args, **kwargs)
return wrapper
def owns_resource(f):
@wraps(f)
def wrapper(user_id, *args, **kwargs):
if str(g.user_id) != str(user_id):
return jsonify({'error': 'forbidden'}), 403
return f(user_id, *args, **kwargs)
return wrapper
@app.route('/profile/', methods=['GET'])
@authenticate
@owns_resource
def get_profile(user_id):
# Safe: ownership verified, DynamoDB request uses scoped credentials
response = table.get_item(Key={'user_id': user_id})
item = response.get('Item', {})
if not item:
return jsonify({'error': 'not found'}), 404
return jsonify(item)
# Configure CORS per-route or per-view, never wildcard for authenticated endpoints
@app.after_request
def apply_cors(response):
origin = request.headers.get('Origin')
allowed_origins = {'https://trusted.example.com', 'https://app.example.com'}
if origin in allowed_origins:
response.headers['Access-Control-Allow-Origin'] = origin
response.headers['Access-Control-Allow-Credentials'] = 'true'
return response
Key remediation points:
- Remove the CORS wildcard (
*) for any route that accesses DynamoDB or exposes sensitive data. Instead, validate theOriginheader against an allowlist. - Enforce ownership checks by comparing the authenticated user identity (e.g., from a JWT or session) with the resource identifier before executing
table.get_itemor other DynamoDB calls. - Apply least-privilege IAM roles to the Flask application, granting only the necessary DynamoDB permissions (e.g.,
dynamodb:GetItemfor a specific table) rather than broad read/write access. - Return generic error messages to avoid leaking information that could aid enumeration attacks.
These changes align with OWASP API Security Top 10 controls and reduce the attack surface exposed by a CORS wildcard. In environments with multiple origins, middleBrick’s dashboard can be used to track CORS-related findings over time, and the Pro plan’s continuous monitoring can alert on regressions when wildcard origins reappear.
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 |