CORS Security
CORS (Cross-Origin Resource Sharing) é um mecanismo de segurança implementado pelos navegadores modernos que controla como aplicações web podem fazer requisições HTTP para domínios diferentes daquele que serviu a página original, relaxando de forma controlada a Same-Origin Policy (SOP) - uma política de segurança fundamental que restringe scripts de uma origem a acessar recursos de outra origem. Embora o CORS seja essencial para arquiteturas modernas de aplicações web, onde front-ends frequentemente precisam consumir APIs hospedadas em domínios diferentes, sua configuração inadequada representa uma das vulnerabilidades mais comuns e perigosas em aplicações web contemporâneas. Erros de configuração CORS podem expor dados sensíveis a domínios não autorizados, permitir ataques de Cross-Site Request Forgery (CSRF) mesmo em presença de tokens anti-CSRF, facilitar roubo de credenciais, e em casos extremos, permitir que atacantes executem ações privilegiadas em nome de usuários autenticados. O problema é agravado pelo fato de que muitos desenvolvedores, ao encontrarem erros CORS durante o desenvolvimento, optam por soluções permissivas demais (como usar wildcard "*" ou refletir automaticamente a origem da requisição) sem compreender plenamente as implicações de segurança. Este artigo explora em profundidade os fundamentos do CORS, vulnerabilidades comuns de configuração, e estabelece práticas robustas para implementação segura em diferentes plataformas e frameworks, equilibrando funcionalidade com postura defensiva adequada.
Same-Origin Policy (SOP)
Browsers implementam SOP: scripts só podem acessar recursos da mesma origem (protocolo + domínio + porta). CORS relaxa SOP de forma controlada.
# Mesma origem
https://example.com/api ← https://example.com/app [OK]
# Diferentes origens (bloqueado por SOP)
https://example.com ← http://example.com (protocolo)
https://example.com ← https://api.example.com (subdomínio)
https://example.com ← https://example.com:8080 (porta)
CORS Headers
Access-Control-Allow-Origin
# Permitir origem específica (recomendado)
Access-Control-Allow-Origin: https://trusted.com
# Permitir qualquer origem (PERIGOSO!)
Access-Control-Allow-Origin: *
# Dinâmico baseado em whitelist (correto)
const allowedOrigins = ['https://app1.com', 'https://app2.com'];
const origin = request.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
Outros Headers Importantes
# Permitir credenciais (cookies, auth headers)
Access-Control-Allow-Credentials: true
# Métodos HTTP permitidos
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
# Headers permitidos em requests
Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With
# Headers expostos para client-side JavaScript
Access-Control-Expose-Headers: X-Custom-Header, X-Request-Id
# Tempo de cache do preflight (segundos)
Access-Control-Max-Age: 86400
Preflight Requests
Browsers enviam OPTIONS request antes de requisições "não-simples" para verificar permissões.
# Client envia preflight
OPTIONS /api/resource HTTP/1.1
Origin: https://app.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: Authorization
# Server responde com permissões
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.com
Access-Control-Allow-Methods: GET, POST, DELETE
Access-Control-Allow-Headers: Authorization
Access-Control-Max-Age: 86400
Vulnerabilidades CORS Comuns
1. Wildcard com Credenciais
# [ERRO] VULNERÁVEL - não funciona e é perigoso
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
# Browsers bloqueiam esta combinação
# [OK] CORRETO - origin específica com credentials
Access-Control-Allow-Origin: https://trusted.com
Access-Control-Allow-Credentials: true
2. Reflection Attack
# [ERRO] VULNERÁVEL - reflete qualquer origin
const origin = request.headers.origin;
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
# [OK] CORRETO - whitelist validation
const allowedOrigins = ['https://app.com', 'https://admin.com'];
const origin = request.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
}
3. Subdomain Wildcard
# [ERRO] VULNERÁVEL - regex mal implementada
const origin = request.headers.origin;
if (/https:\/\/.*\.example\.com/.test(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
// Aceita https://evil.example.com.attacker.com
# [OK] CORRETO - validação estrita
const origin = request.headers.origin;
if (/^https:\/\/[a-z0-9-]+\.example\.com$/.test(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
Configuração Segura por Tecnologia
Node.js/Express (CORS middleware)
const cors = require('cors');
// Configuração segura
const corsOptions = {
origin: function (origin, callback) {
const allowedOrigins = [
'https://app.example.com',
'https://admin.example.com'
];
if (!origin || allowedOrigins.includes(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
};
app.use(cors(corsOptions));
Nginx
# Configuração condicional
map $http_origin $cors_origin {
default "";
"~^https://app\\.example\\.com$" $http_origin;
"~^https://admin\\.example\\.com$" $http_origin;
}
server {
location /api {
if ($cors_origin != "") {
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Credentials true always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
}
if ($request_method = OPTIONS) {
return 204;
}
}
}
Apache
# .htaccess
SetEnvIf Origin "^https://(app|admin)\\.example\\.com$" CORS_ORIGIN=$0
Header always set Access-Control-Allow-Origin "%e" env=CORS_ORIGIN
Header always set Access-Control-Allow-Credentials "true" env=CORS_ORIGIN
Header always set Access-Control-Allow-Methods "GET, POST, PUT, DELETE" env=CORS_ORIGIN
Header always set Access-Control-Allow-Headers "Authorization, Content-Type" env=CORS_ORIGIN
# Responder OPTIONS preflight
RewriteEngine On
RewriteCond % OPTIONS
RewriteRule ^(.*)$ $1 [R=204,L]
Testing CORS
# Teste com curl
curl -H "Origin: https://evil.com" \\
-H "Access-Control-Request-Method: DELETE" \\
-H "Access-Control-Request-Headers: Authorization" \\
-X OPTIONS \\
https://api.example.com/resource
# JavaScript test
fetch('https://api.example.com/data', {
method: 'GET',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
}
}).then(response => console.log(response));
Best Practices
- Nunca use wildcard (*) em APIs com dados sensíveis
- Whitelist explícita de origens permitidas
- Validação estrita de origem com regex segura
- Minimize credentials: Só habilite se realmente necessário
- Least privilege: Permita apenas métodos e headers necessários
- Cache preflight: Use Max-Age para reduzir overhead
- Monitoring: Log CORS rejections suspeitos
Checklist CORS Seguro
- [OK] Origem validada contra whitelist explícita
- [OK] Regex de validação não permite bypasses
- [OK] Credentials só habilitado quando necessário
- [OK] Métodos e headers restritos ao mínimo
- [OK] Preflight configurado corretamente
- [OK] Testado contra origens maliciosas
- [OK] Logs de rejected requests monitorados
