Documentation

CORS (Cross-Origin Resource Sharing) Misconfiguration

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?

Danger Alert

CORS misconfigurations can expose your users' sensitive data to malicious websites, leading to account takeovers, data theft, and privacy breaches. This is one of the most common API security issues.

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

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

Most Dangerous Configuration

This combination is supposed to be blocked by browsers, but older browsers or poorly configured proxies might allow it, exposing all authenticated user data.

2. Reflected Origin Vulnerability

HIGH RISK

Python (Vulnerable)
pythonfrom 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': '[email protected]', 'balance': 50000})
    response.headers['Access-Control-Allow-Origin'] = origin
    response.headers['Access-Control-Allow-Credentials'] = 'true'

    return response
Node.js (Vulnerable)
javascriptapp.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: '[email protected]', balance: 50000 });
});
PHP (Vulnerable)
php<?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' => '[email protected]',
    'balance' => 50000
]);
?>

3. Weak Origin Validation

MEDIUM RISK

python# 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

javascript// 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');
}

Null Origin Attack

Attackers can send requests with Origin: null using sandboxed iframes or data URIs, bypassing origin checks.

5. Pre-Domain Wildcard

MEDIUM RISK

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

Browser Limitation

Browsers don't support subdomain wildcards in CORS headers. However, some CDNs or proxies might implement this incorrectly.


Real-World Examples

Case Study 1: Bitcoin Exchange API Leak (2018)

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

httpAccess-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

Major Privacy Breach

Reflected origin vulnerability allowed malicious websites to access private user messages, photos, and contact lists.

Technical Details:

python# 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

javascript// 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:

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

Look for:

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

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

Testing Script

Python
pythonimport 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")
Node.js
javascriptconst 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

Security Testing Tools

  • Burp Suite CORS Scanner - Professional web security testing
  • OWASP ZAP - Free automated CORS testing
  • CORStest - Dedicated CORS misconfiguration scanner
  • cors-scanner - CLI tool for batch testing

Prevention Strategies

1. Strict Whitelist Validation

RECOMMENDED

Python (Secure)
pythonfrom 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': '[email protected]',
        '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
Node.js (Secure)
javascriptconst 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: '[email protected]',
    balance: 50000
  });
});

app.listen(3000);
PHP (Secure)
php<?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' => '[email protected]',
    'balance' => 50000
]);
?>
Java (Secure)
javaimport 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

pythonimport 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

3. Security Headers Best Practices

pythondef 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 Best Practice

For public APIs that don't need authentication, avoid using credentials entirely:

javascript// 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

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

Online Resources


Key Takeaways

Remember These Critical Points

  1. Never use wildcard with credentials: Access-Control-Allow-Origin: * + Access-Control-Allow-Credentials: true is dangerous

  2. Use exact match validation: Don't check if origin contains your domain - use exact match from whitelist

  3. Don't reflect origins blindly: Never echo back the Origin header without strict validation

  4. Reject null origin: Origin: null can be exploited via sandboxed iframes

  5. Add Vary header: Always include Vary: Origin for proper caching behavior

  6. Minimize origin whitelist: Only add origins that absolutely need access

  7. Monitor CORS violations: Log and alert on rejected CORS requests

  8. Test thoroughly: Use both manual and automated testing tools


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