
Há quase 9 anos, a Cloudflare era uma pequena empresa, mas eu não trabalhava nela, era apenas um cliente. Um mês após o lançamento do Cloudflare, recebi uma notificação de que o DNS não parece estar funcionando no meu site jgc.org. O Cloudflare fez uma alteração nos Buffers de Protocolo e houve um DNS quebrado.
Escrevi imediatamente para Matthew Prince, encabeçando a carta “Onde está o meu DNS?”, E ele enviou uma resposta longa, cheia de detalhes técnicos ( leia toda a correspondência aqui ), à qual respondi:
De: John Graham Cumming
Data: 7 de outubro de 2010 9:14
Assunto: Re: Onde está o meu DNS?
Para: Matthew Prince
Relatório legal, obrigado. Definitivamente vou ligar se houver problemas. Provavelmente vale a pena escrever um post sobre isso quando você coletar todas as informações técnicas. Eu acho que as pessoas vão gostar de uma história aberta e honesta. Especialmente se você anexar gráficos a ele para mostrar como o tráfego cresceu após o lançamento.
Eu tenho um bom monitoramento no site e recebo um SMS sobre cada falha. O monitoramento mostra que a falha ocorreu entre 13:03:07 e 14:04:12. Os testes são realizados a cada cinco minutos.
Tenho certeza que você descobrirá. Você definitivamente não precisa de sua própria pessoa na Europa? :-)
E ele respondeu:
De: Matthew Prince
Data: 7 de outubro de 2010, 9:57
Assunto: Re: Onde está o meu DNS?
Para: John Graham Cumming
Obrigada Respondemos a todos que escreveram. Estou indo para o escritório agora e escreveremos algo no blog ou postaremos um post oficial em nosso quadro de avisos. Concordo plenamente, a honestidade é o nosso tudo.
Agora, a Cloudflare é uma empresa realmente grande, trabalho nela e agora tenho que escrever abertamente sobre nosso erro, suas conseqüências e nossas ações.
2 de julho de eventos
Em 2 de julho, implantamos uma nova regra nas regras gerenciadas do WAF, devido à falta de recursos do processador em cada núcleo do processador que processa o tráfego HTTP / HTTPS na rede Cloudflare em todo o mundo. Estamos constantemente aprimorando as regras gerenciadas do WAF em resposta a novas ameaças e vulnerabilidades. Em maio, por exemplo, corremos para adicionar uma regra para nos proteger de uma séria vulnerabilidade no SharePoint. O ponto principal do nosso WAF é a capacidade de implantar regras rápida e globalmente.
Infelizmente, a atualização da quinta-feira passada continha uma expressão regular que gastava muitos recursos do processador dedicados ao HTTP / HTTPS no retorno. Isso afetou nossos recursos principais de proxy, CDN e WAF. O gráfico mostra que os recursos do processador para atender ao tráfego HTTP / HTTPS atingem quase 100% nos servidores da nossa rede.

Usando recursos do processador em um dos pontos de presença durante um incidente
Como resultado, nossos clientes (e os clientes de nossos clientes) encontraram uma página com um erro 502 nos domínios do Cloudflare. Os erros 502 foram gerados pelos servidores Web front-end do Cloudflare, que ainda possuíam kernels gratuitos, mas não podiam entrar em contato com processos que processam o tráfego HTTP / HTTPS.

Sabemos quantos inconvenientes isso causou aos nossos clientes. Estamos terrivelmente envergonhados. E esse fracasso nos impediu de lidar efetivamente com o incidente.
Se você era um desses clientes, provavelmente o assustava, irritava e aborrecia. Além disso, não temos falhas globais há 6 anos. O alto consumo de CPU foi devido a uma regra WAF com uma expressão regular mal formulada, o que levou a um retorno excessivo. Aqui está a expressão culpada: (?:(?:\"|'|\]|\}|\\|\d|(?:nan|infinity|true|false|null|undefined|symbol|math)|\`|\-|\+)+[)]*;?((?:\s|-|~|!|{}|\|\||\+)*.*(?:.*=.*)))
Embora seja interessante por si só (e vou falar mais sobre isso abaixo), o serviço Cloudflare foi interrompido por 27 minutos, não apenas devido à expressão regular imprópria. Demorou um pouco para descrever a sequência de eventos que levaram à falha, por isso não respondemos rapidamente. No final do post, descreverei o backtracking em expressões regulares e direi o que fazer sobre isso.
O que aconteceu
Vamos começar em ordem. Todo o tempo é indicado aqui no UTC.
Às 13:42, um engenheiro da equipe de firewall fez uma pequena alteração nas regras para detectar o XSS usando um processo automático. Assim, um ticket de solicitação de mudança foi criado. Gerenciamos esses tickets pelo Jira (captura de tela abaixo).
Após 3 minutos, a primeira página do PagerDuty apareceu, relatando um problema com o WAF. Foi um teste sintético que verifica a funcionalidade do WAF (temos centenas deles) fora do Cloudflare para monitorar a operação normal. Em seguida, imediatamente houve páginas com notificações de falhas de outros testes ponta a ponta dos serviços Cloudflare, problemas globais de tráfego, erros 502 generalizados e vários relatórios de nossos pontos de presença (PoP) em cidades ao redor do mundo que indicavam falta de recursos do processador.


Recebi várias notificações, saí da reunião e já estava indo para a mesa quando o chefe do departamento de desenvolvimento de soluções disse que perdemos 80% do tráfego. Corri para nossos engenheiros da SRE que já estavam trabalhando no problema. A princípio, pensamos que era algum tipo de ataque desconhecido.

Os engenheiros do Cloudflare SRE estão espalhados pelo mundo e monitoram a situação o tempo todo. Normalmente, esses alertas notificam você sobre problemas locais específicos de escopo limitado, são monitorados em painéis internos e são resolvidos várias vezes ao dia. Mas essas páginas e notificações indicavam algo realmente sério, e os engenheiros do SRE anunciaram imediatamente o nível de gravidade do P0 e se voltaram para os engenheiros de gerenciamento e sistema.
Nossos engenheiros de Londres naquele momento estavam ouvindo uma palestra no salão principal. A palestra teve que ser interrompida, todos se reuniram em uma grande sala de conferências e mais especialistas foram convidados. Este não era um problema comum que o SRE pudesse descobrir por si mesmos. Era urgente conectar os especialistas certos.
Às 14:00, determinamos que havia um problema com o WAF e não havia ataque. O departamento de desempenho recuperou os dados do processador e ficou óbvio que a WAF era a culpada. Outro colaborador confirmou essa teoria com strace. Outra pessoa viu nos logs que o problema com o WAF. Às 14:02, toda a equipe me procurou quando foi proposto o uso de kill global - um mecanismo incorporado ao Cloudflare que desativa um componente em todo o mundo.
Como matamos a WAF globalmente é outra história. Não é assim tão simples. Utilizamos nossos próprios produtos e, como nosso serviço Access não funcionou, não conseguimos autenticar e entrar no painel de controle interno (quando tudo foi reparado, descobrimos que alguns membros da equipe perderam o acesso devido a um recurso de segurança que desabilita credenciais se Não use o painel de controle interno por muito tempo).
E não conseguimos acessar nossos serviços internos, como Jira ou o sistema de compilação. Precisávamos de uma solução alternativa, usada com pouca frequência (isso também precisaria ser resolvido). Finalmente, um engenheiro conseguiu interromper o WAF às 14:07 e, às 14:09, o nível de tráfego e processador em todos os lugares voltou ao normal. O restante dos mecanismos de defesa do Cloudflare funcionou como esperado.
Então começamos a restaurar o WAF. Como a situação estava fora do comum, realizamos testes negativos (perguntando-nos se o problema realmente era essa alteração) e positivos (garantindo que a reversão funcionasse) em uma cidade usando tráfego separado, transferindo clientes pagos a partir daí.
Às 14h52, estávamos convencidos de que entendíamos o motivo, fizemos uma correção e ativamos o WAF novamente.
Como funciona o Cloudflare
O Cloudflare possui uma equipe de engenheiros que gerenciam regras gerenciadas para o WAF. Eles tentam aumentar a taxa de detecção, reduzir o número de falsos positivos e responder rapidamente a novas ameaças à medida que surgem. Nos últimos 60 dias, 476 solicitações de alteração para regras gerenciadas pelo WAF foram processadas (em média, uma a cada 3 horas).
Essa alteração específica precisava ser implantada no modo de simulação, onde o tráfego real do cliente passa pela regra, mas nada é bloqueado. Usamos esse modo para verificar a eficácia das regras e medir a proporção de resultados falso-positivos e falso-negativos. Mas mesmo no modo de simulação, as regras devem ser realmente executadas e, nesse caso, a regra continha uma expressão regular que consumia muitos recursos do processador.

Como você pode ver na solicitação de mudança acima, temos um plano de implantação, um plano de reversão e um link para o procedimento operacional padrão interno (SOP) para esse tipo de implantação. O SOP para alterar a regra permite publicá-la globalmente. De fato, no Cloudflare, tudo é organizado de maneira totalmente diferente, e o SOP exige primeiro enviar o software para teste e uso interno para um ponto de presença interno (PoP) (usado por nossos funcionários), depois para um pequeno número de clientes em um local isolado, depois para um grande número de clientes e somente então para o mundo inteiro.
Aqui está como fica. Usamos git no sistema interno através do BitBucket. Os engenheiros de mudança enviam o código que eles constroem para o TeamCity e, quando a construção é aprovada, os revisores são designados. Quando a solicitação de pool é aprovada, o código é coletado e uma série de testes é realizada (novamente).
Se a montagem e os testes forem bem-sucedidos, uma solicitação de mudança será criada em Jira e a mudança deverá ser aprovada pelo supervisor ou especialista principal apropriado. Após a aprovação, ele é implantado na chamada "variedade de animais silvestres": DOG, PIG e Canary (cão, caxumba e canário).
O DOG PoP é o Cloudflare PoP (como qualquer outra de nossas cidades), usado apenas pelos funcionários do Cloudflare. O PoP para uso interno permite detectar problemas antes mesmo que a solução comece a receber o tráfego do cliente. Coisa útil.
Se o teste DOG for bem-sucedido, o código prosseguirá para o estágio PIG (porquinho da índia). Este é o Cloudflare PoP, onde uma pequena quantidade de tráfego livre do cliente flui através do novo código.
Se tudo estiver bem, o código vai para Canary. Temos três PoPs Canárias em diferentes partes do mundo. O tráfego de clientes pagos e gratuitos passa pelo novo código neles, e esta é a última verificação de erro.

Processo de lançamento do software Cloudflare
Se o código estiver correto no Canary, nós o liberaremos. Passar por todas as etapas - DOG, PIG, Canary, o mundo inteiro - leva várias horas ou dias, dependendo da alteração do código. Devido à diversidade da rede e dos clientes da Cloudflare, testamos exaustivamente o código antes de um lançamento global para todos os clientes. Mas o WAF não segue especificamente esse processo porque as ameaças precisam ser respondidas rapidamente.
Ameaças WAF
Nos últimos anos, houve significativamente mais ameaças em aplicativos convencionais. Isso se deve à maior disponibilidade de ferramentas de teste de software. Por exemplo, escrevemos recentemente sobre difusão ).

Fonte: https://cvedetails.com/
Muitas vezes, uma confirmação do conceito é criada e publicada imediatamente no Github para que as equipes que atendem ao aplicativo possam testá-lo rapidamente e garantir que ele esteja adequadamente protegido. Portanto, o Cloudflare precisa da capacidade de responder a novos ataques o mais rápido possível, para que os clientes tenham a oportunidade de corrigir seu software.
Um ótimo exemplo da resposta rápida do Cloudflare é a implantação da proteção contra vulnerabilidades do SharePoint em maio ( leia aqui ). Quase imediatamente após a publicação dos anúncios, notamos um grande número de tentativas de explorar a vulnerabilidade nas instalações do SharePoint de nossos clientes. Nossos funcionários monitoram constantemente novas ameaças e escrevem regras para proteger nossos clientes.
A regra que causou o problema na quinta-feira foi a proteção contra scripts entre sites (XSS). Também houve muitos outros ataques desse tipo nos últimos anos.

Fonte: https://cvedetails.com/
O procedimento padrão para modificar uma regra gerenciada para WAF requer teste de integração contínua (CI) antes da implantação global. Quinta-feira passada, fizemos e expandimos as regras. Às 13:31, um engenheiro enviou uma solicitação de pool aprovada com uma alteração.

Às 13:37, o TeamCity reuniu as regras, executou os testes e aprovou. O conjunto de testes WAF testa a funcionalidade principal do WAF e consiste em um grande número de testes de unidade para funções individuais. Após os testes de unidade, verificamos as regras do WAF com um grande número de solicitações HTTP. As solicitações HTTP verificam quais solicitações devem ser bloqueadas pelo WAF (para interceptar o ataque) e quais podem ser ignoradas (para não bloquear tudo e evitar falsos positivos). Mas não realizamos testes para o uso excessivo de recursos do processador, e o estudo dos logs de assemblies WAF anteriores mostra que o tempo de execução do teste com a regra não aumentou e era difícil suspeitar que não havia recursos suficientes.
Os testes foram aprovados e o TeamCity começou a implantar automaticamente a alteração às 13:42.

Mercúrio
As regras WAF foram projetadas para lidar com ameaças rapidamente, por isso as implantamos usando o repositório de pares de valores-chave distribuídos do Quicksilver, que distribui as alterações globalmente em segundos. Todos os nossos clientes usam essa tecnologia quando alteram a configuração no painel ou na API, e é graças a isso que respondemos às alterações com a velocidade da luz.
Conversamos um pouco sobre o Mercúrio. Costumávamos usar o Kyoto Tycoon como um repositório globalmente distribuído de pares de valores-chave, mas havia problemas operacionais com ele e escrevemos nosso repositório replicado em mais de 180 cidades. Agora, com o Quicksilver, enviamos alterações na configuração do cliente, atualizamos as regras WAF e distribuímos o código JavaScript gravado pelos clientes no Cloudflare Workers.
Leva apenas alguns segundos para uma configuração percorrer o mundo pressionando um botão em um painel ou chamando uma API para fazer uma alteração na configuração. Os clientes adoram essa configuração de velocidade. E Workers fornece a eles uma implantação de software global quase instantânea. Em média, o Quicksilver distribui cerca de 350 alterações por segundo.
E o Quicksilver é muito rápido. Em média, atingimos o 99º percentil de 2,29s para propagar alterações em todos os computadores do mundo. Geralmente a velocidade é boa. Afinal, quando você ativa uma função ou limpa o cache, isso acontece quase instantaneamente e em qualquer lugar. O envio de código através do Cloudflare Workers é na mesma velocidade. O Cloudflare promete a seus clientes atualizações rápidas no momento certo.
Mas, nesse caso, a velocidade nos enganou e as regras mudaram em todos os lugares em questão de segundos. Você provavelmente notou que o código WAF usa Lua. O Cloudflare faz uso extensivo de Lua no ambiente de produção e já discutimos os detalhes de Lua no WAF . O Lua WAF usa o PCRE internamente e aplica o rastreamento para correspondência. Não possui mecanismos de defesa contra expressões que estão fora de controle. Abaixo vou falar mais sobre isso e o que estamos fazendo com isso.

Antes da implantação das regras, tudo corria bem: a solicitação de pool foi criada e aprovada, o pipeline de CI / CD coletou e testou o código, a solicitação de alteração foi enviada de acordo com o SOP, que regula a implantação e a reversão, e a implantação foi concluída.

Processo de implantação do CloudFare WAF
O que deu errado
Como eu disse antes, implantamos dezenas de novas regras WAF toda semana e temos muitos sistemas de proteção contra as consequências negativas dessa implantação. E quando algo dá errado, geralmente é uma combinação de várias circunstâncias. Se você encontrar apenas uma razão, é claro que isso é calmante, mas nem sempre é verdade. Aqui estão os motivos que levaram à falha do nosso serviço HTTP / HTTPS.
- O engenheiro escreveu uma expressão regular que poderia levar a um retorno excessivo.
- Uma ferramenta que poderia impedir o uso excessivo dos recursos do processador usados pela expressão regular foi removida por engano durante a refatoração do WAF, algumas semanas antes - a refatoração era necessária para que o WAF consumisse menos recursos.
- O mecanismo regex não tinha garantias de complexidade.
- O conjunto de testes não pôde revelar consumo excessivo de CPU.
- O procedimento SOP permite a implantação global de alterações de regra não urgentes sem um processo de várias etapas.
- O plano de reversão exigia uma montagem WAF completa duas vezes, o que demorou muito tempo.
- O primeiro alerta de alerta de tráfego global funcionou tarde demais.
- Adiamos a atualização da página de status.
- Tivemos problemas ao acessar os sistemas devido a uma falha e a solução alternativa não foi bem-sucedida.
- Os engenheiros do SRE perderam o acesso a alguns sistemas porque suas credenciais expiraram por motivos de segurança.
- Nossos clientes não tiveram acesso ao painel ou à API do Cloudflare porque eles passam pela região do Cloudflare.
O que mudou desde a última quinta-feira
Primeiro, paramos completamente todo o trabalho em versões do WAF e fazemos o seguinte:
- Reintroduzindo a proteção contra recursos excessivos do processador que removemos. (Feito)
- Verifique manualmente todas as regras 3868 nas regras gerenciadas do WAF para localizar e corrigir outros casos em potencial de retorno excessivo. (Verificação concluída)
- Incluímos perfil de desempenho para todas as regras no conjunto de testes. (Esperado: 19 de julho)
- Mudamos para o mecanismo re2 ou Rust regex - ambos fornecem garantias no tempo de execução. (Esperado: 31 de julho)
- Reescrevemos o SOP para implantar as regras em etapas, como outros softwares no Cloudflare, mas, ao mesmo tempo, temos a capacidade de implantar uma emergência global se os ataques já começaram.
- Estamos desenvolvendo a possibilidade de retirada urgente do painel e da API do Cloudflare da região do Cloudflare.
- Estamos automatizando a atualização da página Status do Cloudflare .
A longo prazo, estamos abandonando o Lua WAF, que escrevi há vários anos. Migramos o WAF para o novo sistema de firewall . Portanto, o WAF será mais rápido e receberá um nível adicional de proteção.
Conclusão
Essa falha causou problemas para nós e nossos clientes. Reagimos rapidamente para corrigir a situação e agora estamos trabalhando em falhas nos processos que causaram a falha, e estamos nos aprofundando ainda mais para nos proteger de possíveis problemas com expressões regulares no futuro, mudando para uma nova tecnologia.
Temos muita vergonha desse fracasso e pedimos desculpas aos nossos clientes. Esperamos que essas mudanças garantam que isso não ocorra novamente.
Aplicação. Retrocesso de expressão regular
Para entender como a expressão:
(?:(?:\"|'|\]|\}|\\|\d (?:nan|infinity|true|false|null|undefined|symbol|math)|\`|\- |\+)+[)]*;?((?:\s|-|~|!|{}|\|\||\+)*.*(?:.*=.*)))
Com todos os recursos do processador, você precisa saber um pouco sobre como o mecanismo regex padrão funciona. O problema aqui é o padrão .*(?:.*=.*)
. (?:
e o correspondente )
não é um grupo excitante (ou seja, a expressão entre parênteses é agrupada como uma expressão).
No contexto de consumo excessivo de recursos do processador, esse padrão pode ser designado como .*.*=.*
. Como tal, o padrão parece desnecessariamente complexo. Mais importante, porém, no mundo real, essas expressões (semelhantes às expressões complexas nas regras da WAF) que solicitam que o mecanismo corresponda a um fragmento, seguido por outro fragmento, podem levar a um retorno catastrófico. E aqui está o porquê.

Em expressão regular .
significa que você precisa corresponder a um caractere. .*
- corresponder a zero ou mais caracteres "avidamente", ou seja, capturar um máximo de caracteres, portanto .*.*=.*
significa corresponder a zero ou mais caracteres e corresponder a zero ou mais caracteres, localize caractere literal =, corresponde a zero ou mais caracteres.
Faça a linha de teste x=x
. Corresponde à expressão .*.*=.*. .*.*
.*.*=.*. .*.*
até o sinal de igual corresponde ao primeiro x
(um dos grupos .*
corresponde a x
e o segundo a zero caracteres). .*
after = corresponde ao último x
.
Para essa comparação, são necessárias 23 etapas. O primeiro grupo .*
.*.*=.*
Atua "avidamente" e corresponde à linha inteira x=x
. O mecanismo passa para o próximo grupo .*
. Não temos mais caracteres para corresponder, portanto, o segundo grupo .*
Corresponde a zero caracteres (isso é permitido). Então o mecanismo vai para o sinal =
. Não há mais caracteres (o primeiro grupo .*
Usou toda a expressão x=x
), nenhuma correspondência ocorre.
E aqui o mecanismo de expressão regular retorna ao início. Ele vai para o primeiro grupo .*
E o compara x=
(em vez de x=x
) e assume o segundo grupo .*
. O segundo grupo .*
Mapeia para o segundo x
e, novamente, não temos mais caracteres. E quando o mecanismo atinge =
v .*.*=.*
Novamente, nada acontece. E ele volta atrás novamente.
Desta vez, o grupo .*
Ainda corresponde a x=
, mas o segundo grupo .*
mais x
, mas zero caracteres. O mecanismo tenta encontrar o símbolo literal =
no padrão .*.*=.*
, Mas não sai (afinal, o primeiro grupo já o ocupou .*
). E ele volta atrás novamente.
Desta vez, o primeiro grupo .*
Leva apenas o primeiro x. Mas o segundo grupo .*
"Avidamente" captura =x
. Já adivinhou o que vai acontecer? O mecanismo tenta corresponder ao literal =
, falha e faz o próximo retorno.
.*
x
. .*
=
. , =
, .*
. . !
.*
x
, .*
— , =
=
. .*
x
.
23 x=x
. Perl Regexp::Debugger , , .

, x=x
x=xx
? 33 . x=xxx
? 45. . x=x
x=xxxxxxxxxxxxxxxxxxxx
(20 x
=
). 20 x =
, 555 ! ( , x=
20 x
, 4067 , , ).

x=xxxxxxxxxxxxxxxxxxxx
:

, . , . , .*.*=.*
; ( ). , , foo=bar;
.
. x=x
90 , 23. . x=
20 x
, 5353 . . Y
.

, 5353 x=xxxxxxxxxxxxxxxxxxxx
.*.*=.*;

«», «» , . .*?.*?=.*?
, x=x
11 ( 23). x=xxxxxxxxxxxxxxxxxxxx
. , ?
.*
, .
«» . .*.*=.*;
.*?.*?=.*?;
, . x=x
555 , x=
20 x
— 5353.
, ( ) — . .
1968 , Programming Techniques: Regular expression search algorithm (« : »). , , , .

(Ken Thompson)
Bell Telephone Laboratories, Inc., -, -
. IBM 7094 . , . , .
, .
. . — .
. , . , .
, IBM 7094.
. — , . «·» . . . 2 , .
, ALGOL-60, IBM 7094. , .

. ⊕ .
1 . — a, b, c, S[i] NNODE.
NNODE , (. . 5)
.*.*=.*
, , .

. 0 , 0, 3 , 1, 2 3. .*
. 3 . =
=
. 4 . , .
, .*.*=.*
, x=x
. 0, . 1

, . .
, (1 2), . 2)

. 2 , , x
x=x
. x
, 1 1. x
, 2 2.
x
x=x
- 1 2. 3 4, =
.
=
x=x
. x , 1 1 2 2, =
2 3 ( 4). . 3)

x
x=x
. 1 2 1 2. 3 x
3.
x=x
, 4, . , . .
, 4 ( x=
) , , x
.
.