Home/Blog/CORS Security Guide: Preventing Cross-Origin Attacks and

CORS Security Guide: Preventing Cross-Origin Attacks and

Learn how to implement secure CORS policies, avoid common misconfigurations like wildcard origins and origin reflection, and protect your APIs from cross-origin attacks.

By Inventive HQ Team
CORS Security Guide: Preventing Cross-Origin Attacks and

Cross-Origin Resource Sharing (CORS) is one of the most misunderstood and misconfigured security mechanisms in web applications. According to recent security research, nearly 90% of API breaches involve misconfigured CORS policies. This guide explains how CORS works, common vulnerabilities, and how to implement secure configurations.

What is CORS and Why Does It Matter?

CORS is a browser security mechanism that controls how web pages from one origin (domain) can request resources from another origin. Without CORS, the browser's same-origin policy would block all cross-origin requests, making modern web applications impossible.

Here's what happens during a cross-origin request:

  1. Browser sends request with Origin header
  2. Server responds with Access-Control-Allow-Origin header
  3. Browser compares origins and allows or blocks the response
Request:
GET /api/data HTTP/1.1
Host: api.example.com
Origin: https://app.example.com

Response:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true

Key CORS Headers Explained

HeaderPurposeExample
Access-Control-Allow-OriginSpecifies allowed originshttps://example.com or *
Access-Control-Allow-CredentialsAllows cookies/auth headerstrue
Access-Control-Allow-MethodsAllowed HTTP methodsGET, POST, PUT
Access-Control-Allow-HeadersAllowed request headersContent-Type, Authorization
Access-Control-Max-AgePreflight cache duration (seconds)86400
VaryCache key for responsesOrigin

Understanding Preflight Requests

Browsers send preflight OPTIONS requests before "complex" cross-origin requests. A request is considered complex if it:

  • Uses methods other than GET, HEAD, or POST
  • Includes custom headers
  • Uses Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain
Preflight Request:
OPTIONS /api/data HTTP/1.1
Host: api.example.com
Origin: https://app.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type, Authorization

Preflight Response:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400

Common CORS Misconfigurations

1. Wildcard Origin with Credentials (Critical)

The vulnerability:

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

This configuration is impossible according to the CORS specification - browsers reject it. However, many developers work around this by reflecting any origin, which is even worse.

Why it's dangerous: Any website can make authenticated requests to your API using the victim's cookies.

Attack scenario:

  1. User is logged into your application
  2. User visits attacker's website
  3. Attacker's JavaScript makes requests to your API
  4. Browser includes user's cookies automatically
  5. Attacker receives sensitive data from your API

2. Origin Reflection (Critical)

The vulnerability:

# Dangerous: Reflects any origin
allowed_origin = request.headers.get('Origin')
response.headers['Access-Control-Allow-Origin'] = allowed_origin
response.headers['Access-Control-Allow-Credentials'] = 'true'

Why it's dangerous: The server accepts ANY origin, completely bypassing CORS protection.

Secure alternative:

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

origin = request.headers.get('Origin')
if origin in ALLOWED_ORIGINS:
    response.headers['Access-Control-Allow-Origin'] = origin
    response.headers['Access-Control-Allow-Credentials'] = 'true'
    response.headers['Vary'] = 'Origin'

3. Null Origin Acceptance (High)

The vulnerability:

Access-Control-Allow-Origin: null

Why it's dangerous: The null origin can be triggered from:

  • Sandboxed iframes (<iframe sandbox="allow-scripts">)
  • Local file:// URLs
  • Data URLs
  • Redirects from HTTP to HTTPS

Attackers can craft pages that send requests with a null origin.

4. Trusting All Subdomains (Medium)

The vulnerability:

origin = request.headers.get('Origin')
if origin and origin.endswith('.example.com'):
    # Accepts evil.example.com, anything.example.com, etc.
    response.headers['Access-Control-Allow-Origin'] = origin

Why it's dangerous: If any subdomain is compromised (XSS, subdomain takeover), attackers can access your API.

Secure alternative:

ALLOWED_SUBDOMAINS = {'app.example.com', 'api.example.com'}

parsed = urlparse(origin)
if parsed.netloc in ALLOWED_SUBDOMAINS:
    response.headers['Access-Control-Allow-Origin'] = origin

5. Missing Vary Header (Medium)

The vulnerability: Not including Vary: Origin when the CORS response varies by origin.

Why it's dangerous: CDNs and browsers may cache responses with the wrong CORS headers, causing legitimate requests to fail or allowing unauthorized access.

Fix: Always include Vary: Origin when your CORS response depends on the request origin:

Vary: Origin

Implementing Secure CORS

Step 1: Define Your Origin Whitelist

// Express.js example
const allowedOrigins = new Set([
  'https://app.example.com',
  'https://admin.example.com',
  'https://mobile.example.com'
]);

Step 2: Validate Origins Strictly

const cors = require('cors');

const corsOptions = {
  origin: (origin, callback) => {
    // Allow requests with no origin (mobile apps, curl)
    if (!origin) return callback(null, true);

    if (allowedOrigins.has(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  maxAge: 86400 // 24 hours
};

app.use(cors(corsOptions));

Step 3: Restrict Methods and Headers

Only allow methods and headers your API actually needs:

methods: ['GET', 'POST'],  // Not PUT, DELETE unless needed
allowedHeaders: ['Content-Type', 'Authorization']

Step 4: Add Vary Header

Ensure proper caching behavior:

app.use((req, res, next) => {
  res.header('Vary', 'Origin');
  next();
});

Step 5: Set Reasonable Max-Age

Balance security with performance:

maxAge: 86400  // 24 hours - re-validate daily

Testing Your CORS Configuration

Using curl

# Test basic CORS
curl -I -X OPTIONS https://api.example.com/endpoint \
  -H "Origin: https://evil.com" \
  -H "Access-Control-Request-Method: GET"

# Test with credentials
curl -I https://api.example.com/endpoint \
  -H "Origin: https://evil.com" \
  -H "Cookie: session=abc123"

What to Check

  1. Wildcard with credentials: Does the API return * with credentials: true?
  2. Origin reflection: Does the API echo back any origin you send?
  3. Null origin: Does the API accept Origin: null?
  4. Subdomain bypass: Does evil.example.com get accepted?
  5. Method exposure: Are dangerous methods (DELETE, PATCH) allowed?
  6. Vary header: Is Vary: Origin present?

Automated Testing

Include CORS tests in your CI/CD pipeline:

describe('CORS Security', () => {
  it('should reject unauthorized origins', async () => {
    const response = await fetch('https://api.example.com/data', {
      headers: { 'Origin': 'https://evil.com' }
    });
    expect(response.headers.get('Access-Control-Allow-Origin'))
      .not.toBe('https://evil.com');
  });

  it('should not reflect arbitrary origins', async () => {
    const response = await fetch('https://api.example.com/data', {
      headers: { 'Origin': 'https://random-attacker.com' }
    });
    expect(response.headers.get('Access-Control-Allow-Origin'))
      .toBeNull();
  });
});

Framework-Specific Configurations

Express.js

const cors = require('cors');

app.use(cors({
  origin: ['https://app.example.com'],
  credentials: true,
  methods: ['GET', 'POST'],
  maxAge: 86400
}));

Django

# settings.py
CORS_ALLOWED_ORIGINS = [
    "https://app.example.com",
]
CORS_ALLOW_CREDENTIALS = True
CORS_PREFLIGHT_MAX_AGE = 86400

Flask

from flask_cors import CORS

CORS(app,
     origins=['https://app.example.com'],
     supports_credentials=True,
     max_age=86400)

Spring Boot

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
            .allowedOrigins("https://app.example.com")
            .allowCredentials(true)
            .allowedMethods("GET", "POST")
            .maxAge(86400);
    }
}

CORS Security Checklist

  • Origin whitelist defined (no wildcards in production)
  • No origin reflection (server-side validation, not echo)
  • Null origin rejected
  • Subdomain validation uses exact matching
  • Credentials only for specific trusted origins
  • HTTP methods restricted to minimum needed
  • Custom headers restricted to minimum needed
  • Vary: Origin header present
  • Max-Age set to reasonable value (≤24 hours)
  • CORS configuration tested in CI/CD
  • Regular security audits include CORS review

Conclusion

CORS misconfigurations are among the most common and dangerous web security vulnerabilities. By implementing strict origin whitelists, avoiding origin reflection, and testing your configuration regularly, you can protect your APIs from cross-origin attacks.

Use our CORS Policy Analyzer to test your current configuration and identify vulnerabilities before attackers do.

Let's turn this knowledge into action

Get a free 30-minute consultation with our experts. We'll help you apply these insights to your specific situation.

How to Extract and Analyze Cookies from Your Browser

How to Extract and Analyze Cookies from Your Browser

Learn how to view, export, and analyze HTTP cookies from Chrome, Firefox, Edge, and Safari using browser DevTools. Includes security analysis tips.

API Development & Security Testing Workflow: OWASP API Security Top 10 Guide

API Development & Security Testing Workflow: OWASP API Security Top 10 Guide

Build secure APIs with this 7-stage workflow covering design, authentication, development, security testing, integration testing, deployment, and monitoring. Includes OWASP API Top 10 2023 coverage, OAuth 2.0, JWT, rate limiting, and webhook security.

The Complete Developer Debugging & Data Transformation Workflow

The Complete Developer Debugging & Data Transformation Workflow

Reduce debugging time by 50% with this systematic 7-stage workflow. Learn error detection, log analysis, data format validation, API debugging, SQL optimization, regex testing, and documentation strategies with 10 integrated developer tools.

Incident Response & Forensics Investigation Workflow: NIST & SANS Framework Guide

Incident Response & Forensics Investigation Workflow: NIST & SANS Framework Guide

Learn the complete incident response workflow following NIST SP 800-61r3 and SANS 6-step methodology. From preparation to post-incident analysis, this guide covers evidence preservation, forensic collection, threat intelligence, and compliance reporting.

Email Security Hardening & Deliverability: The 13-Week SPF, DKIM, DMARC Implementation Guide

Email Security Hardening & Deliverability: The 13-Week SPF, DKIM, DMARC Implementation Guide

Implement email authentication following Google and Yahoo 2025 requirements. This phased 13-week deployment guide covers SPF optimization, DKIM key rotation, DMARC policy enforcement, deliverability testing, and advanced protections like BIMI and MTA-STS.

Infrastructure-as-Code Security & Change Management: Terraform Best Practices 2025

Infrastructure-as-Code Security & Change Management: Terraform Best Practices 2025

Implement secure IaC workflows with Terraform following 2025 best practices. This comprehensive guide covers pre-commit validation, security scanning with tfsec/Checkov, policy-as-code enforcement, automated testing, drift detection, and cost optimization.