Skip to main content

Overview

This vulnerability occurs when a web application implements Cross-Origin Resource Sharing (CORS) policies that are too permissive, particularly by setting the Access-Control-Allow-Origin header to overly broad values like the wildcard (*) or by dynamically reflecting the requesting Origin header without proper validation. This allows malicious websites, visited by an authenticated user, to make requests to the vulnerable application and read the responses, potentially stealing sensitive user data or performing unauthorized actions via the user’s session. 🌐🔓➡️😈

Business Impact

Permissive CORS policies undermine the browser’s Same-Origin Policy, leading to:
  • Sensitive Data Exposure: Malicious websites can make authenticated requests (using the victim’s cookies) to the vulnerable application’s API endpoints and read sensitive data returned in the response (e.g., user profile details, messages, financial information).
  • Unauthorized Actions: While the primary risk is data reading, permissive CORS can sometimes facilitate actions if combined with other vulnerabilities or if the endpoint relies solely on cookies for authorization and performs state changes via GET requests (though this is less common).
  • Trust Exploitation: It abuses the trust relationship between the user and the vulnerable application.

Reference Details

CWE ID: CWE-942 (Related: CWE-346 Origin Validation Error, CWE-264 Permissions/Privileges) OWASP Top 10 (2021): A05:2021 - Security Misconfiguration Severity: High (especially if sensitive data is exposed)

Framework-Specific Analysis and Remediation

CORS is typically configured either at the web server/proxy level (Nginx, Apache) or within the application framework using middleware or filters. The vulnerability is allowing origins that should not be trusted. Key Remediation Principles:
  1. Avoid Wildcard (*) if Credentials Allowed: Never set Access-Control-Allow-Origin: * if Access-Control-Allow-Credentials: true is also set. Browsers generally block this combination anyway, but relying on it is insecure.
  2. Use Strict Allow-lists: Maintain an explicit list of trusted origin domains that are permitted to make cross-origin requests.
  3. Validate Dynamic Origins: If dynamically reflecting the Origin header, strictly validate it against the allow-list. Do not simply echo back any origin.
  4. Least Privilege: Only allow the specific HTTP methods (Access-Control-Allow-Methods) and headers (Access-Control-Allow-Headers) required by the trusted origins.
  5. Vary Header: Consider using the Vary: Origin header to prevent caching issues when serving different Access-Control-Allow-Origin headers based on the request origin.

  • Python
  • Java
  • .NET(C#)
  • PHP
  • Node.js
  • Ruby

Framework Context

Using libraries like django-cors-headers (Django) or Flask-CORS (Flask) with insecure configurations.

Vulnerable Scenario 1: Django CORS_ALLOW_ALL_ORIGINS = True

# settings.py (Django)

INSTALLED_APPS = [ ..., 'corsheaders', ... ]
MIDDLEWARE = [ ..., 'corsheaders.middleware.CorsMiddleware', ... ]

# DANGEROUS: Allows any origin to make requests.
# If ALLOW_CREDENTIALS is True (or default), sensitive data can be read.
CORS_ALLOW_ALL_ORIGINS = True
# CORS_ALLOW_CREDENTIALS = True # Defaults to False, but True makes '*' deadly

Vulnerable Scenario 2: Flask-CORS Allowing Any Origin

# app.py (Flask)
from flask import Flask, jsonify
from flask_cors import CORS

app = Flask(__name__)
# DANGEROUS: origins="*" allows any domain.
# supports_credentials=True makes this exploitable for authenticated endpoints.
CORS(app, resources={r"/api/*": {"origins": "*", "supports_credentials": True}})

@app.route('/api/user/profile')
# Assume requires login via session cookie
def get_user_profile():
    # Attacker on evil.com uses victim's browser (with valid cookie)
    # to fetch this data via JavaScript. CORS allows the read.
    user_data = get_sensitive_user_data()
    return jsonify(user_data)

Mitigation and Best Practices

  • Django: Set CORS_ALLOW_ALL_ORIGINS = False. Define CORS_ALLOWED_ORIGINS (list of specific domains like https://trusted.example.com) or CORS_ALLOWED_ORIGIN_REGEXES. Set CORS_ALLOW_CREDENTIALS = True only if necessary and origins are strictly controlled.
  • Flask: Replace "origins": "*" with a specific list: "origins": ["https://trusted.example.com", "https://another.trusted.com"]. Only set supports_credentials=True if absolutely needed and origins are restricted.

Secure Code Example

# settings.py (Django - Secure)
CORS_ALLOW_ALL_ORIGINS = False # SECURE
# SECURE: Explicitly list trusted origins
CORS_ALLOWED_ORIGINS = [
    "[https://trusted.frontend.com](https://trusted.frontend.com)",
    "[https://api-consumer.internal](https://api-consumer.internal)",
]
# Allow credentials only if needed by trusted origins
CORS_ALLOW_CREDENTIALS = True
# app.py (Flask - Secure)
# SECURE: List specific trusted origins
trusted_origins = ["[https://trusted.frontend.com](https://trusted.frontend.com)", "[https://sub.trusted.com](https://sub.trusted.com)"]
CORS(app, resources={r"/api/*": {"origins": trusted_origins, "supports_credentials": True}})

Testing Strategy

Use curl or browser developer tools to send requests to your API endpoints from a different origin (e.g., set the Origin header manually in curl).
  • Check Access-Control-Allow-Origin. Is it *? Is it reflecting an untrusted origin?
  • Check Access-Control-Allow-Credentials. Is it true?
  • If both Origin: * and Credentials: true are returned (or if an untrusted origin is reflected with Credentials: true), the configuration is vulnerable. Test by making a JavaScript fetch request from a dummy HTML page served on a different domain to see if you can read authenticated data.