Segurança através da restrição do usuário ou como criar uma vulnerabilidade

Em 2019, a vulnerabilidade de negação de serviço envenenada por cache do CPDoS ) foi descoberta na rede CDN, o que permite envenenar o cache HTTP do provedor de CDN e causar uma negação de serviço. A vulnerabilidade ainda não recebeu muita publicidade, pois não foi vista em ataques reais. Mas quero falar sobre um dos métodos de envenenamento de cache separadamente. Substituição de método HTTP.


Se outras variantes de explorar a vulnerabilidade de uma maneira ou de outra dependem de bugs ou recursos de modificação de solicitação por um intermediário, a variante Substituição de Método é baseada na tática de mesmo nome, que não faz parte do padrão HTTP, carrega consigo problemas adicionais e que surgiram e se espalharam devido a descuido. relação com a segurança. Aqui vamos considerar.

Curta sobre o CPDoS, se você perdeu
, URI method .

, , , , - . — - - -, , - , . , .

, . , , -. - , , .


Limite o cliente, menos pode - menos irá quebrar


A necessidade de substituir o método na solicitação surgiu devido ao fato de algumas implementações de firewalls de aplicativos da Web e de clientes HTTP serem muito limitadas e não permitirem a execução de métodos diferentes de GET e POST. O problema não é que se tratava de uma restrição de implementação, mas de uma restrição intencional de clientes HTTP por uma política de segurança.

É claro que tudo foi realizado com a intenção de cortar o tráfego restrito, não padrão para clientes HTTP comuns. Mas, na busca pela segurança, todos os métodos, exceto GET e POST, foram cortados. Talvez porque esses sejam os únicos métodos que não são opcionais e necessários para servidores de uso geral.

Por que foi necessário introduzir uma restrição tão estrita não está claro. Sim, ataques com a introdução de vários caracteres para confundir o analisador são apenas o hobby de protocolos de texto. Mas você pode permitir um pouco mais de métodos, por exemplo, usar pelo menos aqueles que são descritos no próprio padrão ou registrados na IANA . Não valeu a pena remover completamente a verificação do método, mas você pode discar vários dos métodos mais populares e excluí-los daqueles que alteram o protocolo de interação e interrompem o trabalho com conexões no servidor proxy (CONNECT). Mas não, surgiu uma política de segurança que introduziu restrições e proibições desnecessárias para os clientes.

E os clientes estavam limitados aos errados. Eles queriam limitar a variabilidade das mensagens dos clientes HTTP e limitar os clientes que esses WAFs protegiam, os servidores de aplicativos finais e seus desenvolvedores. Agora, os desenvolvedores ficaram com apenas dois métodos que nem sempre eram suficientes para descrever a lógica do cliente HTTP.

Restrições são criadas para superá-las.


Era de se esperar que essa restrição excessiva, mais cedo ou mais tarde, começasse a interferir nos desenvolvedores da web. A ironia é que é tão fácil não se livrar dessas WAFs. Especialmente quando eles estão com clientes ou fornecedores. Desafiar as políticas de segurança de outras pessoas é uma questão desastrosa.

Devido à flexibilidade do HTTP, não é difícil contornar essa limitação; basta adicionar algo à solicitação em que você pode substituir o método. O WAF estrito verificará apenas o método na Linha de Solicitação (a primeira linha da solicitação) e ficará feliz em ver um GET ou POST aprovado lá. E o back-end poderá analisar o elemento adicionado e extrair o método real dele.

Você pode pesquisar no google vários artigos, realmente um montesobre como os proxies ruins quebraram os aplicativos REST e como os autores tiveram que passar o método real em um cabeçalho separado. Em todos eles, eles sugerem que você insira aproximadamente o mesmo cabeçalho (X-HTTP-Method, X-HTTP-Method-Override ou X-Method-Override - a ortografia varia um pouco) para indicar um método substituído. Muito, muito raramente, é possível encontrar referências que possam ser usadas para a mesma finalidade URI do componente de consulta.

O que está faltando nesses artigos é a seção Considerações de segurança. E eles simplesmente são.

A substituição do método é segura?


Às vezes, os desenvolvedores de aplicativos da web esquecem que, entre o cliente e o servidor, pode haver participantes intermediários que interagem com o protocolo HTTP: proxies, caches da web do provedor, CDN e WAF. A proliferação do TLS reduz bastante a chance de um participante intermediário entre o cliente e o servidor. Provavelmente, o único proxy entre o cliente e o back-end será o seu próprio servidor com o Nginx. E essa configuração é fácil o suficiente para testar em cenários típicos antes do lançamento.

Mas estamos entrando na era da CDN, e mais e mais aplicativos se escondem atrás das CDNs que lêem e manipulam o tráfego do usuário. Os back-end diretamente quase nunca atendem aos usuários e se escondem atrás de proxies reversos para aumentar a capacidade de resposta e o desempenho. Portanto, você precisará lembrar como a substituição de um método pode afetar o processamento de uma solicitação em um servidor de mediação.

Os ataques dos quais quero falar são principalmente aplicáveis ​​ao HTTP / 1.1. O HTTP / 2, de alguma maneira, herda o comportamento do padrão antigo; de certa forma, ele segue seu próprio caminho; portanto, a aplicabilidade de cada ataque ao novo padrão será considerada separadamente.

Ataques de cache


Na maioria das vezes, os servidores intermediários não levam em consideração as substituições de métodos, não verificam os cabeçalhos da família X-HTTP-Method-Override e trabalham com a solicitação usando seu método principal na linha de solicitação. E como o método substituído não está incluído na chave para procurar uma solicitação no cache (método + URI), esses servidores não podem distinguir POST de POST + POST + X-HTTP-Method-Override: DELETE. Isso significa que você não pode permitir o armazenamento em cache de nenhuma solicitação para um determinado URI se o back-end puder monitorar e executar métodos substituídos.

O documento CPDoS tem um bom exemplo do que acontece se você armazenar em cache essa solicitação. Quando um invasor disfarça uma solicitação POST como uma solicitação GET, o proxy não reconhece a substituição e trata a solicitação como uma solicitação GET legítima. O back-end, no entanto, reconhece o método substituído e executa o verbo descrito no cabeçalho X-HTTP-Method-Override - POST. Como o método POST não está definido para o URI de destino, o servidor gera um erro. Além disso, a resposta de back-end é armazenada no cache como uma resposta ao método original - GET. Agora, qualquer próxima solicitação GET para o mesmo URI retornará um erro em cache.


De fato, o ataque é um pouco mais amplo do que o apresentado no documento. Os autores se concentraram em armazenar o erro no cache, que não está em todo lugar (já) pode ser reproduzido. Mas se o método solicitado para o URI selecionado for definido e for executado com êxito, o proxy receberá uma resposta com status 200 e o armazenará em cache. Em seguida, solicitações subsequentes do mesmo URI para receber respostas para o método completamente errado. Nesse cenário, não há mais um requisito com um erro de cache de respostas 4XX, como na descrição original do CPDoS.

O problema inverso pode ocorrer. Se um cliente HTTP respeitável enviar uma solicitação GET + X-HTTP-Method-Override: PATCH (isso é ruim, mas mais sobre isso depois) e o cache já tiver uma resposta GET, o cliente receberá essa resposta em cache. Nesse caso, o back-end nunca receberá uma solicitação PATCH, o que pode violar a lógica do aplicativo no cliente e no servidor.

Você pode reduzir o efeito no cache criando as políticas de cache corretas e dividindo os recursos em dois grupos: aqueles para os quais a operação de substituição do método é inaceitável ou não necessária, as respostas podem ser armazenadas em cache e aqueles para os quais a operação de substituição do método é necessária, qualquer armazenamento em cache dessas respostas é inaceitável. . Porém, quanto menos recursos forem armazenados em cache, menos útil será a CDN e quanto mais tráfego atingir o back-end, mais o aplicativo será exposto à inundação HTTP.

Portanto, é melhor usar o cache HTTP o máximo possível, para isso é necessário que o servidor de cache possa distinguir entre solicitações com diferentes métodos substituídos. A primeira maneira é transferir a substituição do método do componente de consulta para o URI:

POST /some-uri HTTP/1.1
X-HTTP-Method-Override: DELETE
   ↓  ↓   ↓
POST /some-uri?method=DELETE HTTP/1.1

Agora, solicitações com métodos diferentes parecem diferentes para o cache, pois elas obtêm chaves diferentes. Alguns proxies preferem não armazenar em cache respostas a solicitações que contêm o componente de consulta no URI. Mas isso afetará apenas a eficiência do cache. Esse método sempre resolve problemas com o cache incorreto.

Outra maneira é deixar a substituição do método em um cabeçalho separado, mas digite uma chave secundária para encontrar a resposta no cache. Isso é possível com o cabeçalho Vary. Ao atender a solicitação, o servidor repetirá o cabeçalho com a substituição do método e refletirá o nome desse cabeçalho no cabeçalho Vary. Em seguida, nas solicitações a seguir, o servidor de cache usará o valor do método substituído como uma chave secundária ao procurar uma solicitação no cache.


Este método funciona se o servidor intermediário puder trabalhar com chaves secundárias. Geralmente, esse é o caso, mas o nível de confiança do proxy, que corta todos os métodos, exceto GET e POST, geralmente é mais baixo e é melhor verificar isso.

A substituição de um método por qualquer entidade dentro do corpo da solicitação tem exatamente as mesmas desvantagens que a substituição por um cabeçalho adicional - está fora da visibilidade do cache.

Ataques de enfileiramento de mensagens


Mesmo se os ataques de cache estiverem fechados, isso não é tudo. Um invasor, substituindo um método, pode tentar alterar o enquadramento da resposta e, assim, violar a correspondência dos pares solicitação-resposta de outros clientes. Ou force o lado do servidor do aplicativo a processar a mesma solicitação várias vezes.

A coisa mais importante necessária para isso é um servidor intermediário operando no modo proxy reverso. Ou seja, qualquer servidor de cache ou CDN. Esse proxy suporta um número relativamente pequeno de conexões com o back-end e multiplica solicitações de muitos clientes em cada um deles. Isso é necessário para suportar o carregamento de um grande número de conexões de clientes dos back-ends para o servidor proxy e para equilibrar a carga entre os back-end. O término das conexões TLS também ocorre no proxy, as conexões do cliente nunca são conectadas diretamente ao back-end.

Como agora as solicitações de clientes diferentes estarão na mesma conexão entre o back-end e o proxy, é necessário manter uma correspondência clara entre os pares solicitação-resposta. A maioria dos proxies não canaliza(pipelines) solicita ao back-end e trabalha com ele no modo solicitação-resposta. O modo solicitação-resposta é mais simples e está sujeito a praticamente uma ameaça - bloqueio de conexão. Se você deixar a conexão travada em um único par de solicitação-resposta, poderá causar um atraso ou até uma recusa em processar as seguintes solicitações (por exemplo, se você conseguir transbordar as filas de solicitações de proxy).

Solicitações de pipeline de proxies mais produtivas para o back-end - isso permite enviar um pacote de solicitações ao servidor imediatamente e aguardar sua execução. O desempenho é maior, mas há mais ameaças. Primeiro, o problema do bloqueio do cabeçalho da linha não desaparece em lugar nenhum - mesmo que o back-end possa varrer o pipeline de consulta e executá-lo em paralelo, ele não poderá ser enviado se o primeiro travar. Em segundo lugar, se você interromper o enquadramento da resposta, poderá confundir o proxy e interromper a correspondência dos pares solicitação-resposta; alguns clientes poderão receber respostas de outras pessoas ou, pelo menos, conseguir um fechamento instantâneo da conexão com o back-end.


A redefinição mais simples e divertida de um método é substituir GET pelo verbo HEAD. Se a resposta para o primeiro tiver um corpo, o segundo não. Além disso, todos os outros cabeçalhos são os mesmos, incluindo aqueles que fornecem o enquadramento da solicitação. Quando o proxy redireciona um HEAD substituído para o servidor, ele espera do servidor não apenas os cabeçalhos de resposta, mas também o corpo da resposta, que o back-end não enviará. Se o proxy e o servidor interagirem no modo de solicitação-resposta, a conexão será travada até que o tempo limite seja interrompido.

Se o servidor enviar as seguintes respostas (modo em pipeline), elas poderão ser analisadas não como respostas independentes, mas como parte da resposta incompleta anterior ao GET. O proxy os colocará (ou parte deles) no corpo da resposta "GET" e o enviará ao invasor para lê-los. Você pode criar um pseudo-GET para receber um arquivo grande e despejar algum tráfego entre o proxy e o back-end. O sucesso depende de como o back-end coloca os cabeçalhos de comprimento do conteúdo e codificação de transferência: agrupados para enquadrar as mensagens. O primeiro quase sempre permite que você obtenha um despejo, o segundo geralmente gera erros de análise e causa uma desconexão do back-end. Se você não tiver sorte, o pseudo-GET pode cobrir várias respostas na íntegra e terminar um pouco antes da próxima resposta.O proxy não poderá reconhecer esse problema e, para obter mais respostas nesse sentido, a correspondência solicitação-resposta será violada.


Mesmo que tudo o que foi conseguido com a substituição do método estivesse fechando a conexão entre o proxy e o back-end, isso pode ser suficiente para um ataque. Você pode enviar solicitações de serviço com essas solicitações - as conexões com os back-ends quebram constantemente. Não há muitos deles, e a redescoberta leva tempo; como resultado, é possível obter uma diminuição significativa no desempenho da comunicação de back-end do proxy e, assim, reduzir a taxa de transferência do serviço.

Repetição automática de spam


Eu disse acima que os pedidos do formulário GET + X-HTTP-Method-Override: PATCH de clientes respeitáveis ​​são ruins. E isso é ruim porque os métodos têm duas propriedades: segurança e idempotência . Segurança significa que o método não altera o estado do servidor (somente leitura) e não nos interessa no contexto deste artigo. A idempotência do método garante que a solicitação repetida tenha o mesmo efeito que uma única solicitação. Você pode desenhar uma analogia: (a = 5)- solicitação idempotente e (a += 2) - não idempotente.

Esta propriedade é o que nos interessa. Se a conexão entre o cliente e o servidor interromper repentinamente, o cliente, sabendo que o método é idempotente, poderá reenviar automaticamente a solicitação. Proxies se comportam da mesma maneira. Solicitações não idempotentes não são repetidas automaticamente porque não se sabe como elas afetam o servidor e o que o cliente receberá no final. Acho que todo mundo conhece os pop-ups no navegador: "Tem certeza de que deseja repetir a solicitação?"

Se você mascarar um método não idempotente como idempotente, em caso de erros, ele não será descartado, mas será redirecionado ao servidor novamente. Mesmo que o cliente considere o método de solicitação real antes de reenviá-la, isso não ajudará muito, porque o servidor proxy não está ciente da substituição do método e repetirá essas solicitações.

Se um invasor conseguir forçar desconexões entre o back-end e os clientes, ele poderá fazer com que o servidor execute solicitações não idempotentes várias vezes e reduza a confiabilidade e a previsibilidade do aplicativo. Na seção anterior, descobrimos uma maneira de causar interrupções de conexão com a mesma substituição de método. Embora tenhamos de lembrar que a Internet é uma rede não confiável, por definição, e o próprio aplicativo está em perigo.

Para se proteger desse ataque, você deve usar apenas métodos que não adicionam novas propriedades à solicitação como transporte. O POST é um bom candidato, porque, por padrão, não é seguro nem idempotente.

Esse antigo HTTP / 1.1, como no HTTP / 2?


O HTTP / 2 mudou a maneira como as solicitações são transportadas entre os nós, mas não mudou seu significado lexical. Portanto, naqueles ataques relacionados ao valor da solicitação, o HTTP / 2 se comporta da mesma maneira. Mas os ataques de "transporte" não são reproduzidos, pois eles já são levados em consideração no padrão.

Ataques no cache são reproduzidos de maneira semelhante ao HTTP / 1 e a proteção é semelhante.

Os ataques do serviço de enfileiramento de mensagens não são aplicáveis ​​ao HTTP / 2. As mensagens HTTP nele são divididas em quadros separados, com cabeçalhos de quadro separados que determinam explicitamente o comprimento e o final da mensagem. Como se o invasor não mudasse o método e modificasse os cabeçalhos HTTP, isso não afetará o quadro da mensagem. Roubar a resposta falhará.

Ataques à repetição de mensagens não idempotentes são aplicáveis ​​mesmo levando em consideração o fato de que no HTTP / 2 existemmecanismo de notificação da última solicitação processada . No HTTP / 2, várias solicitações são multiplicadas no mesmo TCP e, portanto, criam fluxos . Cada thread tem seu próprio número. Se o servidor HTTP / 2 for desconectado, poderá indicar o número da última solicitação processada no GOAWAY. Solicitações com um número maior sempre são seguras para redirecionar; solicitações com um número menor são redirecionadas apenas se forem idempotentes. Se uma solicitação com um método substituído parecer idempotente para um servidor proxy, o proxy a encaminhará para o servidor.

Como substituir com segurança um método


A resposta curta não é possível. É melhor não usar substituições de método. E desative completamente o suporte no back-end, se houver. Bloquear clientes HTTP que substituem métodos. Recuse o proxy / WAF, que corta os métodos "extras".

Se você precisar, de alguma forma, conviver com a redefinição do método, evite edições suficientes no back-end. Primeiro, é aconselhável substituir o método apenas através do componente de consulta do URI.

Em segundo lugar, deve haver uma lista branca da transformação de métodos: que são aceitáveis ​​como "transporte" e quais são os resultantes. Não deve haver funções generalizadas de transformação quando qualquer método puder ser substituído por qualquer. O método de "transporte" não deve ter as propriedades de segurança e idempotência se o resultante não tiver. Transformações perigosas devem ser proibidas, a mesma substituição GET -> HEAD.

Preciso corrigir um proxy / WAF com problema?


Se o proxy implementar apenas os métodos GET e POST e bloquear os outros por um motivo ou outro, definitivamente sim. Você pode otimizá-lo principalmente para GET e POST, mas bloquear outros métodos é uma má idéia. O que ainda cria um abismo de desconfiança no produto: se coisas básicas são bloqueadas, o que esperar da implementação de problemas mais complexos?

Se você estiver preocupado com a segurança de aplicativos Web protegidos, pode valer a pena proteger aplicativos de políticas de substituição de método não seguras. Obviamente, no caso geral, sem conhecer os detalhes da implementação do aplicativo Web, é impossível proteger completamente o aplicativo contra substituições incorretas, mas você pode cobrir parcialmente os usuários que simplesmente não conhecem o problema. É necessário não apenas proteger contra o envenenamento de seu próprio cache, mas também possibilitar ou desativar a substituição de cada aplicativo protegido. Para fazer isso, acompanhe os cabeçalhos mais usados.Método X-HTTP, Substituição de método X-HTTP e Substituição de método X. O rastreamento da redefinição no componente de consulta do URI não faz muito sentido: o cache não envenena essa solicitação e a consulta pode ser muito longa e ter um formato completamente arbitrário.

?


Desenvolvedores de segurança, não limite os desenvolvedores de aplicativos às políticas de segurança. Eles ainda descobrirão como contorná-los, e quanto mais flexível o protocolo, mais fácil será fazê-lo. É muito provável que eles não o chutem e esperem até você tornar as restrições mais razoáveis, mas simplesmente ignorá-las.

Se você descobriu como implementar algo no protocolo, mas isso substitui ou contraria um dos principais conceitos do padrão, certamente haverá problemas de compatibilidade e segurança. E eles precisam ser cobertos ao mesmo tempo que a decisão. Toda vez. Se você seguiu esse conselho e não viu avisos de segurança, não o duplique em toda a Internet. Seja sempre crítico com a decisão e descubra o que pode dar errado .

Em vez de um posfácio


Quais problemas do servidor proxy você encontrou? O que tinha que ser contornado e como?

All Articles