Skip to main content
SecScannerSecScanner
FeaturesCompliancePricingBlogContact
Sign InGet Started
← Back to Blog
HeadersJanuary 27, 202511 min read

CORS Security: The Complete Guide to Cross-Origin Resource Sharing

Understand CORS from a security perspective. Learn how misconfigurations lead to data theft, and implement secure cross-origin policies that protect your users and APIs.

By SecScanner Team
CORS Security: The Complete Guide to Cross-Origin Resource Sharing

Cross-Origin Resource Sharing (CORS) is one of the most misunderstood web security mechanisms. Implemented correctly, it enables secure cross-origin requests. Implemented incorrectly, it can expose your users' data to attackers. This guide explains CORS security from the ground up.

The Same-Origin Policy Foundation

Before understanding CORS, you must understand the Same-Origin Policy (SOP). This fundamental browser security mechanism prevents scripts on one origin from accessing data on another origin.

What Defines an Origin?

An origin consists of three components:

  • Protocol: http vs https
  • Host: example.com vs api.example.com
  • Port: :80 vs :8080

If any component differs, the origins are different. https://example.com and https://api.example.com are different origins.

What SOP Blocks

  • Reading responses from cross-origin fetch/XHR requests
  • Accessing cross-origin iframe content
  • Reading cross-origin canvas data

What SOP Allows

  • Loading cross-origin images, scripts, stylesheets
  • Submitting forms to cross-origin targets
  • Embedding cross-origin media

How CORS Works

CORS relaxes the Same-Origin Policy in a controlled way. It allows servers to specify which origins can access their resources.

Simple Requests

For "simple" requests (GET, HEAD, POST with standard content types), the browser adds an Origin header:

GET /api/data HTTP/1.1
Origin: https://example.com

The server responds with CORS headers:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com

If the origin matches, the browser allows JavaScript to access the response.

Preflight Requests

For non-simple requests (PUT, DELETE, custom headers, non-standard content types), the browser first sends an OPTIONS preflight:

OPTIONS /api/data HTTP/1.1
Origin: https://example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header

The server must explicitly allow the method and headers:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: PUT, POST, DELETE
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Max-Age: 86400

CORS Response Headers

Access-Control-Allow-Origin

Specifies which origins can access the resource:

  • * - Any origin (public APIs only)
  • https://example.com - Specific origin
  • Dynamic value based on request Origin header

Access-Control-Allow-Credentials

When set to true, allows requests with cookies and authentication headers. Cannot be used with Access-Control-Allow-Origin: *

Access-Control-Allow-Methods

Lists allowed HTTP methods for preflight responses.

Access-Control-Allow-Headers

Lists allowed request headers for preflight responses.

Access-Control-Expose-Headers

Lists response headers that JavaScript can access. By default, only "simple" response headers are exposed.

Access-Control-Max-Age

How long preflight results can be cached (in seconds).

CORS Security Vulnerabilities

Vulnerability 1: Reflecting Origin Without Validation

The most dangerous misconfiguration:

// DANGEROUS: Don't do this!
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', req.headers.origin);
  res.header('Access-Control-Allow-Credentials', 'true');
  next();
});

This allows any website to make authenticated requests to your API and read the responses. An attacker can steal user data by getting victims to visit a malicious page.

Vulnerability 2: Weak Origin Validation

// DANGEROUS: Substring matching
const origin = req.headers.origin;
if (origin.includes('example.com')) {
  res.header('Access-Control-Allow-Origin', origin);
}

An attacker can use attacker-example.com or example.com.attacker.com to bypass this check.

Vulnerability 3: Null Origin Acceptance

// DANGEROUS: Accepting null origin
if (origin === 'null' || allowedOrigins.includes(origin)) {
  res.header('Access-Control-Allow-Origin', origin);
}

The null origin can be triggered by sandboxed iframes, local files, and redirects. Never trust it.

Vulnerability 4: Wildcard with Credentials

Browsers block this combination, but server misconfigurations can still cause issues:

Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true  // Browser will reject!

Secure CORS Implementation

Use an Allowlist

const allowedOrigins = new Set([
  'https://example.com',
  'https://app.example.com',
  'https://staging.example.com'
]);

app.use((req, res, next) => {
  const origin = req.headers.origin;

  if (allowedOrigins.has(origin)) {
    res.header('Access-Control-Allow-Origin', origin);
    res.header('Access-Control-Allow-Credentials', 'true');
  }

  next();
});

Validate Origin Properly

function isAllowedOrigin(origin) {
  if (!origin) return false;

  try {
    const url = new URL(origin);
    // Exact match or subdomain of allowed domain
    return url.hostname === 'example.com' ||
           url.hostname.endsWith('.example.com');
  } catch {
    return false;
  }
}

Handle Preflight Correctly

app.options('*', (req, res) => {
  const origin = req.headers.origin;

  if (isAllowedOrigin(origin)) {
    res.header('Access-Control-Allow-Origin', origin);
    res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
    res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
    res.header('Access-Control-Allow-Credentials', 'true');
    res.header('Access-Control-Max-Age', '86400');
  }

  res.status(204).end();
});

CORS for Different Scenarios

Public API (No Authentication)

Access-Control-Allow-Origin: *

Safe for truly public data that doesn't require authentication.

Authenticated API (Single App)

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

Authenticated API (Multiple Apps)

// Dynamic, validated origin
const origin = req.headers.origin;
if (allowedOrigins.has(origin)) {
  res.header('Access-Control-Allow-Origin', origin);
  res.header('Access-Control-Allow-Credentials', 'true');
  res.header('Vary', 'Origin');  // Important for caching!
}

Third-Party Widget Integration

// Allow specific partners
Access-Control-Allow-Origin: https://partner-site.com
Access-Control-Allow-Methods: GET
// No credentials for third-party access

The Vary Header: Often Forgotten

When dynamically setting Access-Control-Allow-Origin, always include:

Vary: Origin

Without this, CDNs and caches might serve responses with the wrong origin header, causing security issues or broken functionality.

Testing CORS Security

Manual Testing

// Test from browser console on attacker.com
fetch('https://api.target.com/user/profile', {
  credentials: 'include'
})
.then(r => r.json())
.then(data => console.log('Stolen data:', data))
.catch(err => console.log('CORS blocked:', err));

Automated Testing with curl

# Test if arbitrary origin is reflected
curl -H "Origin: https://evil.com" -I https://api.target.com/data

# Look for:
# Access-Control-Allow-Origin: https://evil.com  # BAD!
# Access-Control-Allow-Credentials: true         # VERY BAD!

CORS Security Checklist

  • Never reflect the Origin header without validation
  • Use exact-match allowlists for origins
  • Never accept the null origin
  • Don't use wildcards with credentials
  • Validate origin using URL parsing, not string matching
  • Include Vary: Origin for dynamic CORS headers
  • Minimize exposed headers and methods
  • Use SecScanner to audit CORS configurations
  • Test with malicious origins during security reviews
  • Document your CORS policy for team awareness

CORS misconfigurations are a common source of data breaches. Take the time to implement it correctly, validate your configuration, and regularly test your endpoints.

Related Articles

Headers

Content Security Policy Masterclass: From Basics to Advanced Protection

12 min read

Headers

The Complete Guide to HTTP Security Headers

10 min read

Headers

Cookie Security: HttpOnly, Secure, SameSite and Beyond

9 min read

Check Your Website Security

Want to see how your website measures up? Run a free security scan with SecScanner to identify vulnerabilities and get actionable remediation guidance.

Scan Your Website Free

On This Page

ComplianceTerms of UsePrivacy PolicyRefund PolicyContact

© 2025-2026 SecScanner. All rights reserved.