Cross-Origin Resource Sharing (CORS)

Security mechanism that enables controlled access to resources located outside of a given domain, relaxing the Same-Origin Policy.

What is Cross-Origin Resource Sharing (CORS)?

Cross-Origin Resource Sharing (CORS) is a security mechanism that allows web applications running on one domain to access resources from another domain. CORS extends and relaxes the Same-Origin Policy (SOP) by enabling servers to specify who can access their resources and which HTTP methods are allowed.

CORS works by adding HTTP headers that instruct browsers to give web applications from different origins access to selected resources. This is essential for modern web applications that often need to interact with APIs and services hosted on different domains.

How CORS Works

CORS operates through a series of HTTP headers exchanged between the browser and server:

  1. Browser sends preflight request (for certain types of requests)
  2. Server responds with CORS headers
  3. Browser evaluates the headers and decides whether to allow the request
  4. If allowed, browser makes the actual request

Simple vs. Preflight Requests

Simple requests (no preflight needed):

  • Use GET, HEAD, or POST methods
  • Only allow specific headers (Accept, Accept-Language, Content-Language, Content-Type)
  • Content-Type limited to: application/x-www-form-urlencoded, multipart/form-data, or text/plain

Preflight requests (require OPTIONS request first):

  • Use PUT, DELETE, or other methods
  • Include custom headers
  • Have Content-Type other than simple types

Key CORS Headers

Request Headers

  • Origin: Indicates the origin of the requesting site
  • Access-Control-Request-Method: Used in preflight requests to specify the method
  • Access-Control-Request-Headers: Used in preflight requests to specify headers

Response Headers

  • Access-Control-Allow-Origin: Specifies which origins are allowed
  • Access-Control-Allow-Methods: Lists allowed HTTP methods
  • Access-Control-Allow-Headers: Lists allowed request headers
  • Access-Control-Allow-Credentials: Indicates if credentials can be included
  • Access-Control-Expose-Headers: Lists response headers that can be accessed
  • Access-Control-Max-Age: Specifies how long preflight response can be cached

Header Syntax and Examples

Basic CORS response:

Access-Control-Allow-Origin: https://example.com

Multiple allowed origins:

Access-Control-Allow-Origin: https://example.com, https://trusted-site.com

Allow all origins (not recommended for sensitive data):

Access-Control-Allow-Origin: *

Allow credentials (cookies, auth headers):

Access-Control-Allow-Credentials: true

Specify allowed methods:

Access-Control-Allow-Methods: GET, POST, PUT, DELETE

Specify allowed headers:

Access-Control-Allow-Headers: Content-Type, Authorization

Security Implications

Benefits of CORS:

  • Enables secure cross-origin requests
  • Provides granular control over resource access
  • Maintains security while allowing necessary cross-origin interactions
  • Prevents unauthorized access to sensitive data

Potential risks:

  • Misconfiguration can expose sensitive data
  • Overly permissive policies can enable CSRF attacks
  • Wildcard (*) origins should be avoided for sensitive operations
  • Credentials require careful handling

Best Practices

  1. Be specific with allowed origins - avoid using * for sensitive data
  2. Use credentials carefully - only enable when necessary
  3. Limit allowed methods to only what's required
  4. Restrict allowed headers to essential ones
  5. Set appropriate cache durations for preflight responses
  6. Combine with other security headers:

Example Implementations

Node.js (Express) Example:

app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', 'https://example.com');
  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');
  next();
});

// Handle preflight requests
app.options('*', (req, res) => {
  res.header('Access-Control-Max-Age', '86400'); // Cache for 24 hours
  res.sendStatus(204);
});

Apache (.htaccess) Example:

Header set Access-Control-Allow-Origin "https://example.com"
Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE"
Header set Access-Control-Allow-Headers "Content-Type, Authorization"
Header set Access-Control-Allow-Credentials "true"
Header set Access-Control-Max-Age "86400"

Nginx Example:

location / {
    add_header 'Access-Control-Allow-Origin' 'https://example.com';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE';
    add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
    add_header 'Access-Control-Allow-Credentials' 'true';
    add_header 'Access-Control-Max-Age' '86400';

    if ($request_method = 'OPTIONS') {
        return 204;
    }
}

Common Use Cases

  1. API access: Allowing web applications to access APIs hosted on different domains
  2. Single Page Applications (SPAs): Loading resources from CDNs while accessing backend APIs
  3. Microservices architecture: Enabling communication between services on different domains
  4. Third-party integrations: Securely accessing external services and resources
  5. Content delivery: Loading fonts, scripts, and stylesheets from CDNs
  6. Authentication flows: Handling cross-origin authentication with credentials

CORS vs. JSONP

FeatureCORSJSONP
SecurityMore secure (built-in browser support)Less secure (relies on script injection)
Error HandlingProper HTTP status codesLimited error handling
Request TypesSupports all HTTP methodsOnly GET requests
Data FormatsSupports JSON, XML, etc.Only JSON
CredentialsSupports cookies and auth headersNo credential support
Browser SupportModern browsersLegacy browser support

Debugging CORS Issues

Common CORS errors and solutions:

  1. No 'Access-Control-Allow-Origin' header:
    • Ensure server is sending the CORS headers
    • Verify the origin is included in allowed origins
  2. Preflight request fails:
    • Check that OPTIONS method is handled
    • Verify allowed methods and headers
  3. Credentials not working:
    • Ensure Access-Control-Allow-Credentials: true is set
    • Avoid using wildcard (*) with credentials
  4. Method not allowed:
    • Verify the HTTP method is included in Access-Control-Allow-Methods
  5. Header not allowed:
    • Ensure custom headers are listed in Access-Control-Allow-Headers

Advanced CORS Features

Dynamic Origin Handling:

app.use((req, res, next) => {
  const allowedOrigins = ['https://example.com', 'https://trusted-site.com'];
  const origin = req.headers.origin;

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

  res.header('Access-Control-Allow-Credentials', 'true');
  next();
});

CORS with Authentication:

app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', 'https://example.com');
  res.header('Access-Control-Allow-Credentials', 'true');
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');

  if (req.method === 'OPTIONS') {
    res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
    return res.status(204).send();
  }

  next();
});