Cloudflare 2019: O Regex que Derrubou uma Rede Global
Ouvir estudo
gerado ao ouvirGerado apenas no primeiro play
Com tecnologia Amazon Polly + OmniVoice
Em 2 de julho de 2019, uma única regra de WAF contendo um regex com backtracking catastrófico saturou 100% da CPU em todos os servidores da Cloudflare globalmente, derrubando serviços por aproximadamente 27 minutos. O incidente expôs falhas críticas no processo de deploy de regras, na ausência de proteção contra complexidade algorítmica e na insuficiência do pipeline de testes para detectar padrões patológicos.
Fatos do Incidente
- Empresa / Sistema
- Cloudflare — Rede de borda global (CDN, WAF, DDoS protection)
- Data
- 2 de julho de 2019
- Duração
- ~27 minutos de interrupção total; rollback completado às 14h52 UTC
- Impacto
- Queda de ~80% do tráfego processado pela Cloudflare; 100% de utilização de CPU em todos os PoPs globais
- Componente falho
- Regra WAF XSS (número 100137) com regex de backtracking catastrófico
- Engine de WAF
- Lua WAF rodando sobre NGINX / OpenResty
- Mecanismo de deploy
- Deploy global simultâneo sem rollout gradual (sem canary/staged rollout)
- Escala da rede
- Presença em mais de 193 cidades / PoPs na época do incidente
- Causa raiz
- Regex com backtracking catastrófico consumindo CPU ilimitada por requisição HTTP
Uma expressão regular mal construída — 18 tokens, um grupo de captura com quantificador aninhado — foi suficiente para paralisar a maior rede de borda do mundo por 27 minutos. Não foi um ataque. Foi um deploy de regra de WAF que passou por revisão de código, testes automatizados e staging, e ainda assim chegou à produção global com um padrão algoritmicamente explosivo. Este post-mortem examina o que aconteceu, por que os controles existentes falharam e o que a Cloudflare — e qualquer equipe que opera infraestrutura crítica — deveria ter feito de forma diferente.
O que aconteceu
Em 2 de julho de 2019, às 13h42 UTC, a Cloudflare realizou um deploy de novas regras para seu WAF gerenciado. Entre as regras estava a 100137, destinada a detectar ataques XSS. O regex central da regra era:
(?:(?:"|'|\]|\[|\\|(?:nan|infinity|true|false|null|undefined|symbol|math)|\`|\-|\+)+[)]*;?((?:\s|-|~|!|{}|\|\||&&)*(?:[0-9]{0,4}d?(?:x|xx|xxx|xxxx)?)(?:;|\\|,|\+|\-|\*|\/|%|&|\||\^|<|>|=|!)))+
O problema está no grupo ((?:\s|-|~|!|{}|\|\||&&)*) combinado com o quantificador externo +. Esse padrão cria o que a teoria da computação chama de backtracking catastrófico: para uma string que não casa com o padrão mas que é longa o suficiente, o motor de regex tenta exponencialmente mais combinações antes de desistir. O tempo de execução deixa de ser O(n) e passa a ser O(2ⁿ) em relação ao comprimento da entrada.
A Cloudflare utilizava a engine de regex do Lua (baseada em PCRE — Perl Compatible Regular Expressions), que não possui proteção nativa contra esse comportamento. Ao contrário de engines baseadas em autômatos finitos (como RE2 ou Rust's regex crate), o PCRE realiza backtracking sem limite de tempo por padrão.
Cada requisição HTTP processada pelo WAF passou a consumir 100% de um core de CPU pelo tempo que durasse a tentativa de matching. Com tráfego real chegando a centenas de milhares de requisições por segundo em cada PoP, todos os cores de todos os servidores em todos os data centers foram saturados quase instantaneamente. O sistema de proteção contra DDoS da própria Cloudflare — que depende dos mesmos processos NGINX — parou de funcionar. O tráfego legítimo foi descartado junto com o malicioso.
Linha do Tempo
- 1
13h42 UTC — Deploy iniciado
Regra WAF 100137 é publicada globalmente via pipeline de deploy padrão. Nenhum staged rollout. A regra vai para todos os PoPs simultaneamente.
- 2
13h42 UTC — CPU satura globalmente
Imediatamente após o deploy, a CPU em todos os servidores da Cloudflare atinge 100%. O tráfego HTTP começa a ser descartado. Alertas disparam nos sistemas de monitoramento.
- 3
13h47 UTC — Primeira resposta interna
Engenheiros de plantão são acionados. A correlação inicial aponta para o deploy de WAF como causa provável, mas o diagnóstico preciso ainda não foi confirmado.
- 4
~14h00 UTC — Diagnóstico confirmado
A equipe identifica a regra 100137 como a fonte do problema. Análise do regex revela o padrão de backtracking catastrófico. Decisão de rollback é tomada.
- 5
14h52 UTC — Rollback completo
A regra problemática é revertida em todos os PoPs. A CPU volta ao normal. O tráfego é restaurado. Duração total da interrupção: aproximadamente 27 minutos.
- 6
Horas seguintes — Análise forense
A equipe realiza análise detalhada do regex, documenta o mecanismo de backtracking, e começa a desenhar as mudanças de processo e tecnologia necessárias para evitar recorrência.
Fluxo de Falha: Deploy de Regra WAF → Saturação Global de CPU
O diagrama reconstrói o caminho percorrido pela regra defeituosa desde o pipeline de deploy até o impacto em cada PoP global, e como o backtracking catastrófico se propagou para saturar toda a capacidade de processamento.
- Engineer · Rule Author
- WAF Rules Repo · Git + CI
- Test Suite · (unit + staging)
- Global Deploy · (simultaneous)
- NGINX / OpenResty · HTTP Worker
- Lua WAF Engine · (PCRE regex)
- CPU Core · (saturated 100%)
- DDoS Protection · (NGINX-dependent)
- HTTP Request · (legitimate traffic)
- Customer Origin · Server
Causa Raiz: Backtracking Catastrófico + Deploy Global Atômico
A causa raiz técnica é o regex ((?:\s|-|~|!|{}|\|\||&&)*) com quantificador externo +, criando ambiguidade de matching que força o motor PCRE a explorar exponencialmente mais caminhos à medida que a string de entrada cresce. Para uma string de 30 caracteres que não casa, o número de tentativas pode ultrapassar bilhões. A causa raiz sistêmica é a combinação de três ausências: (1) nenhuma análise estática de complexidade de regex no pipeline de CI; (2) nenhum timeout ou limite de CPU por operação de regex na engine Lua/PCRE; (3) nenhum mecanismo de staged rollout que teria limitado o blast radius ao primeiro PoP afetado.
Por que os Controles Existentes Falharam
Este incidente é pedagogicamente valioso precisamente porque a Cloudflare tinha controles de qualidade. A regra passou por revisão de código humana. Passou por uma suíte de testes automatizados. Passou por um ambiente de staging. E ainda assim chegou à produção global com um padrão catastrófico. Por quê?
Revisão de código humana é eficaz para lógica de negócio, mas detectar backtracking catastrófico em regex requer conhecimento especializado em teoria de autômatos que não é universalmente distribuído entre engenheiros. O regex parecia razoável à inspeção visual — ele cobria os casos de XSS pretendidos nos testes de exemplo.
Testes automatizados validaram que a regra funcionava — que ela detectava os payloads XSS para os quais foi projetada. Mas os testes não incluíam casos de borda patológicos: strings longas que quase casam mas não casam, que são exatamente as entradas que maximizam o backtracking. Esse é um gap clássico: testar o caminho feliz não é suficiente para sistemas de segurança que processam input adversarial.
Staging replicava a configuração de produção, mas não replicava o volume de tráfego. Com poucas requisições por segundo, mesmo um regex patológico pode não ser observável — o impacto só se torna visível sob carga real. Isso é uma armadilha comum em testes de performance: o ambiente de staging raramente tem a pressão necessária para revelar problemas de complexidade algorítmica.
Deploy global simultâneo foi a decisão arquitetural que transformou um bug em uma catástrofe. Se a Cloudflare tivesse feito um staged rollout — começando com 1% dos PoPs, observando métricas de CPU por 5 minutos, e só então progredindo — o impacto teria sido contido. A ausência de staged rollout para mudanças de WAF é surpreendente dado o histórico da indústria com deploys graduais. A justificativa implícita provavelmente era velocidade de resposta a ameaças: quando você descobre um novo vetor de ataque, quer proteção global imediata. Mas essa velocidade tem um custo de risco que este incidente quantificou de forma dolorosa.
Remediação e Mudanças Implementadas
A Cloudflare publicou um post-mortem detalhado e implementou um conjunto substancial de mudanças. Analiso cada uma com o olho de quem já operou sistemas de borda em escala financeira:
1. Substituição do motor de regex (PCRE → RE2/Rust)
A mudança mais importante. RE2 e a crate regex do Rust garantem complexidade O(n) para qualquer expressão regular, eliminando a possibilidade de backtracking catastrófico por construção. Isso não é uma mitigação — é uma eliminação da classe de falha. O trade-off é que RE2 não suporta alguns recursos do PCRE (lookahead negativo, backreferences), o que pode requerer reescrita de regras existentes. Para um WAF, esse trade-off é claramente favorável: previsibilidade de performance supera expressividade de regex.
2. Análise estática de complexidade no CI
Ferramentas como rxxr2 ou análise de complexidade de regex baseada em teoria de autômatos foram adicionadas ao pipeline. Qualquer regex que possa exibir comportamento super-linear é rejeitado antes do merge. Isso é defense-in-depth correto: não confiar apenas na revisão humana para propriedades que podem ser verificadas automaticamente.
3. Staged rollout obrigatório para mudanças de WAF
Deploys de regras agora seguem um processo de rollout gradual: um subconjunto de PoPs primeiro, com observação automática de métricas de CPU e latência, antes de progressão global. Isso é o padrão correto para qualquer mudança que afete o caminho crítico de processamento de tráfego.
4. Limite de CPU por operação de regex
Mesmo com RE2, um timeout por operação de WAF foi adicionado como camada adicional de proteção. Se uma operação excede um threshold de tempo, ela é abortada e a requisição é passada (fail-open) ou bloqueada (fail-closed) conforme a política configurada. Isso é defense-in-depth: múltiplas camadas independentes.
5. Melhoria dos casos de teste
A suíte de testes foi expandida para incluir inputs patológicos: strings longas, strings que quase casam, strings com repetição de caracteres. Isso deveria ter existido desde o início para um sistema que processa input adversarial.
O conjunto de mudanças é exemplar. A Cloudflare não apenas corrigiu o sintoma (a regra específica) mas atacou a causa raiz em múltiplas camadas: a engine, o processo de deploy, o pipeline de CI e os testes. Isso é como um post-mortem bem executado deve resultar em ação.
Lições Técnicas
rxxr2, safe-regex (Node.js) e análise de autômatos podem detectar padrões ReDoS antes do deploy. Não existe justificativa para não incluir isso em qualquer CI que processe regras de regex em produção.re, Java java.util.regex) não garantem complexidade linear. Para WAFs, parsers e qualquer sistema que processe input não confiável, prefira RE2, Rust regex, ou Hyperscan.Trabalhei com sistemas de borda e WAF em contextos de serviços financeiros, onde um minuto de indisponibilidade tem custo mensurável e regulatório. O que me chama atenção neste incidente não é o bug em si — regex com backtracking catastrófico é um problema bem documentado desde os anos 1990 — mas a ausência de controles que são, francamente, básicos para sistemas nessa escala.
O que eu teria exigido antes desse deploy chegar à produção:
Primeiro, análise estática de complexidade de regex no CI é não-negociável para qualquer sistema que processe input de internet. Ferramentas como rxxr2 existiam antes deste incidente. O custo de integração é de horas, não semanas. A ausência disso em um WAF que protege uma fração significativa da internet é uma lacuna de processo difícil de justificar.
Segundo, o deploy global atômico me preocupa mais do que o regex em si. Um bug de configuração vai acontecer — a questão é se você tem mecanismos para conter o blast radius. Staged rollout com circuit breaker automático (se CPU > X% no PoP piloto, parar o rollout e alertar) é arquitetura padrão para sistemas críticos. A justificativa de velocidade de resposta a ameaças pode ser endereçada com um fast-path de rollout (5 minutos em 1% dos PoPs, não 30 minutos em 10%) sem abrir mão da segurança do processo.
Terceiro, e isso é menos óbvio: a escolha de PCRE para uma engine de WAF de alta frequência é uma decisão de risco que deveria ter sido revisitada independentemente deste incidente. RE2 e Hyperscan existem precisamente para esse caso de uso.
Veredicto: Uma Falha Sistêmica Evitável
O incidente da Cloudflare de julho de 2019 é um caso de estudo sobre como múltiplas camadas de controle podem falhar simultaneamente quando cada uma delas foi projetada para verificar uma propriedade diferente da que causou o problema. A revisão de código verificou lógica. Os testes verificaram corretude funcional. O staging verificou compatibilidade de configuração. Nenhuma camada verificou complexidade algorítmica de pior caso sob input adversarial — que é exatamente a propriedade que importa para um WAF. A lição central não é 'cuidado com regex'. É mais profunda: propriedades de segurança e performance de sistemas críticos devem ser verificadas por mecanismos adequados para cada propriedade, não assumidas como consequência de controles genéricos de qualidade. Backtracking catastrófico é verificável estaticamente. Deploy gradual é uma prática estabelecida. Timeouts de operação são primitivas básicas de engenharia de resiliência. A ausência de qualquer um desses controles em um sistema da escala e criticidade da Cloudflare é, em retrospecto, surpreendente.
Post-mortems, ADRs e deep dives de arquitetura no seu email — do jeito que um arquiteto lê.
Sem spam · cancele quando quiser
Pergunte ao Fernando sobre isto
Receba uma resposta focada sobre este estudo do meu assistente de IA, baseada no meu trabalho.
Participe da conversa
Entre para comentar
Confirme seu e-mail para participar — você também recebe a newsletter. Sem senha.