Segurança: Same-Origin Policy e CORS
Por que um site não pode ler dados de outro? A Política de Mesma Origem protege usuários, e CORS é o mecanismo oficial para quebrar essa barreira de forma segura.
Por que origens precisam ser isoladas
Imagine que você abre banco.com e em outra aba abre site-malicioso.com.
Sem proteção, o JavaScript do site malicioso poderia fazer requests para banco.com
usando seus cookies de sessão — lendo seu saldo, transferindo dinheiro. A
Same-Origin Policy (SOP) impede isso: JavaScript só pode ler respostas da
mesma origem.
Mas APIs legítimas precisam ser acessadas de outras origens. O CORS (Cross-Origin Resource Sharing) permite que o servidor autorize explicitamente origens específicas via headers HTTP. O browser fiscaliza — não o servidor nem o cliente JS.
Simule uma requisição cross-origin
Configure a origem, o destino, o método e se o servidor inclui CORS headers. Veja passo a passo por que a requisição é permitida ou bloqueada.
A Política de Mesma Origem
# Same-Origin Policy (SOP) — a regra fundamental # Origem = protocolo + host + porta https://meuapp.com:443 # origem A https://meuapp.com:443/pagina # mesma origem (path não conta) http://meuapp.com:443 # DIFERENTE — protocolo diferente https://api.meuapp.com:443 # DIFERENTE — subdomínio diferente https://meuapp.com:8080 # DIFERENTE — porta diferente # O que a SOP protege: # JavaScript de origem A não pode ler resposta de origem B # Exceto: <img src="B">, <script src="B">, <link href="B"> (leitura bloqueada) # O que CORS faz: # Permite que B autorize explicitamente A via headers HTTP
Headers CORS
# Headers CORS que o servidor precisa retornar # Requisição simples (GET, POST form-data, text/plain) HTTP/1.1 200 OK Access-Control-Allow-Origin: https://meuapp.com # ou * para todos # Requisição não-simples — requer preflight OPTIONS primeiro OPTIONS /api/dados HTTP/1.1 Origin: https://meuapp.com Access-Control-Request-Method: POST Access-Control-Request-Headers: Content-Type HTTP/1.1 204 No Content Access-Control-Allow-Origin: https://meuapp.com Access-Control-Allow-Methods: GET, POST, PUT, DELETE Access-Control-Allow-Headers: Content-Type, Authorization Access-Control-Max-Age: 86400 # cache do preflight por 24h # Com credenciais (cookies) — * não funciona, precisa ser explícito Access-Control-Allow-Origin: https://meuapp.com Access-Control-Allow-Credentials: true
script-src 'self' bloqueia scripts externos não autorizados).
XFO (X-Frame-Options) — impede que seu site seja embutido em iframe de outro
domínio, prevenindo clickjacking. Ambos são configurados no servidor e fiscalizados pelo browser.
Configure CORS em um servidor
Mini projeto: crie uma API com Express.js e use o pacote cors para permitir apenas requisições de localhost:3000. Teste com fetch de origens diferentes e observe o erro no console do browser.
Projeto principal: implemente CORS manualmente (sem pacote): intercepte o header Origin, compare com uma allowlist, e adicione os headers de resposta corretos para GET simples e para POST JSON (com preflight). Teste requisições com e sem credenciais.
Desafio extra: configure uma Content Security Policy para uma página HTML: permita scripts apenas do próprio domínio (script-src 'self'), imagens de CDN específico, e bloqueie inline scripts. Use o Report-Only mode para monitorar violações sem bloquear.
Teste sua intuição
Onde você encontra isso
APIs públicas
APIs públicas (OpenWeather, GitHub, Stripe) precisam retornar Access-Control-Allow-Origin: * ou com autenticação por API Key. Sem isso, nenhum app frontend conseguiria consumi-las.
OAuth e cookies
Login com Google envolve redirects entre origens. O token de acesso é passado via parâmetro de URL, não via fetch direto, justamente para contornar CORS. Com credenciais (cookies), credentials: 'include' e headers exatos são obrigatórios.
CSP em produção
Sites grandes como GitHub usam CSP estrito: script-src 'nonce-abc123' — só scripts com o nonce correto (gerado no servidor a cada request) executam. Bloqueia XSS mesmo se o atacante injetar HTML.
Reverse proxy
Uma solução comum para evitar problemas de CORS em dev: usar Nginx ou Vite como proxy — as requests vão para localhost:3000/api que internamente faz forward para api.outro.com. O browser vê sempre a mesma origem.