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