CORS – Qual é a motivação por trás da introdução de solicitações de comprovação?

O compartilhamento de resources de origem cruzada é um mecanismo que permite que uma página web crie XMLHttpRequests para outro domínio (da wikipedia ), e é muito importante (de mim :).

Tenho andado a mexer com o CORS nos últimos dias e acho que tenho uma boa compreensão de como tudo funciona.

Portanto, minha pergunta não é sobre como o CORS / preflight funciona, mas sim sobre o motivo por trás da realização de preflight como um novo tipo de solicitação . Eu não vejo qualquer razão pela qual o servidor A precisa enviar um preflight (PR) para o servidor B apenas para descobrir se a solicitação real (RR) será aceita ou não – certamente seria possível para B aceitar / rejeitar RR sem qualquer PR anterior.

Depois de pesquisar bastante, encontrei esta informação em www.w3.org (7.1.5):

Para proteger resources contra solicitações de origem cruzada que não puderam se originar de determinados agentes do usuário antes que esta especificação existisse, foi feito um pedido de simulação para garantir que o recurso esteja ciente dessa especificação.

Acho que é o mais difícil de entender a sentença. Minha interpretação (melhor chamá-la de “melhor palpite”) é que se trata de proteger o servidor B contra as solicitações do servidor C que não conhece as especificações.

Alguém pode por favor explicar um cenário / mostrar um problema que PR + RR resolve melhor do que apenas a RR?

Passei algum tempo confuso quanto ao propósito do pedido de comprovação, mas acho que já o tenho.

O principal insight é que as solicitações de comprovação não são uma garantia . Pelo contrário, eles são uma coisa que não muda as regras .

As solicitações de comprovação não têm nada a ver com segurança e não têm relação com os aplicativos que estão sendo desenvolvidos agora, com um conhecimento do CORS. Em vez disso, o mecanismo de preflight beneficia os servidores que foram desenvolvidos sem o conhecimento do CORS e funciona como uma verificação de sanidade entre o cliente e o servidor que ambos reconhecem CORS. Os desenvolvedores do CORS achavam que havia servidores suficientes por aí que estavam confiando na suposição de que nunca receberiam, por exemplo, um pedido DELETE entre domínios que inventaram o mecanismo de comprovação para permitir que ambos os lados optassem por participar. Eles achavam que a alternativa, que teria sido simplesmente habilitar as chamadas entre domínios, teria quebrado muitos aplicativos existentes.

Existem três cenários aqui:

  1. Servidores antigos, não mais em desenvolvimento e desenvolvidos antes do CORS. Esses servidores podem fazer suposições que nunca receberão, por exemplo, uma solicitação DELETE entre domínios. Esse cenário é o principal beneficiário do mecanismo de comprovação. Sim, esses serviços já podem ser usados ​​por um agente de usuário mal-intencionado ou não (e o CORS não faz nada para mudar isso), mas em um mundo com o CORS o mecanismo de comprovação fornece uma ‘verificação de sanidade’ extra para que clientes e servidores não quebrar porque as regras subjacentes da web mudaram.

  2. Servidores que ainda estão em desenvolvimento, mas que contêm muito código antigo e para os quais não é viável / desejável auditar todo o código antigo para garantir que ele funcione corretamente em um mundo de vários domínios. Este cenário permite que os servidores optem por aderir progressivamente ao CORS, por exemplo, dizendo “Agora permitirei este header específico”, “Agora permitirei este verbo HTTP específico”, “Agora permitirei que as informações de cookies / autenticação sejam enviado “, etc. Este cenário se beneficia do mecanismo de comprovação.

  3. Novos servidores que são escritos com uma consciência do CORS. De acordo com as práticas de segurança padrão, o servidor tem que proteger seus resources diante de qualquer solicitação recebida – os servidores não podem confiar em clientes para não fazerem coisas mal-intencionadas. Este cenário não se beneficia do mecanismo de comprovação : o mecanismo de comprovação não oferece segurança adicional a um servidor que tenha protegido adequadamente seus resources.

Qual foi a motivação por trás da introdução de solicitações de comprovação?

As solicitações de comprovação foram introduzidas para que os navegadores tenham certeza de que estavam lidando com um servidor que reconhece o CORS antes de enviar determinadas solicitações. Essas solicitações foram definidas como aquelas que eram potencialmente perigosas (mudança de estado) e novas (não possíveis antes do CORS devido à Política de mesma origem ). A utilização de solicitações de comprovação significa que os servidores devem aceitar (respondendo adequadamente à comprovação) aos novos tipos de solicitações potencialmente perigosos que o CORS possibilita.

Esse é o significado desta parte da especificação : “Para proteger resources contra solicitações de origem cruzada que não puderam se originar de determinados agentes do usuário antes que esta especificação existisse, foi feito um pedido de simulação para garantir que o recurso esteja ciente dessa especificação.”

Você pode me dar um exemplo?

Vamos imaginar que um usuário do navegador esteja logado em seu site bancário no A.com . Quando eles navegam para o malicioso B.com , essa página inclui algum Javascript que tenta enviar uma solicitação DELETE para A.com/account . Como o usuário está conectado ao A.com , essa solicitação, se enviada, includeá cookies que identificam o usuário.

Antes do CORS, a Política de mesma origem do navegador o impediria de enviar essa solicitação. Mas como o objective do CORS é tornar possível esse tipo de comunicação de origem cruzada, isso não é mais apropriado.

O navegador poderia simplesmente enviar o DELETE diretamente e deixar tudo para o servidor. Mas e se a A.com não estiver ciente do protocolo CORS? Pode ir em frente e executar o DELETE perigoso. Assumiu que nunca poderia receber tal solicitação devido à Política de mesma origem do navegador e, portanto, nunca foi projetado para impedir tal ataque.

Para proteger esses servidores não sensíveis ao CORS, o protocolo exige que o navegador primeiro envie uma solicitação de comprovação . Esse novo tipo de solicitação é algo que somente servidores com reconhecimento de CORS podem responder adequadamente, permitindo que o navegador saiba se é ou não seguro enviar o DELETE real.

Por que toda essa confusão sobre o navegador, o invasor não pode simplesmente enviar uma solicitação DELETE do próprio computador?

Claro, mas essa solicitação não includeá os cookies do usuário. O ataque que isso é projetado para evitar depende do fato de que o navegador enviará cookies (em particular, informações de autenticação para o usuário) para o outro domínio junto com a solicitação.

Isso soa como falsificação de solicitação entre sites , em que um formulário no site B.com pode ser A.com para o A.com com os cookies do usuário e causar danos.

Está certo. Outra maneira de colocar isso é que as solicitações de comprovação foram criadas para não aumentar a superfície de ataque do CSRF para servidores que não reconhecem o CORS.

Mas olhando para os requisitos de solicitações “simples” que não exigem pré-visualizações, vejo que o POST ainda é permitido. Isso pode alterar o estado e excluir dados como um DELETE !

Isso é verdade! O CORS não protege seu site contra ataques de CSRF. Então, novamente, sem o CORS, você também não está protegido contra ataques de CSRF. A finalidade das solicitações de comprovação é limitar a exposição do CSRF ao que já existia no mundo pré-CORS.

Suspiro. Ok, aceito de má vontade a necessidade de solicitações de comprovação. Mas por que temos que fazer isso para cada recurso (URL) no servidor? O servidor manipula o CORS ou não.

Você tem certeza sobre isso? Não é incomum que vários servidores lidem com solicitações de um único domínio. Por exemplo, pode ser que os pedidos para A.com/url1 sejam tratados por um tipo de servidor e os pedidos para A.com/url2 sejam tratados por um tipo diferente de servidor. Geralmente, não é o caso de o servidor manipular um único recurso poder fazer garantias de segurança sobre todos os resources nesse domínio.

Bem. Vamos comprometer. Vamos criar um novo header CORS que permita ao servidor indicar exatamente quais resources ele pode falar, para que as solicitações adicionais de comprovação para esses URLs possam ser evitadas.

Boa ideia! Na verdade, o header Access-Control-Policy-Path foi proposto exatamente para esse propósito. Em última análise, porém, ficou de fora da especificação, aparentemente porque alguns servidores implementaram incorretamente a especificação do URI de tal forma que solicitações para caminhos que pareciam seguros para o navegador não seriam de fato seguras nos servidores quebrados.

Essa foi uma decisão prudente que priorizou a segurança em relação ao desempenho, permitindo que os navegadores implementassem imediatamente a especificação CORS sem colocar em risco os servidores existentes? Ou foi míope de condenar a internet ao desperdício de largura de banda e duplicar a latência apenas para acomodar erros em um determinado servidor em um determinado momento?

Opiniões divergem.

Bem, no mínimo, os navegadores armazenarão em cache a pré-impressão de um único URL?

Sim. Embora provavelmente não seja por muito tempo. Nos navegadores WebKit, o tempo máximo de cache de preflight é atualmente de 10 minutos .

Suspiro. Bem, se eu souber que meus servidores estão cientes do CORS e, portanto, não precisam da proteção oferecida pelos pedidos de comprovação, há alguma maneira de evitá-los?

Sua única opção real é garantir que você atenda aos requisitos de solicitações “simples”. Isso pode significar deixar headers personalizados que você includeia (como X-Requested-With ), mentir sobre o Content-Type ou mais.

Seja o que for que você faça, certifique-se de ter as proteções CSRF adequadas, já que a especificação CORS não aborda a rejeição de solicitações “simples”, incluindo o POST não seguro. Como a especificação diz : “os resources para os quais as solicitações simples têm significado diferente de recuperação devem proteger-se da falsificação de solicitação entre sites”.

Considere o mundo das solicitações entre domínios antes do CORS. Você poderia fazer um formulário padrão POST ou usar um script ou uma tag de image para emitir uma solicitação GET. Você não pode fazer nenhum outro tipo de solicitação diferente de GET / POST e não pode emitir headers personalizados para essas solicitações.

Com o advento do CORS, os autores da especificação enfrentaram o desafio de introduzir um novo mecanismo de cross-domain sem quebrar a semântica existente da web. Eles optaram por fazer isso dando aos servidores uma maneira de aceitar qualquer novo tipo de solicitação. Essa opção é a solicitação de comprovação.

Portanto, as solicitações GET / POST sem nenhum header personalizado não precisam de um comprovante, pois essas solicitações já eram possíveis antes do CORS. Mas qualquer solicitação com headers personalizados ou solicitações PUT / DELETE precisa de um preflight, pois são novos para as especificações do CORS. Se o servidor não souber nada sobre o CORS, ele responderá sem nenhum header específico do CORS e a solicitação real não será feita.

Sem a solicitação de comprovação, os servidores poderiam começar a ver solicitações inesperadas de navegadores. Isso poderia levar a um problema de segurança se os servidores não estivessem preparados para esses tipos de solicitações. O preflight CORS permite que solicitações entre domínios sejam introduzidas na Web de maneira segura.

O CORS permite que você especifique mais headers e tipos de methods do que era possível anteriormente com origem cruzada ou

.

Alguns servidores podem ter sido (mal) protegidos com a suposição de que um navegador não pode fazer, por exemplo, solicitação DELETE origem cruzada ou solicitação de origem cruzada X-Requested-With header X-Requested-With , portanto tais solicitações são “confiáveis”.

Para certificar-se de que o servidor realmente realmente suporta o CORS e não apenas responde a solicitações aleatórias, o preflight é executado.

Aqui está outra maneira de ver isso, usando código:

    

Pré-CORS, a tentativa de exploração acima falharia porque viola a política de mesma origem. Uma API projetada dessa forma não precisava de proteção XSRF, porque ela era protegida pelo modelo de segurança nativo do navegador. Era impossível para um navegador pré-CORS gerar um JSON POST de origem cruzada.

Agora o CORS entra em cena – se não fosse necessário entrar no CORS via pré-voo, de repente este site teria uma enorme vulnerabilidade, sem nenhuma falha própria.

Para explicar por que algumas solicitações podem pular o pré-voo, isso é respondido pela especificação:

Uma solicitação simples de origem cruzada foi definida como congruente com aquelas que podem ser geradas por agentes de usuários atualmente implementados que não estão em conformidade com essa especificação.

Para desvendar isso, GET não é pré-flighted porque é um “método simples”, conforme definido por 7.1.5. (Os headers também devem ser “simples” para evitar o pré-voo). A justificativa para isso é que a solicitação GET de origem cruzada “simples” já pode ser executada, por exemplo, por (é assim que o JSONP funciona). Como qualquer elemento com um atributo src pode acionar um GET de origem cruzada, sem nenhum pré-voo, não haveria nenhum benefício de segurança em exigir uma pré-luta em XHRs "simples".

Eu sinto que as outras respostas não estão se concentrando na razão que a pré-luta aumenta a segurança.

Cenários:

1) Com pré-voo . Um atacante forja uma solicitação do site dummy-forums.com enquanto o usuário é autenticado para safe-bank.com
Se o servidor não verificar a origem e, de alguma forma, apresentar uma falha, o navegador emitirá uma solicitação de pré-vôo, o método OPTION. O servidor não conhece nenhum dos CORS que o navegador está esperando como resposta para que o navegador não prossiga (sem dano algum)

2) Sem pré-voo Um invasor falsifica a solicitação no mesmo cenário acima, o navegador emitirá a solicitação POST ou PUT imediatamente, o servidor a aceitará e poderá processá-la. Isso poderá causar algum dano.

Se o invasor enviar uma solicitação diretamente, de origem cruzada, de algum host random é mais provável que você esteja pensando em uma solicitação sem autenticação. Isso é um pedido falsificado, mas não um xsrf. então o servidor verificará as credenciais e falhará. O CORS não tenta impedir que um invasor tenha as credenciais para emitir solicitações, embora uma lista de permissions possa ajudar a reduzir esse vetor de ataque.

O mecanismo de pré-voo adiciona segurança e consistência entre clientes e servidores. Eu não sei se isso vale a pena o aperto de mão extra para cada pedido, pois o cache é muito difícil de usar lá, mas é assim que funciona.

Além disso, para methods de solicitação HTTP que podem causar efeitos colaterais nos dados do usuário (em particular, para methods HTTP diferentes de GET ou para uso POST com determinados tipos MIME), a especificação exige que os navegadores “preflight” a solicitação

Fonte

Os pedidos prévios não são sobre o desempenho ? Com as solicitações preflighted, um cliente pode saber rapidamente se a operação é permitida antes de enviar uma grande quantidade de dados, por exemplo, no método JSON com PUT. Ou antes de viajar dados confidenciais em headers de autenticação pela rede.

O fato de PUT, DELETE e outros methods, além de headers personalizados, não são permitidos por padrão (eles precisam de permissão explícita com “Access-Control-Request-Methods” e “Access-Control-Request-Headers”), que soa como uma verificação dupla, porque essas operações podem ter mais implicações nos dados do usuário, em vez de solicitações GET. Então, parece que:

“Vi que você permite solicitações entre sites de http: //foo.example , MAS tem CERTEZA de que permitirá solicitações DELETE? Considerou os impactos que essas solicitações podem causar nos dados do usuário?”

Eu não entendi a correlação citada entre as solicitações preflighted e os benefícios dos servidores antigos. Um serviço da Web que foi implementado antes do CORS, ou sem um reconhecimento de CORS, nunca receberá qualquer solicitação de site cruzado, porque primeiro a resposta não terá o header “Access-Control-Allow-Origin”.

Em um navegador que suporta o CORS, as solicitações de leitura (como GET) já estão protegidas pela política de mesma origem: um site mal-intencionado tentando fazer uma solicitação autenticada entre domínios (por exemplo, o site de Internet banking da vítima ou a interface de configuração do roteador) não ser capaz de ler os dados retornados porque o banco ou o roteador não define o header Access-Control-Allow-Origin .

No entanto, com solicitações de gravação (como POST), o dano é feito quando a solicitação chega ao servidor da Web. * Um servidor pode verificar o header Origin para determinar se a solicitação é legítima, mas essa verificação geralmente não é implementada porque o servidor da Web não tem A necessidade do CORS ou do servidor da web é mais antiga que o CORS e, portanto, pressupõe que os POSTs entre domínios sejam completamente proibidos pela política de mesma origem.

É por isso que os servidores da Web têm a chance de optar por receber solicitações de gravação entre domínios .

* Essencialmente a versão AJAX do CSRF.