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
.
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
.
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.
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!
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).
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.
É 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.
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
.
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
.
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
.
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
?
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!
1 2 3 |
The 'Access-Control-Allow-Origin' header has a value 'https://www.mywebsite.com' that is not equal to the supplied origin. |
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.
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.
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.
Então, o navegador envia a solicitação real ao servidor, que responde com os dados solicitados.
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.
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).