HIGH cors wildcardflaskbearer tokens

Cors Wildcard in Flask with Bearer Tokens

Cors Wildcard in Flask with Bearer Tokens — how this specific combination creates or exposes the vulnerability

Cross-origin resource sharing (CORS) misconfigurations are common in Flask APIs, especially when authentication relies on bearer tokens. A wildcard CORS policy, typically set via Access-Control-Allow-Origin: *, can unintentionally expose protected endpoints when combined with bearer token authentication. In Flask, this often occurs when developers use extensions like flask-cors and apply a global wildcard without accounting for credentialed requests.

When an API uses bearer tokens in the Authorization header, the browser’s CORS preflight and simple requests follow specific rules. If Access-Control-Allow-Origin: * is returned while the request includes credentials (such as an Authorization header), the browser treats the response as a CORS error and blocks the frontend from reading the response. This does not prevent a non-browser client (like curl or Postman) from calling the endpoint, but it does break legitimate web clients and can create confusion about whether the API is truly protected.

More critically, a wildcard origin can pair with permissive CORS methods and headers to expose token-handling logic to any website. For example, if Access-Control-Allow-Methods includes unsafe methods and Access-Control-Allow-Headers includes Authorization without strict validation, an attacker-controlled site can probe the API via JavaScript. While the browser blocks the frontend from reading the response, the preflight and actual requests still reach the server, potentially aiding reconnaissance or token leakage via logs or error messages.

Consider a Flask route that validates bearer tokens manually:

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.before_request
def cors_headers():
    # Dangerous: wildcard with bearer usage
    origin = request.headers.get('Origin')
    if origin:
        # This allows any origin when bearer tokens are used
        response = app.response_class()
        response.headers['Access-Control-Allow-Origin'] = '*'
        response.headers['Access-Control-Allow-Headers'] = 'Authorization, Content-Type'
        response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'

@app.route('/api/me')
def me():
    auth = request.headers.get('Authorization')
    if not auth or not auth.startswith('Bearer '):
        return jsonify({'error': 'Unauthorized'}), 401
    token = auth.split(' ')[1]
    # Token validation omitted for brevity
    return jsonify({'ok': True})

In this pattern, the wildcard origin is applied regardless of the token’s validity or origin. A safer approach is to mirror the request origin when it is known and trusted, and to avoid wildcard origins when credentials are involved. Even with bearer tokens, CORS should be as restrictive as possible: specific origins, limited methods, and explicit headers.

Another subtle risk involves preflight caching. Browsers cache preflight responses, and a wildcard policy can persist across deployments, keeping overly permissive CORS active longer than intended. This can delay detection of misconfigurations and prolong exposure. Combining this with bearer tokens means token validation logic might be assumed to be enforced by CORS, when in fact CORS only governs browser-based cross-origin access and does not replace server-side authorization checks.

In summary, the combination of CORS wildcard and bearer tokens in Flask creates a misconfiguration where the API appears protected by authentication but is actually exposed to cross-origin abuse in browser contexts. It can lead to CORS errors for legitimate web clients and may aid attacker reconnaissance. The fix is not to remove bearer tokens but to tighten CORS: avoid wildcards, explicitly list origins, and ensure server-side authorization remains the ultimate gatekeeper.

Bearer Tokens-Specific Remediation in Flask — concrete code fixes

Remediation centers on precise CORS configuration and robust token validation. For Flask, prefer flask-cors with fine-grained controls or manually set headers to avoid wildcards when Authorization is present. Below are two concrete, secure patterns.

Pattern 1: Conditional CORS without wildcards

Instead of a global wildcard, set Access-Control-Allow-Origin to the request origin only when it matches an allowlist, and never when the request is unauthenticated or ambiguous.

from flask import Flask, request, jsonify, make_response

app = Flask(__name__)

ALLOWED_ORIGINS = {
    'https://app.example.com',
    'https://admin.example.com',
}

@app.after_request
def apply_cors(response):
    origin = request.headers.get('Origin')
    if origin in ALLOWED_ORIGINS:
        response.headers['Access-Control-Allow-Origin'] = origin
        response.headers['Access-Control-Allow-Headers'] = 'Authorization, Content-Type'
        response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
        response.headers['Access-Control-Allow-Credentials'] = 'true'
    return response

@app.route('/api/me')
def me():
    auth = request.headers.get('Authorization')
    if not auth or not auth.startswith('Bearer '):
        return jsonify({'error': 'Unauthorized'}), 401
    token = auth.split(' ')[1]
    # Validate token here (e.g., verify signature and claims)
    return jsonify({'ok': True})

Pattern 2: Using flask-cors with resource-specific rules

If you use flask-cors, configure it to avoid wildcards and to handle credentials explicitly.

from flask import Flask, jsonify
from flask_cors import CORS, cross_origin

app = Flask(__name__)

# Global config without wildcard
CORS(app, resources={r&api/*: {"origins": ["https://app.example.com", "https://admin.example.com"], "supports_credentials": True}})

@app.route('/api/me')
def me():
    auth = request.headers.get('Authorization')
    if not auth or not auth.startswith('Bearer '):
        return jsonify({'error': 'Unauthorized'}), 401
    token = auth.split(' ')[1]
    # Validate token here
    return jsonify({'ok': True})

In both examples, the key remediation points are:

  • Never return Access-Control-Allow-Origin: * when the request includes bearer tokens or credentials.
  • Validate the bearer token on the server on every request; do not rely on CORS to enforce authorization.
  • Explicitly list allowed origins and methods, and set Access-Control-Allow-Credentials: true only when necessary.
  • Ensure token validation logic is independent of CORS and runs for every request.

These patterns ensure that CORS does not inadvertently weaken bearer token protections while still allowing controlled cross-origin access for your web clients.

Related CWEs: dataExposure

CWE IDNameSeverity
CWE-200Exposure of Sensitive Information HIGH
CWE-209Error Information Disclosure MEDIUM
CWE-213Exposure of Sensitive Information Due to Incompatible Policies HIGH
CWE-215Insertion of Sensitive Information Into Debugging Code MEDIUM
CWE-312Cleartext Storage of Sensitive Information HIGH
CWE-359Exposure of Private Personal Information (PII) HIGH
CWE-522Insufficiently Protected Credentials CRITICAL
CWE-532Insertion of Sensitive Information into Log File MEDIUM
CWE-538Insertion of Sensitive Information into Externally-Accessible File HIGH
CWE-540Inclusion of Sensitive Information in Source Code HIGH

Frequently Asked Questions

Why is a wildcard CORS dangerous when bearer tokens are used?
A wildcard origin (Access-Control-Allow-Origin: *) with bearer tokens can expose APIs to cross-origin requests in browsers, causing CORS errors for legitimate clients and aiding attacker reconnaissance; server-side token validation must remain the primary authorization control.
Can I safely allow credentials with a wildcard origin in Flask CORS?
No. Browsers block requests with credentials when Access-Control-Allow-Origin is a wildcard. You must specify exact origins and explicitly set Access-Control-Allow-Credentials to true only for those origins.