Documentation

# CORS (Cross-Origin Resource Sharing) Misconfiguration

CORS Attack Illustration
CORS Attack Illustration

A security vulnerability where improper CORS configuration allows unauthorized websites to access sensitive data from your API or web application.

CRITICAL VULNERABILITY API SECURITY OWASP TOP 10


# What is CORS Misconfiguration?

In Simple Terms:

Imagine you have a bank vault (your API) that should only allow authorized bank employees (your website) to access customer records. CORS is like the security guard who checks IDs.

A CORS misconfiguration is like the security guard saying: "Anyone can enter! I don't care who you are!"

Now a thief (malicious website) can walk in, pretend to be a bank employee, and steal customer information while the real customer is logged into their online banking.


# Real-World Analogy

Think of CORS like a shared office building's access control system:

Your API server is like an office building with valuable data inside.

Your legitimate website (example.com) is like an authorized tenant with a valid keycard.

CORS is the security guard who checks if visitors are allowed to access specific offices.

Misconfigured CORS is like the security guard who:

  • Lets ANYONE into ANY office (Access-Control-Allow-Origin: *)
  • Accepts fake keycards (Trusts any origin that claims to be authorized)
  • Doesn't verify credentials (Allows credentials with wildcard origins)

# How CORS Misconfiguration Works

# The Attack Process

Victim browses to evil-site.com while logged into bank.com in another tab.

Evil site makes JavaScript request to bank.com/api/account-details using victim's cookies.

Browser sends preflight request asking: "Can evil-site.com access bank.com API?"

Bank's API responds: Access-Control-Allow-Origin: * or reflects the origin back.

Browser allows the request, evil site receives victim's account data and sends it to attacker.


# Types of CORS Misconfigurations

# 1. Wildcard Origin with Credentials

CRITICAL

Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

# 2. Reflected Origin Vulnerability

HIGH RISK

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/api/user-data')
def get_user_data():
    origin = request.headers.get('Origin')

    # VULNERABLE: Reflects any origin back
    response = jsonify({'email': 'user@example.com', 'balance': 50000})
    response.headers['Access-Control-Allow-Origin'] = origin
    response.headers['Access-Control-Allow-Credentials'] = 'true'

    return response
app.get('/api/user-data', (req, res) => {
  const origin = req.headers.origin;

  // VULNERABLE: Reflects any origin
  res.setHeader('Access-Control-Allow-Origin', origin);
  res.setHeader('Access-Control-Allow-Credentials', 'true');

  res.json({ email: 'user@example.com', balance: 50000 });
});
<?php
$origin = $_SERVER['HTTP_ORIGIN'];

// VULNERABLE: Accepts any origin
header("Access-Control-Allow-Origin: $origin");
header("Access-Control-Allow-Credentials: true");

echo json_encode([
    'email' => 'user@example.com',
    'balance' => 50000
]);
?>

# 3. Weak Origin Validation

MEDIUM RISK

# VULNERABLE: Weak regex validation
origin = request.headers.get('Origin')

# This accepts: evil-example.com, example.com.evil.com, etc.
if 'example.com' in origin:
    response.headers['Access-Control-Allow-Origin'] = origin
    response.headers['Access-Control-Allow-Credentials'] = 'true'

# 4. Null Origin Acceptance

MEDIUM RISK

// VULNERABLE: Accepts null origin
const origin = req.headers.origin;

if (origin === 'null') {
  res.setHeader('Access-Control-Allow-Origin', 'null');
  res.setHeader('Access-Control-Allow-Credentials', 'true');
}

# 5. Pre-Domain Wildcard

MEDIUM RISK

Access-Control-Allow-Origin: *.example.com

# Real-World Examples

# Case Study 1: Bitcoin Exchange API Leak (2018)

A major cryptocurrency exchange had CORS misconfiguration on their trading API.

Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: *

Attackers created phishing sites that made requests to the exchange's API, stealing:

  • User balances
  • Trading history
  • API keys
  • Wallet addresses
  • $2.3M in cryptocurrency stolen
  • 10,000+ user accounts compromised
  • Exchange forced to halt trading for 72 hours
  • $15M in compensation paid to affected users

# Case Study 2: Social Media Platform Data Breach (2019)

150M Users Affected

Technical Details:

# Their vulnerable code
@app.route('/api/messages')
def get_messages():
    origin = request.headers.get('Origin')
    response = jsonify(get_user_messages())
    response.headers['Access-Control-Allow-Origin'] = origin  # Reflected!
    response.headers['Access-Control-Allow-Credentials'] = 'true'
    return response

Consequences:

  • 150 million user accounts affected
  • Private messages and photos exposed
  • $5 billion FTC fine
  • Congressional hearings on data privacy

# Case Study 3: Healthcare Portal CORS Issue (2020)

Electronic health records (EHR) system with vulnerable API

// Vulnerable whitelist check
const allowedOrigins = [
  'hospital.com',
  'patient-portal.com'
];

const origin = req.headers.origin;
// VULNERABLE: Substring match instead of exact match
if (allowedOrigins.some(allowed => origin.includes(allowed))) {
  res.setHeader('Access-Control-Allow-Origin', origin);
  res.setHeader('Access-Control-Allow-Credentials', 'true');
}

Attacker registered: hospital.com.evil-phishing.com

  • 45,000 patient records exposed
  • HIPAA violations: $4.5M fine
  • Class action lawsuit: $12M settlement

# How to Detect CORS Misconfigurations

# Manual Testing

Use browser DevTools or Burp Suite to capture an API request

Send request with malicious origin:

GET /api/sensitive-data HTTP/1.1
Host: target.com
Origin: https://evil.com
Cookie: session=abc123

Look for:

Access-Control-Allow-Origin: https://evil.com
Access-Control-Allow-Credentials: true

If present, you can exfiltrate data from JavaScript on evil.com

# Testing Script

import requests

def test_cors_misconfiguration(target_url):
    """Test for CORS misconfigurations"""

    test_origins = [
        'https://evil.com',
        'null',
        'https://target.com.evil.com',
        'https://evil-target.com'
    ]

    vulnerabilities = []

    for origin in test_origins:
        headers = {
            'Origin': origin,
            'Cookie': 'session=test123'  # Use your session cookie
        }

        response = requests.get(target_url, headers=headers)

        allow_origin = response.headers.get('Access-Control-Allow-Origin')
        allow_creds = response.headers.get('Access-Control-Allow-Credentials')

        if allow_origin and allow_creds == 'true':
            if allow_origin == origin or allow_origin == '*':
                vulnerabilities.append({
                    'origin': origin,
                    'allowed': allow_origin,
                    'credentials': allow_creds
                })
                print(f"[!] VULNERABLE: {origin} is allowed with credentials!")

    return vulnerabilities

# Test your API
vulns = test_cors_misconfiguration('https://api.example.com/user-data')
if vulns:
    print(f"\n[!] Found {len(vulns)} CORS misconfigurations!")
else:
    print("[+] No CORS vulnerabilities detected")
const axios = require('axios');

async function testCORSMisconfiguration(targetUrl) {
  const testOrigins = [
    'https://evil.com',
    'null',
    'https://target.com.evil.com',
    'https://evil-target.com'
  ];

  const vulnerabilities = [];

  for (const origin of testOrigins) {
    try {
      const response = await axios.get(targetUrl, {
        headers: {
          'Origin': origin,
          'Cookie': 'session=test123'
        }
      });

      const allowOrigin = response.headers['access-control-allow-origin'];
      const allowCreds = response.headers['access-control-allow-credentials'];

      if (allowOrigin && allowCreds === 'true') {
        if (allowOrigin === origin || allowOrigin === '*') {
          vulnerabilities.push({
            origin,
            allowed: allowOrigin,
            credentials: allowCreds
          });
          console.log(`[!] VULNERABLE: ${origin} is allowed with credentials!`);
        }
      }
    } catch (error) {
      console.log(`[-] Error testing ${origin}:`, error.message);
    }
  }

  return vulnerabilities;
}

// Test your API
testCORSMisconfiguration('https://api.example.com/user-data')
  .then(vulns => {
    if (vulns.length > 0) {
      console.log(`\n[!] Found ${vulns.length} CORS misconfigurations!`);
    } else {
      console.log('[+] No CORS vulnerabilities detected');
    }
  });

# Automated Tools


# Prevention Strategies

# 1. Strict Whitelist Validation

RECOMMENDED

from flask import Flask, request, jsonify
from urllib.parse import urlparse

app = Flask(__name__)

# Strict whitelist of allowed origins
ALLOWED_ORIGINS = [
    'https://www.example.com',
    'https://app.example.com',
    'https://mobile.example.com'
]

def validate_origin(origin):
    """Validate origin with exact match"""
    if not origin:
        return False

    # Parse and validate
    try:
        parsed = urlparse(origin)

        # Exact match only (no substrings!)
        if origin in ALLOWED_ORIGINS:
            return True

        return False
    except Exception:
        return False

@app.route('/api/user-data')
def get_user_data():
    origin = request.headers.get('Origin')

    response = jsonify({
        'email': 'user@example.com',
        'balance': 50000
    })

    # Only set CORS headers if origin is whitelisted
    if validate_origin(origin):
        response.headers['Access-Control-Allow-Origin'] = origin
        response.headers['Access-Control-Allow-Credentials'] = 'true'
        response.headers['Vary'] = 'Origin'  # Important for caching!

    return response

@app.route('/api/user-data', methods=['OPTIONS'])
def preflight():
    """Handle CORS preflight"""
    origin = request.headers.get('Origin')

    if not validate_origin(origin):
        return '', 403  # Forbidden

    response = app.make_response('')
    response.headers['Access-Control-Allow-Origin'] = origin
    response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE'
    response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
    response.headers['Access-Control-Allow-Credentials'] = 'true'
    response.headers['Access-Control-Max-Age'] = '86400'  # 24 hours
    response.headers['Vary'] = 'Origin'

    return response
const express = require('express');
const cors = require('cors');

const app = express();

// Strict whitelist
const ALLOWED_ORIGINS = [
  'https://www.example.com',
  'https://app.example.com',
  'https://mobile.example.com'
];

// CORS configuration
const corsOptions = {
  origin: function (origin, callback) {
    // Allow requests with no origin (mobile apps, Postman, etc.)
    if (!origin) {
      return callback(null, true);
    }

    // Exact match validation
    if (ALLOWED_ORIGINS.indexOf(origin) !== -1) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true, // Allow credentials
  optionsSuccessStatus: 200,
  maxAge: 86400 // 24 hours
};

// Apply CORS middleware
app.use(cors(corsOptions));

app.get('/api/user-data', (req, res) => {
  res.json({
    email: 'user@example.com',
    balance: 50000
  });
});

app.listen(3000);
<?php
// Strict whitelist
$allowedOrigins = [
    'https://www.example.com',
    'https://app.example.com',
    'https://mobile.example.com'
];

$origin = $_SERVER['HTTP_ORIGIN'] ?? '';

// Validate origin with exact match
if (in_array($origin, $allowedOrigins, true)) {
    header("Access-Control-Allow-Origin: $origin");
    header("Access-Control-Allow-Credentials: true");
    header("Vary: Origin");

    // Handle preflight
    if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
        header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE");
        header("Access-Control-Allow-Headers: Content-Type, Authorization");
        header("Access-Control-Max-Age: 86400");
        http_response_code(200);
        exit;
    }
} else {
    http_response_code(403);
    exit('Origin not allowed');
}

// Return data
echo json_encode([
    'email' => 'user@example.com',
    'balance' => 50000
]);
?>
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

public class CORSFilter implements Filter {

    private static final List<String> ALLOWED_ORIGINS = Arrays.asList(
        "https://www.example.com",
        "https://app.example.com",
        "https://mobile.example.com"
    );

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                        FilterChain chain) throws IOException, ServletException {

        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;

        String origin = req.getHeader("Origin");

        // Validate origin with exact match
        if (origin != null && ALLOWED_ORIGINS.contains(origin)) {
            res.setHeader("Access-Control-Allow-Origin", origin);
            res.setHeader("Access-Control-Allow-Credentials", "true");
            res.setHeader("Vary", "Origin");

            // Handle preflight
            if ("OPTIONS".equalsIgnoreCase(req.getMethod())) {
                res.setHeader("Access-Control-Allow-Methods",
                             "GET, POST, PUT, DELETE");
                res.setHeader("Access-Control-Allow-Headers",
                             "Content-Type, Authorization");
                res.setHeader("Access-Control-Max-Age", "86400");
                res.setStatus(HttpServletResponse.SC_OK);
                return;
            }
        } else if (origin != null) {
            res.setStatus(HttpServletResponse.SC_FORBIDDEN);
            return;
        }

        chain.doFilter(request, response);
    }
}

# 2. Environment-Based Configuration

[config.py]
import os

class CORSConfig:
    """Environment-based CORS configuration"""

    PRODUCTION_ORIGINS = [
        'https://www.example.com',
        'https://app.example.com'
    ]

    STAGING_ORIGINS = [
        'https://staging.example.com',
        'https://staging-app.example.com'
    ]

    DEVELOPMENT_ORIGINS = [
        'http://localhost:3000',
        'http://localhost:8080',
        'http://127.0.0.1:3000'
    ]

    @staticmethod
    def get_allowed_origins():
        env = os.getenv('ENVIRONMENT', 'development')

        if env == 'production':
            return CORSConfig.PRODUCTION_ORIGINS
        elif env == 'staging':
            return CORSConfig.STAGING_ORIGINS
        else:
            return CORSConfig.DEVELOPMENT_ORIGINS
[.env.production]
ENVIRONMENT=production
ALLOWED_ORIGINS=https://www.example.com,https://app.example.com
[.env.development]
ENVIRONMENT=development
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:8080

# 3. Security Headers Best Practices

def add_security_headers(response):
    """Add comprehensive security headers"""

    # CORS headers (already set based on whitelist)
    # response.headers['Access-Control-Allow-Origin'] = validated_origin
    # response.headers['Access-Control-Allow-Credentials'] = 'true'

    # Important: Vary header for caching
    response.headers['Vary'] = 'Origin'

    # Additional security headers
    response.headers['X-Content-Type-Options'] = 'nosniff'
    response.headers['X-Frame-Options'] = 'DENY'
    response.headers['X-XSS-Protection'] = '1; mode=block'
    response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'

    # Content Security Policy
    response.headers['Content-Security-Policy'] = "default-src 'self'"

    return response

# 4. Avoid Credentials with Public APIs

// Public API - no credentials needed
app.use(cors({
  origin: '*',  // Allow all origins
  credentials: false  // NO credentials - this is safe for public data
}));

app.get('/api/public-data', (req, res) => {
  // No authentication required
  res.json({ publicInfo: 'This is public data' });
});

# Security Checklist

# Development Phase

  • Define strict whitelist of allowed origins
  • Use exact match validation (no substring matching)
  • Never reflect Origin header without validation
  • Never use Access-Control-Allow-Origin: * with credentials
  • Implement proper preflight (OPTIONS) request handling
  • Add Vary: Origin header for proper caching
  • Use environment-based configuration
  • Implement comprehensive error logging

# Testing Phase

  • Test with malicious origins (evil.com, null, etc.)
  • Test with subdomain variations (target.com.evil.com)
  • Verify wildcard (*) is not used with credentials
  • Test preflight request handling
  • Verify exact match validation (not substring)
  • Test with different HTTP methods (GET, POST, PUT, DELETE)
  • Test credential handling with cross-origin requests
  • Use automated CORS scanners (Burp Suite, ZAP)

# Production Deployment

  • Review and minimize allowed origins list
  • Enable CORS logging and monitoring
  • Set up alerts for CORS violations
  • Implement rate limiting on API endpoints
  • Use Web Application Firewall (WAF) with CORS rules
  • Regular security audits and penetration testing
  • Keep CORS middleware/libraries updated
  • Document CORS policy and exceptions

# Testing Tools & Resources

# Professional Tools

CORS Scanner Extension

  • Automated CORS misconfiguration detection
  • Preflight request analysis
  • Credential-based testing
  • Download Burp Suite

Free Open-Source Scanner

  • Active/passive CORS scanning
  • API endpoint testing
  • Report generation
  • Download ZAP

Dedicated CORS Testing

npm install -g corstest
corstest https://api.example.com/endpoint

# Online Resources


# Key Takeaways


# How Layerd AI Protects Against CORS Attacks

Layerd AI's advanced security platform provides comprehensive protection against CORS misconfigurations:

  • Automatic CORS Analysis: Continuously monitors and validates CORS configurations across all API endpoints
  • Origin Validation: Enforces strict whitelist validation with exact matching
  • Credential Protection: Detects and prevents dangerous credential + wildcard combinations
  • Real-time Monitoring: Alerts on suspicious cross-origin requests and potential data exfiltration
  • Configuration Hardening: Automatically suggests secure CORS configurations based on your application needs
  • Compliance Enforcement: Ensures CORS policies meet security standards and regulatory requirements

Protect your APIs from CORS attacks with Layerd AI's intelligent security platform.


# Additional Resources


Last Updated: November 2025