Entendendo CORS
LidoNesta aula, você aprenderá o que é CORS (Cross-Origin Resource Sharing), por que ele existe, como funciona por baixo dos panos, quais erros comuns podem ocorrer e, principalmente, como resolver esses problemas na prática. Vamos explorar a política de mesma origem, os cabeçalhos HTTP envolvidos e estratégias eficazes para habilitar o CORS em suas aplicações.
O CORS (Cross-Origin Resource Sharing) é um mecanismo de segurança fundamental para a web moderna. Quando você desenvolve aplicações que consomem APIs de terceiros ou mesmo seus próprios backends em domínios diferentes, o CORS pode ser tanto um aliado quanto um obstáculo. Nesta aula, vamos desmistificar o CORS, entender por que ele existe, como ele opera nos bastidores e, principalmente, como resolver os problemas que ele pode causar.
Imagine que você está construindo um frontend em http://localhost:3000 e precisa fazer uma requisição para uma API em https://api.exemplo.com. Sem o CORS configurado corretamente, o navegador bloqueará a resposta, mesmo que o servidor a envie. Isso acontece porque o navegador aplica a política de mesma origem (same-origin policy) para proteger os usuários de ataques maliciosos. Nesta aula, você aprenderá a configurar o CORS tanto no servidor quanto no cliente, e a diagnosticar erros comuns.
Same-origin policy
A same-origin policy é uma regra de segurança implementada pelos navegadores que restringe como documentos ou scripts de uma origem podem interagir com recursos de outra origem. Uma origem é definida pela combinação de protocolo, domínio e porta. Por exemplo, https://exemplo.com:443 é uma origem, e https://exemplo.com:8080 é outra, mesmo que o domínio seja o mesmo.
Essa política é crucial para evitar que sites maliciosos acessem dados de outros sites sem permissão. Por exemplo, se você está logado no seu banco online, um site malicioso não pode fazer requisições para a API do banco e ler suas informações, pois o navegador bloquearia a resposta. A same-origin policy é a base da segurança na web, e o CORS é uma exceção controlada a essa regra.
Quando uma requisição é feita para uma origem diferente, o navegador verifica se o servidor permite essa origem através de cabeçalhos específicos. Se não houver permissão, a resposta é bloqueada, e o erro aparece no console do navegador. Vamos ver um exemplo de código que tenta fazer uma requisição cross-origin:
// Exemplo de requisição cross-origin que será bloqueada sem CORS
fetch('https://api.exemplo.com/dados')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Erro:', error));
No código acima, se o servidor api.exemplo.com não enviar o cabeçalho Access-Control-Allow-Origin apropriado, o navegador bloqueará a resposta e o erro será exibido no console. Isso é a same-origin policy em ação.
Como o CORS funciona
O CORS é um padrão que permite que servidores declarem quais origens podem acessar seus recursos. Ele funciona através de cabeçalhos HTTP adicionados às respostas do servidor. O navegador, ao receber a resposta, verifica esses cabeçalhos e decide se permite ou bloqueia o acesso ao JavaScript.
Existem dois tipos de requisições CORS: as simples e as pré-flight. Requisições simples são aquelas que usam métodos GET, HEAD ou POST com certos cabeçalhos permitidos (como Content-Type: application/x-www-form-urlencoded, multipart/form-data ou text/plain). Requisições que usam outros métodos (como PUT, DELETE) ou cabeçalhos personalizados disparam uma requisição pré-flight, que é uma requisição OPTIONS enviada antes da requisição real, para verificar se o servidor permite a operação.
Os principais cabeçalhos CORS são:
Access-Control-Allow-Origin: especifica quais origens podem acessar o recurso. Pode ser uma origem específica ou*para permitir todas.Access-Control-Allow-Methods: lista os métodos HTTP permitidos.Access-Control-Allow-Headers: lista os cabeçalhos que podem ser usados na requisição.Access-Control-Allow-Credentials: indica se cookies e credenciais são permitidos.Access-Control-Expose-Headers: controla quais cabeçalhos podem ser acessados pelo JavaScript.
Vamos ver um exemplo de resposta do servidor com CORS habilitado:
// Exemplo de resposta HTTP com cabeçalhos CORS
HTTP/1.1 200 OK
Content-Type: application/json
Access-Control-Allow-Origin: https://meusite.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
{ "mensagem": "Olá, mundo!" }
No exemplo acima, o servidor permite que apenas a origem https://meusite.com acesse o recurso. O navegador verificará o cabeçalho Access-Control-Allow-Origin e, se a origem da página corresponder, permitirá o acesso.
Erros comuns
Os erros de CORS são frustrantes porque muitas vezes o servidor responde corretamente, mas o navegador bloqueia a resposta. Os erros mais comuns incluem:
- Erro de origem não permitida: o cabeçalho
Access-Control-Allow-Originnão inclui a origem da requisição. Isso acontece quando o servidor configura o cabeçalho com uma origem específica que não corresponde à do cliente. - Método não permitido: o método HTTP usado na requisição não está listado em
Access-Control-Allow-Methods. - Cabeçalho não permitido: algum cabeçalho personalizado enviado na requisição não está listado em
Access-Control-Allow-Headers. - Problemas com credenciais: se você usar
credentials: 'include'no fetch, o servidor deve responder comAccess-Control-Allow-Credentials: truee não pode usar*emAccess-Control-Allow-Origin. - Requisição pré-flight falhou: a requisição OPTIONS não retorna os cabeçalhos corretos, ou o servidor não responde a ela.
Vamos ver um exemplo de erro típico no console:
// Exemplo de erro no console
Access to fetch at 'https://api.exemplo.com/dados' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Esse erro indica que o servidor não enviou o cabeçalho Access-Control-Allow-Origin na resposta. Para resolver, é necessário configurar o servidor para incluir esse cabeçalho.
Como resolver
Resolver problemas de CORS envolve ajustes no servidor, pois o CORS é uma responsabilidade do servidor. O cliente (navegador) não pode forçar a liberação. Aqui estão as principais estratégias:
- Configurar o servidor: a solução mais comum é adicionar os cabeçalhos CORS no servidor. Em Node.js com Express, você pode usar o middleware
cors. - Usar um proxy: se você não controla o servidor, pode usar um proxy reverso para adicionar os cabeçalhos. Por exemplo, em desenvolvimento, você pode configurar o webpack dev server para fazer proxy.
- Permitir todas as origens: para APIs públicas, você pode usar
Access-Control-Allow-Origin: *, mas isso não funciona com credenciais. - Configurar pré-flight corretamente: certifique-se de que o servidor responda a requisições OPTIONS com os cabeçalhos adequados.
Vamos ver um exemplo de configuração de CORS no servidor Node.js com Express:
// Exemplo de configuração de CORS com Express
const express = require('express');
const cors = require('cors');
const app = express();
// Opções de CORS
const corsOptions = {
origin: 'https://meusite.com', // ou '*' para todas
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
};
app.use(cors(corsOptions));
app.get('/dados', (req, res) => {
res.json({ mensagem: 'Olá, mundo!' });
});
app.listen(3000, () => {
console.log('Servidor rodando na porta 3000');
});
No exemplo acima, o middleware cors adiciona automaticamente os cabeçalhos necessários. Você pode personalizar as opções conforme sua necessidade.
Boas práticas
Ao trabalhar com CORS, considere as seguintes boas práticas:
- Nunca use
*com credenciais: se você precisa enviar cookies ou autenticação, defina origens específicas ecredentials: true. - Restrinja origens: em produção, evite permitir todas as origens. Liste apenas as origens confiáveis.
- Use proxies em desenvolvimento: para evitar problemas de CORS durante o desenvolvimento, configure um proxy no seu servidor de desenvolvimento.
- Teste com ferramentas de rede: use o DevTools do navegador para inspecionar as requisições e verificar os cabeçalhos CORS.
- Documente a API: se você é o desenvolvedor da API, documente quais origens são permitidas e como os clientes devem configurar suas requisições.
Referências
- MDN: CORS
- MDN: Same-origin policy
- Express CORS middleware
- Fetch Living Standard: CORS protocol
- MDN: Access-Control-Allow-Origin
- MDN: Access-Control-Allow-Methods
- MDN: Access-Control-Allow-Headers
- MDN: Access-Control-Allow-Credentials
- MDN: Access-Control-Expose-Headers
- MDN: Access-Control-Max-Age
Exercícios
- Explique o que é a same-origin policy e por que ela é importante para a segurança web.
- Descreva a diferença entre requisições simples e pré-flight no contexto do CORS.
- Liste os principais cabeçalhos CORS e explique a função de cada um.
- Escreva um código JavaScript que faça uma requisição fetch para uma API externa e trate o erro de CORS.
- Configure um servidor Node.js com Express para habilitar CORS apenas para a origem
https://meusite.come com suporte a credenciais.
-
Access-Control-Allow-Origin: especifica quais origens podem acessar o recurso.-
Access-Control-Allow-Methods: lista os métodos HTTP permitidos.-
Access-Control-Allow-Headers: lista os cabeçalhos personalizados permitidos na requisição.-
Access-Control-Allow-Credentials: indica se cookies e credenciais são permitidos.-
Access-Control-Expose-Headers: controla quais cabeçalhos podem ser lidos pelo JavaScript.-
Access-Control-Max-Age: define por quanto tempo a resposta da pré-flight pode ser armazenada em cache.// Exemplo de fetch tratando erro de CORS
fetch('https://api.exemplo.com/dados')
.then(response => {
if (!response.ok) {
throw new Error('Erro na requisição');
}
return response.json();
})
.then(data => console.log(data))
.catch(error => {
console.error('Erro:', error);
// Verificar se o erro é de CORS
if (error.message.includes('CORS')) {
console.error('Problema de CORS detectado. Verifique o servidor.');
}
});
// Servidor Express com CORS configurado
const express = require('express');
const cors = require('cors');
const app = express();
const corsOptions = {
origin: 'https://meusite.com',
credentials: true,
};
app.use(cors(corsOptions));
app.get('/dados', (req, res) => {
res.json({ mensagem: 'Olá, mundo!' });
});
app.listen(3000, () => {
console.log('Servidor rodando na porta 3000');
});