Por que uma solicitação OPTIONS é enviada e posso desativá-la?

415

Estou construindo uma API da web. Descobri que sempre que uso o Chrome para POST, GET para minha API, sempre há uma solicitação de OPÇÕES enviada antes da solicitação real, o que é bastante irritante. Atualmente, eu recebo o servidor para ignorar quaisquer solicitações de OPÇÕES. Agora, minhas perguntas são: o que é bom para enviar uma solicitação OPTIONS para dobrar a carga do servidor? Existe alguma maneira de impedir completamente o navegador de enviar solicitações OPTIONS?

Qian Chen
fonte

Respostas:

376

editar 2018-09-13 : adicionadas algumas precisões sobre essa solicitação antes do voo e como evitá-la no final desta resposta.

OPTIONSsolicitações são o que chamamos de pre-flightsolicitações Cross-origin resource sharing (CORS).

Eles são necessários quando você faz solicitações de diferentes origens em situações específicas.

Essa solicitação de pré-vôo é feita por alguns navegadores como uma medida de segurança para garantir que a solicitação que está sendo feita seja confiável pelo servidor. Significando que o servidor entende que o método, a origem e os cabeçalhos enviados na solicitação são seguros para agir.

Seu servidor não deve ignorar, mas manipular essas solicitações sempre que você estiver tentando fazer solicitações de origem cruzada.

Um bom recurso pode ser encontrado aqui http://enable-cors.org/

Uma maneira de lidar com eles para ficar confortável é garantir que, para qualquer caminho com o OPTIONSmétodo, o servidor envie uma resposta com esse cabeçalho

Access-Control-Allow-Origin: *

Isso informará ao navegador que o servidor está disposto a responder a solicitações de qualquer origem.

Para obter mais informações sobre como adicionar suporte CORS ao seu servidor, consulte o fluxograma a seguir

http://www.html5rocks.com/static/images/cors_server_flowchart.png

Fluxograma CORS


editar 2018-09-13

A OPTIONSsolicitação CORS é acionada apenas em alguns casos, conforme explicado nos documentos MDN :

Algumas solicitações não acionam um preflight do CORS. Essas são chamadas de "solicitações simples" neste artigo, embora a especificação Fetch (que define o CORS) não use esse termo. Uma solicitação que não aciona uma verificação prévia do CORS - a chamada "solicitação simples" - é aquela que atende a todas as seguintes condições:

Os únicos métodos permitidos são:

  • PEGUE
  • CABEÇA
  • POSTAR

Além dos cabeçalhos definidos automaticamente pelo agente do usuário (por exemplo, Connection, User-Agent ou qualquer outro cabeçalho com nomes definidos na especificação Buscar como um "nome de cabeçalho proibido"), os únicos cabeçalhos que podem ser definidos manualmente são aqueles que a especificação Fetch define como sendo um "cabeçalho de solicitação listado na lista de segurança do CORS", que são:

  • Aceitar
  • Accept-Language
  • Idioma do Conteúdo
  • Tipo de conteúdo (mas observe os requisitos adicionais abaixo)
  • DPR
  • Downlink
  • Guardar dados
  • Largura da janela de visualização
  • Largura

Os únicos valores permitidos para o cabeçalho Content-Type são:

  • application / x-www-form-urlencoded
  • multipart / form-data
  • texto / simples

Nenhum ouvinte de evento é registrado em nenhum objeto XMLHttpRequestUpload usado na solicitação; estes são acessados ​​usando a propriedade XMLHttpRequest.upload.

Nenhum objeto ReadableStream é usado na solicitação.

Leo Correa
fonte
8
Mas não é realista definir esse sinalizador do Chrome para todos os usuários em geral.
Qian Chen
37
É incorreto dizer que solicitações de comprovação são necessárias ao fazer solicitações de origem cruzada. As solicitações de comprovação são necessárias apenas em situações específicas, como se você estiver configurando cabeçalhos personalizados ou fazendo solicitações que não sejam get, head e post.
Robin Clowers
4
Curiosamente, ao fazer uma solicitação CORS usando jQuery, a biblioteca JavaScript evita especificamente a configuração do cabeçalho personalizado, juntamente com uma palavra de aviso aos desenvolvedores: Para solicitações entre domínios, visto que as condições para uma comprovação são semelhantes a um quebra-cabeça, nós simplesmente nunca defina para ter certeza.
Abaixo do radar
3
Como é que, se eu fizer um curlpara a API, ele funciona, mas ao executar no chrome, recebo o erro?
SuperUberDuper
5
@SuperUberDuper porque as solicitações de CORS e de comprovação são um assunto relacionado ao navegador. Você pode simular o CORS adicionando um Origincabeçalho à sua solicitação para simular como se a solicitação fosse proveniente de um host específico (por exemplo, yourwebsite.com). Você também pode simular pedidos de pré-impressão, definindo o método de HTTP de um pedido para OPTIONSe os Access-Control-*cabeçalhos
Leo Correa
234

Passamos por esse problema, abaixo está minha conclusão sobre esse problema e minha solução.

De acordo com a estratégia do CORS (é altamente recomendável que você leia sobre isso) Você não pode simplesmente forçar o navegador a parar de enviar a solicitação OPTIONS, se achar necessário.

Existem duas maneiras de contornar isso:

  1. Verifique se sua solicitação é uma "solicitação simples"
  2. Definido Access-Control-Max-Agepara a solicitação OPTIONS

Pedido simples

Uma solicitação simples entre sites é aquela que atende a todas as seguintes condições:

Os únicos métodos permitidos são:

  • PEGUE
  • CABEÇA
  • POSTAR

Além dos cabeçalhos definidos automaticamente pelo agente do usuário (por exemplo, Conexão, User-Agent, etc.), os únicos cabeçalhos que podem ser configurados manualmente são:

  • Aceitar
  • Accept-Language
  • Idioma do Conteúdo
  • Tipo de conteúdo

Os únicos valores permitidos para o cabeçalho Content-Type são:

  • application / x-www-form-urlencoded
  • multipart / form-data
  • texto / simples

Uma solicitação simples não causará uma solicitação de OPÇÕES antes do voo.

Defina um cache para a verificação OPTIONS

Você pode definir um Access-Control-Max-Agepara a solicitação OPTIONS, para que ela não verifique a permissão novamente até expirar.

O Access-Control-Max-Age fornece o valor em segundos por quanto tempo a resposta à solicitação de comprovação pode ser armazenada em cache sem enviar outra solicitação de comprovação.

Limitação observada

  • Para cromo, os segundos máximos para Access-Control-Max-Ageé 600que é de 10 minutos, de acordo com cromo código fonte
  • Access-Control-Max-Agefunciona apenas para um recurso toda vez, por exemplo, GETsolicitações com o mesmo caminho de URL, mas consultas diferentes serão tratadas como recursos diferentes. Portanto, a solicitação para o segundo recurso ainda acionará uma solicitação de comprovação.
Neekey
fonte
3
Sim ... esta deve ser a resposta aceita e mais relevante para o questionário ..!
Rajesh Mbm
7
Obrigado por mencionar Access-Control-Max-Age. Essa é a chave aqui. Ajuda a evitar solicitações de comprovação excessivas.
Idris Mokhtarzada
Estou usando axios para chamar get request. Onde posso definir o Access-Control-Max-Age na solicitação de axios?
Mohit Shah
+1 O cabeçalho Access-Control-Max-Age é a chave aqui. Esta deve ser a resposta aceita! Eu configurei 86400 segundos (24 horas) no cabeçalho e a solicitação de pré-licença se foi!
revobtz
1
@VitalyZdanevich no! Não evite application/jsonapenas porque sua solicitação não é "simples" (e, portanto, aciona o CORS). O navegador está fazendo seu trabalho. Defina seu servidor para retornar algo como um cabeçalho Access-Control-Max-Age: 86400e o navegador não reenviará uma solicitação de OPÇÕES por 24 horas.
Colm.anseo 10/03/19
139

Consulte esta resposta sobre a necessidade real de solicitação de OPÇÕES pré-veiculada: CORS - Qual é a motivação por trás da introdução de solicitações de pré-voo?

Para desativar a solicitação OPTIONS, as condições abaixo devem ser atendidas para a solicitação ajax:

  1. A solicitação não define cabeçalhos HTTP personalizados como 'application / xml' ou 'application / json' etc.
  2. O método de solicitação deve ser um dos métodos GET, HEAD ou POST. Se POST, tipo de conteúdo deve ser uma das application/x-www-form-urlencoded, multipart/form-dataoutext/plain

Referência: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

device_exec
fonte
14
+1 para "cabeçalhos HTTP personalizados"! No meu caso, eles estavam fazendo com que a solicitação antes do voo fosse acionada. Refatorei a solicitação para enviar o que estava enviando nos cabeçalhos, pois o corpo da solicitação e as solicitações OPTIONS deixaram de ser enviadas.
4306 Andre Andre
21
application/xmlou application/jsonnão são "Cabeçalhos HTTP personalizados". O cabeçalho em si seria Content-Typee chamar esse cabeçalho de "personalizado" seria enganoso.
Leo Correa
1
Removidos cabeçalhos HTTP personalizados e isso funcionou como um encanto!
Tim D
47

Quando você tiver o console de depuração aberto e a Disable Cacheopção ativada, as solicitações de comprovação sempre serão enviadas (ou seja, antes de cada solicitação). se você não desabilitar o cache, uma solicitação pré-vôo será enviada apenas uma vez (por servidor)

Nir
fonte
3
oh o que eu estou pensando. tendo depuração por horas, esta foi a minha solução. cache desativado devido ao console de depuração.
22516 mauris
1
Mesmo que o console de depuração está fechado, os pedidos de pré-impressão são enviados
Luca Perico
2
Luca: isso é verdade, mas o ponto é que o "desativar cache" não tem efeito quando as ferramentas de desenvolvimento são fechadas. solicitações de comprovação são enviadas apenas uma vez (por servidor, é claro) se o cache não estiver desativado e antes de cada solicitação se o cache estiver desativado.
Nir
Isso foi realmente útil.
Anuraag Patil
38

Sim, é possível evitar a solicitação de opções. A solicitação de opções é uma solicitação de comprovação quando você envia (publica) quaisquer dados para outro domínio. É um problema de segurança do navegador. Mas podemos usar outra tecnologia: camada de transporte iframe. Eu recomendo fortemente que você esqueça qualquer configuração do CORS e use a solução readymade e ela funcionará em qualquer lugar.

Dê uma olhada aqui: https://github.com/jpillora/xdomain

E exemplo de trabalho: http://jpillora.com/xdomain/

XTRUST.ORG
fonte
isso é realmente um tipo de proxy drop-in?
matanster
15
"A solicitação de opções é uma solicitação de comprovação quando você envia (publica) dados para outro domínio." - Isso não é verdade. Você pode usar o XHR para enviar qualquer solicitação POST que possa enviar com um formulário HTML normal sem acionar uma solicitação de comprovação. Somente quando você começa a fazer coisas que um formulário não pode fazer (como tipos de conteúdo personalizados ou cabeçalhos de solicitação extras) é que uma comprovação é enviada.
Quentin
A solução aqui parece depender de um calço de iframe que funciona em alguns casos, mas tem algumas limitações importantes. O que acontece se você deseja saber o código de status HTTP da resposta ou o valor de outro cabeçalho de resposta HTTP?
Stephen Crosby
5
Os iFrames não foram feitos para isso.
Romko
1
Esta é uma implementação de software e responde à pergunta final que foi: "Existe alguma maneira de impedir completamente o navegador de enviar solicitações de OPÇÕES?". Para encurtar a história, não há como desativá-lo no Mozilla ou no Chromium, ele é implementado no código e as únicas opções "funcionais" apenas evitam o comportamento.
limpador
15

Para um desenvolvedor que entende o motivo da existência, mas precisa acessar uma API que não lida com chamadas OPTIONS sem autenticação, preciso de uma resposta temporária para que eu possa desenvolver localmente até que o proprietário da API adicione suporte SPA CORS adequado ou obtenha uma API proxy funcionando.

Descobri que você pode desativar o CORS no Safari e Chrome em um Mac.

Desativar a mesma política de origem no Chrome

Chrome: saia do Chrome, abra um terminal e cole este comando: open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir

Safari: desativando a política de mesma origem no Safari

Se você deseja desativar a política de mesma origem no Safari (eu tenho a versão 9.1.1), você só precisa ativar o menu do desenvolvedor e selecionar "Desativar restrições de origem cruzada" no menu de desenvolvimento.

Joseph Juhnke
fonte
4
Você deve colocar mais destaque na parte que diz: "NUNCA deve ser uma solução permanente!" . A mesma política de origem é uma medida de segurança do navegador muito importante e nunca deve ser desabilitada ao navegar normalmente na Internet.
jannis
Desejo que a Web funcione dessa maneira, para que possamos solicitar dados de que precisamos de servidores sem problemas adicionais.
jemiloii
14

Como mencionado nas postagens anteriores, as OPTIONSsolicitações existem por um motivo. Se você tiver um problema com grandes tempos de resposta do servidor (por exemplo, conexão com o exterior), também poderá fazer com que o navegador armazene em cache as solicitações de comprovação.

Faça com que o servidor responda com o Access-Control-Max-Agecabeçalho e, para solicitações que vão para o mesmo terminal, a solicitação de comprovação será armazenada em cache e não ocorrerá mais.

enpenax
fonte
1
Obrigado por isso! O fato de que as OPTIONSsolicitações serão armazenadas em cache com esse cabeçalho é bastante opaco em toda a documentação do CORS que eu li.
joshperry
E o cache só entra em vigor com exatamente o mesmo URL. Quero um cache de comprovação no nível do domínio que possa realmente reduzir as viagens de ida e volta. (CORS é bobo!)
pergunte
8

Eu resolvi esse problema como.

if($_SERVER['REQUEST_METHOD'] == 'OPTIONS' && ENV == 'devel') {
    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Headers: X-Requested-With');
    header("HTTP/1.1 200 OK");
    die();
}

É apenas para o desenvolvimento. Com isso eu estou esperando 9ms e 500ms e não 8s e 500ms. Eu posso fazer isso porque o aplicativo JS de produção estará na mesma máquina que a produção, portanto não haverá, OPTIONSmas o desenvolvimento é o meu local.

AntiCZ
fonte
4

Você não pode, mas pode evitar o CORS usando JSONP.

Jose Mato
fonte
2
Você só recebe uma solicitação OPTIONS se estiver fazendo algo não simples . Você só pode fazer solicitações simples (GET, sem cabeçalhos personalizados, sem dados de autenticação) com JSONP; portanto, o JSONP não pode substituir aqui.
Quentin
Sim, eu sei disso, mas não sei exatamente os requisitos do projeto. Eu sei que não é simples evitar cors, mas depende do projeto. No pior cenário para evitar o CORS, você precisa passar dados usando os parâmetros get. Então, JSONP poderia substituir coros dependendo dos requisitos do projeto (como você disse, usando pedidos simples)
Jose Mato
Eu não entendo o design do chamado pré-vôo. O que poderia causar insegurança se o cliente decidisse enviar dados para o servidor? Não acho que faça sentido dobrar a carga no fio.
Qian Chen
@ElgsQianChen isso provavelmente pode responder à sua pergunta stackoverflow.com/questions/15381105/…
Leo Correa
0

Depois de passar um dia e meio tentando resolver um problema semelhante, descobri que tinha a ver com o IIS .

Meu projeto de API da Web foi configurado da seguinte maneira:

// WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
    var cors = new EnableCorsAttribute("*", "*", "*");
    config.EnableCors(cors);
    //...
}

Eu não tinha opções de configuração específicas do CORS no nó web.config> system.webServer, como já vi em tantas postagens

Nenhum código específico do CORS no global.asax ou no controlador como decorador

O problema foram as configurações do pool de aplicativos .

O modo de pipeline gerenciado foi definido como clássico ( alterado para integrado ) e a Identidade foi definida como Serviço de Rede ( alterada para ApplicationPoolIdentity )

Alterar essas configurações (e atualizar o pool de aplicativos) corrigiu isso para mim.

Ju66ernaut
fonte
-2

O que funcionou para mim foi importar "github.com/gorilla/handlers" e depois usá-lo desta maneira:

router := mux.NewRouter()
router.HandleFunc("/config", getConfig).Methods("GET")
router.HandleFunc("/config/emcServer", createEmcServers).Methods("POST")

headersOk := handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type"})
originsOk := handlers.AllowedOrigins([]string{"*"})
methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"})

log.Fatal(http.ListenAndServe(":" + webServicePort, handlers.CORS(originsOk, headersOk, methodsOk)(router)))

Assim que eu executava uma solicitação Ajax POST e anexava dados JSON a ela, o Chrome sempre adicionava o cabeçalho Content-Type que não estava na minha configuração anterior do AllowedHeaders.

Kurt
fonte
-2

Uma solução que eu usei no passado - digamos que seu site esteja em mydomain.com e você precise fazer uma solicitação de ajax para foreigndomain.com

Configure uma reescrita do IIS do seu domínio para o domínio externo - por exemplo

<rewrite>
  <rules>
    <rule name="ForeignRewrite" stopProcessing="true">
        <match url="^api/v1/(.*)$" />
        <action type="Rewrite" url="https://foreigndomain.com/{R:1}" />
    </rule>
  </rules>
</rewrite>

no site mydomain.com - você pode fazer a mesma solicitação de origem e não há necessidade de opções :)

David McEleney
fonte
-2

Pode ser resolvido em caso de uso de um proxy que intercepte a solicitação e grave os cabeçalhos apropriados. No caso particular do verniz, estas seriam as regras:

if (req.http.host == "CUSTOM_URL" ) {
set resp.http.Access-Control-Allow-Origin = "*";
if (req.method == "OPTIONS") {
   set resp.http.Access-Control-Max-Age = "1728000";
   set resp.http.Access-Control-Allow-Methods = "GET, POST, PUT, DELETE, PATCH, OPTIONS";
   set resp.http.Access-Control-Allow-Headers = "Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since";
   set resp.http.Content-Length = "0";
   set resp.http.Content-Type = "text/plain charset=UTF-8";
   set resp.status = 204;
}

}

Rafa Cuestas
fonte
-5

Talvez haja uma solução (mas eu não a testei): você poderia usar o CSP (Política de segurança de conteúdo) para ativar seu domínio remoto e os navegadores talvez ignorem a verificação de solicitação do CORS OPTIONS.

Se encontrar algum tempo, testarei isso e atualizarei este post!

CSP: https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Content-Security-Policy

Especificação do CSP: https://www.w3.org/TR/CSP/

Arnaud Tournier
fonte
Eu apenas testada e ele não funciona, CORS é ainda necessária após CSP para o pedido xhr admissão ...
Arnaud Tournier