Entenda CORS de uma vez por todas!

CORS, Access Control, Preflighted, Autenticação… Entenda de uma vez por todas o que é CORS e como funciona essa maravilhosa tecnologia Web!

Ir para o artigo

Não precisa disfarçar: eu sei que você já deu de cara com algum erro de CORS no seu console enquanto trabalhava em um projeto Web. 😬

Se você nunca entendeu bem esse negócio de CORS, mesmo quando procurou algum material de explicação, seus problemas acabaram: neste artigo, aprenda de uma vez por todas o que é CORS, qual sua razão de existir e por que CORS é amigo de todos os devs (e pessoas normais).

No dia-a-dia do desenvolvimento front end, é bastante comum ser preciso exibir dados localizados em outro lugar. Mas, antes de ser possível exibir esses dados, o navegador precisa fazer uma requisição a um servidor para buscar esses dados; o cliente envia uma requisição HTTP com todas as informações que o servidor precisa para retornar esses dados ao cliente.

Digamos que se está tentando buscar algumas informações no site www.mywebsite.com de um servidor localizado em api.website.com.

Animação com exemplo de uma requisição simples de dados em um subdomínio.

Este é um exemplo simples de envio de uma solicitação HTTP para um servidor, que respondeu com dados JSON solicitados.

Agora, exatamente a mesma solicitação, mas de outro domínio. Em vez de fazer a solicitação em www.mywebsite.com, vai ser de um site de URL www.anotherdomain.com.

Animação mostrando uma requisição de dados em outro domínio que ocasionou um erro de CORS.

O quê?! Foi enviada exatamente a mesma requisição, mas, desta vez, o navegador mostrou um erro estranho? 🤔

Isso foi CORS em ação.

Chega o momento de examinar o porquê desse erro ter ocorrido e o quê exatamente ele significa.

Same-Origin Policy

Há algo na Web chamado Same-Origin Policy (ou Política de Mesma Origem). Por padrão, só é possível acessar recursos localizados na mesma origem que a origem de um solicitação. Não há problema em carregar uma imagem localizada em https://mywebsite.com/image1.png, por exemplo.

Entretanto, um recurso tem “origem cruzada” (cross-origin) quando está localizado em um (sub)domínio, protocolo ou porta diferente.

Explicação de em quais situações um recurso é considerado original, same origin e cross origin.

Legal, mas… Por que, afinal, essa política de mesma origem (same-origin) existe?

Digamos que essa política de mesma origem não existisse e você clicou acidentalmente em um dos muitos links de vírus que sua tia envia no Facebook.

Esse link o redireciona para um “site maligno” que possui um iframe incorporado que carrega o site do seu banco e efetua login com sucesso com alguns cookies definidos.

Os desenvolvedores deste “site maligno” tornaram possível o site acessar esse iframe e interagir com o conteúdo do DOM do site do seu banco para enviar dinheiro para uma outra conta!

Animação esquemática de uma fraude bancária a partir de um site falso.

Não é preciso falar mais nada; este é um enorme risco de segurança!

E é exatamente aqui que a política de mesma origem vem para ajudar. Essa política garante que apenas seja possível acessar recursos — adivinhe — da mesma origem (same-origin).

Animação mostrando como a política de mesma origem da Web ajuda a evitar fraudes.

Nesse exemplo, o site www.evilwebsite.com tentou acessar recursos de origem cruzada no site www.bank.com. A política de mesma origem impediu que isso acontecesse, garantindo que os desenvolvedores do “site maligno” não conseguissem acessar os dados bancários.

Entendido, mas… O que isso tem a ver com CORS?

Client-side CORS

Embora a política de mesma origem (same-origin policy) realmente se aplique apenas a scripts, os navegadores estenderam essa política para solicitações JavaScript: por padrão, só é possível acessar recursos da mesma origem.

Animação mostrando várias requisições diferentes e como a política de mesma origem afeta cada uma delas.

É bastante comum no dia-a-dia de desenvolvimento ser preciso acessar recursos de origem cruzada — um exemplo trivial é o front-end precisar interagir com uma API de back-end para pegar alguns dados.

Para permitir solicitações de origem cruzada com segurança, o navegador usa um mecanismo chamado… CORS!

CORS significa Cross-Origin Resource Sharing (ou Compartilhamento de Recursos de Origem Cruzada). Embora o navegador impeça o acesso de recursos que não estão localizados na mesma origem, é possível usar o CORS para alterar um pouco essas restrições de segurança e ainda garantir que este acesso seja feito com segurança.

User Agents (um navegador, por exemplo) podem usar CORS para permitir solicitações de origem cruzada — que, de outra forma, teriam sido bloqueadas — com base nos valores de determinados cabeçalhos específicos de CORS em uma resposta HTTP.

Quando uma requisição cross-origin é feita, o cliente automaticamente adiciona um cabeçalho extra à solicitação HTTP: Origin. O valor desse Origin é a origem de onde a solicitação partiu.

Animação mostrando a inserção automática do cabeçalho Origin em uma requisição CORS.

Para que o navegador permita acessar recursos de origem cruzada, ele espera certos cabeçalhos da resposta do servidor, que especificam se esse servidor permite solicitações de origem cruzada (ou não).

Server-side CORS

Devs responsáveis pelo back-end podem garantir que solicitações de origem cruzada sejam permitidas, adicionando cabeçalhos extras à resposta HTTP — que começam com Access-Control-*.

Com base nos valores desses cabeçalhos de resposta (response headers) de CORS, o navegador agora pode permitir determinadas respostas de origem cruzada que normalmente seriam bloqueadas pela política de mesma origem.

Embora existam diversos cabeçalhos de CORS, há um cabeçalho que o navegador necessariamente precisa para permitir o acesso a recursos de origem: Access-Control-Allow-Origin.

O valor desse cabeçalho especifica quais origens têm permissão para acessar os recursos solicitados do servidor.

Por exemplo, em um servidor ao qual https://mywebsite.com deve ter acesso, é possível adicionar o valor desse domínio ao cabeçalho Access-Control-Allow-Origin.

Animação mostrando o uso do cabeçalho CORS Access-Control-Allow-Origin.

Este cabeçalho é adicionado à resposta que o servidor envia de volta ao cliente. Ao adicionar esse cabeçalho, a política de mesma origem não impede mais de receber recursos localizados na origem https://api.mywebsite.com quando se envia a requisição em https://mywebsite.com.

Animação mostrando exemplo de correta configuração de política de mesma origem para CORS.

O mecanismo CORS no navegador verifica se o valor do cabeçalho Access-Control-Allow-Origin é igual ao valor do Origin enviado pela requisição.

Nesse exemplo, a origem da solicitação é https://www.mywebsite.com, que está listada no cabeçalho de resposta Access-Control-Allow-Origin.

Animação exemplificando requisição feita com sucesso sob uma configuração de CORS feita com sucesso.

Maravilha! Agora é possível receber com êxito os recursos de origem cruzada (cross-origin resources)!

Mas o que acontece quando se tenta acessar esses recursos de uma origem que não está listada no cabeçalho Access-Control-Allow-Origin?

Animação exemplificando requisição falhada devido a uma requisição feita fora da configuração de CORS.

Ahh, sim: acontece aquele erro de CORS (que às vezes pode ser tão frustrant). Mas, agora, você finalmente entendeu que faz todo o sentido!

Nesse exemplo, a origem fornecida foi https://www.anotherwebsite.com; no entanto, o servidor não tinha essa origem fornecida na lista de origens permitidas no cabeçalho Access-Control-Allow-Origin. O CORS bloqueou a solicitação com êxito e não foi possível acessar os dados.

Access-Control-Allow-Methods

Access-Control-Allow-Origin é somente um dos muitos cabeçalhos CORS possíveis. É possível estender as políticas CORS do servidor para permitir/proibir certas solicitações.

Outro cabeçalho comum é o Access-Control-Allow-Methods. Através dele, o CORS só permitirá solicitações de origem cruzada se elas forem enviadas com um dos métodos listados.

Animação exemplificando o uso do cabeçalho CORS Access-Control-Allow-Methods.

Neste exemplo, somente solicitações de método GET, POST ou PUT serão permitidas; outros métodos (como PATCH ou DELETE), bloqueados.

E por falar em requisições PUT, PATCH e DELETE, CORS realmente lida com elas de maneira diferente. Essas solicitações “não simples” iniciam algo chamado preflight request.

Preflighted Requests

CORS possui 2 tipos de requisição: simple request (pedido simples) e preflighted (“pré-confirmada“, “pré-comprovada” ou algo assim). Se uma solicitação é simple ou preflighted, depende de alguns valores dentro da própria requisição.

Uma solicitação é simple quando a requisição é um método GET ou POST e não possui cabeçalhos personalizados. Qualquer outra, como requisições PUT, PATCH ou DELETE, será preflighted.

Mas o que realmente significa uma requisição “preflighted” e por que isso acontece?

Antes de uma requisição ser realmente enviada, o cliente gera uma requisição preflighted que contém informações sobre a solicitação real nos cabeçalhos Access-Control-Request-*.

Isso fornece informações ao servidor sobre a solicitação real que o navegador está tentando fazer: qual é o método, quais são os cabeçalhos adicionais e assim por diante.

Animação exemplificando uma requisição preflighted.

O servidor recebe essa solicitação preflighted e envia uma resposta HTTP vazia de volta com os cabeçalhos CORS do servidor. O navegador recebe a resposta de comprovação (preflight response) — que não contém quaisquer dados além dos cabeçalhos CORS — e verifica se a solicitação HTTP deve ser permitida.

Animação exemplificando o fluxo completo de uma requisição e resposta preflighted.

Então, o navegador envia a solicitação real ao servidor, que responde com os dados solicitados.

Animação exemplificando retorno de dados do servidor após uma requisição preflighted de sucesso.

Entretanto, se não for o caso, o CORS bloqueará a solicitação preflighted e a solicitação real nunca será enviada.

A preflighted request é uma ótima maneira de impedir o acesso ou a modificação de recursos em servidores que não possuem nenhuma política CORS ativada (ainda).

Com isso, os servidores ficam protegidos contra solicitações de origem cruzada indesejadas (unwanted cross-origin requests).

Credenciais

Cookies, cabeçalhos de autorização e certificados TLS são definidos por padrão apenas em solicitações de mesma origem (same-origin requests). No entanto, pode ser preciso usar essas credenciais em solicitações cross-origin — por exemplo, incluir cookies na requisição que o servidor usa para identificar algum usuário.

Embora o CORS não inclua credenciais por padrão, é possível usar o cabeçalho Access-Control-Allow-Credentials.

Se for preciso incluir cookies e/ou outros cabeçalhos de autorização em cross-origin requests, define-se o campo withCredentials como true na solicitação e o cabeçalho Access-Control-Allow-Credentials à resposta.

Animação mostrando o uso de credenciais em requisição/resposta ao/do servidor.

Com isso, é possível incluir credenciais em requisições cross-origin. 🥳

Conclusão

Erros de CORS às vezes podem ser frustrantes, mas é incrível que essa tecnologia permita fazer solicitações de origem cruzada (cross-origin requests) com segurança através do navegador.

Depois de ter lido nosso artigo, com certeza suas principais dúvidas sobre o funcionamento básico de CORS agora foram esclarecidas de uma vez por todas!

Mas, obviamente, há muito mais sobre CORS do que aqui consta. Se tiver interesse em estudar mais sobre o assunto, existem muitos bons recursos por aí (inclusive da própria W3C).