Como um Regex pode derrubar o seu servidor
Há uns dias eu fiz o seguinte post no X. // Detect dark theme var iframe = document.getElementById('tweet-1887545470857191474-322'); if (document.body.className.includes('dark-theme')) { iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1887545470857191474&theme=dark" } Sim, parece exagero, mas é verdade. Um regex mal feito pode derrubar um servidor inteiro. Além de ser um problema que pode causar grandes prejuízos, é bem complicado de ser identificado. Esse tipo de problema é conhecido como Catastrophic Backtracking. Pessoas mal intencionadas podem explorar essa vulnerabilidade para derrubar um servidor no ataque conhecido como ReDoS. O que é Catastrophic Backtracking? Catastrophic Backtracking é um problema que ocorre quando um regex é mal feito e tenta encontrar todas as possíveis combinações de uma string. Por exemplo, considere o regex ^(a+)+$ e a string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!. O regex acima tenta encontrar todas as combinações de a na string, o que é um problema, pois o regex não é eficiente. Você pode estar se perguntando: "Mas como isso pode derrubar um servidor?" Bem, quando um regex é mal feito, ele pode tentar encontrar todas as combinações possíveis de uma string, o que pode consumir muitos recursos do servidor se a string for grande. Como algoritmos de regex são CPU-bound, se não houver mecanismos de proteção, um ataque ReDoS pode derrubar um servidor. Simulação de um ReDoS Para exemplificar um ReDoS, eu criei uma aplicação em Node.js que valida uma URL slug. A validação é feita com o regex ^([a-zA-Z-_0-9]+\/?)*$. Explicando o regex acima: ^: Início da string ([a-zA-Z-_0-9]+\/?)*: Grupo que aceita letras, números, hífens e underscores, seguido de uma barra opcional. Esse grupo pode se repetir zero ou mais vezes. $: Fim da string Isso significa que a URL slug pode conter letras, números, hífens e underscores, seguidos de uma barra opcional. Essa sequência pode se repetir zero ou mais vezes. A aplicação é bem simples. Ela recebe um URL slug via query string e valida se o slug é válido. O código do app está disponível aqui. Agora, vamos simular um ReDoS. Primeiro, vamos testar a aplicação com um URL slug válido: curl "http://localhost:3000/slugs?url=hello-world" Esse request deve retornar {"valid":true} sem maiores problemas. Agora, vamos testar a aplicação com um URL slug inválido: curl "http://localhost:3000/slugs?url=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%21" Bem, o request acima provavelmente não vai terminar. Se você observar o consumo de CPU do processo do Node.js, você verá que ele estará consumindo 100% da CPU. Verificando os resultados da simulação Para simplificar a simulação, eu criei um docker compose que executa duas instâncias do app, uma com o regex seguro e outra com o regex vulnerável. Além disso, eu criei um script que executa um teste de carga nas duas instâncias. Para executar a simulação, você precisa ter o Docker e o Docker Compose instalados. Primeiro, clone o repositório. Depois, execute o seguinte comando: docker compose up O comando acima vai criar as duas instâncias do app e executar o script de teste de carga. Depois de alguns segundos, você verá que a instância com o regex vulnerável vai consumir 100% da CPU, enquanto a instância com o regex seguro vai continuar funcionando normalmente. Ao final da execução do script, nenhuma request para a instância com o regex vulnerável vai ter sido completada enquanto a instância com o regex seguro vai ter completado todas as requests. Como se proteger de ReDoS Para se proteger de ReDoS, você pode seguir algumas práticas: Evite regex complexos Use ferramentas de análise estática que identificam regex que podem causar ReDoS Verifique se a engine de regex que você está usando tem proteção contra ReDoS e explore alternativas Conclusão Regex é uma ferramenta poderosa, mas pode ser perigosa se não for usada corretamente. O grande problema do ReDoS é que ele é difícil de ser identificado e não necessita de muitas requisições para derrubar um servidor. Por isso, é importante ter cuidado ao usar regex e sempre verificar se o regex que você está usando é seguro.

Há uns dias eu fiz o seguinte post no X.
// Detect dark theme var iframe = document.getElementById('tweet-1887545470857191474-322'); if (document.body.className.includes('dark-theme')) { iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1887545470857191474&theme=dark" }
Sim, parece exagero, mas é verdade. Um regex mal feito pode derrubar um servidor inteiro.
Além de ser um problema que pode causar grandes prejuízos, é bem complicado de ser identificado.
Esse tipo de problema é conhecido como Catastrophic Backtracking.
Pessoas mal intencionadas podem explorar essa vulnerabilidade para derrubar um servidor no ataque conhecido como ReDoS.
O que é Catastrophic Backtracking?
Catastrophic Backtracking é um problema que ocorre quando um regex é mal feito e tenta encontrar todas as possíveis combinações de uma string.
Por exemplo, considere o regex ^(a+)+$
e a string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!
.
O regex acima tenta encontrar todas as combinações de a
na string, o que é um problema, pois o regex não é eficiente.
Você pode estar se perguntando: "Mas como isso pode derrubar um servidor?"
Bem, quando um regex é mal feito, ele pode tentar encontrar todas as combinações possíveis de uma string, o que pode consumir muitos recursos do servidor se a string for grande.
Como algoritmos de regex são CPU-bound, se não houver mecanismos de proteção, um ataque ReDoS pode derrubar um servidor.
Simulação de um ReDoS
Para exemplificar um ReDoS, eu criei uma aplicação em Node.js que valida uma URL slug.
A validação é feita com o regex ^([a-zA-Z-_0-9]+\/?)*$
.
Explicando o regex acima:
-
^
: Início da string -
([a-zA-Z-_0-9]+\/?)*
: Grupo que aceita letras, números, hífens e underscores, seguido de uma barra opcional. Esse grupo pode se repetir zero ou mais vezes. -
$
: Fim da string
Isso significa que a URL slug pode conter letras, números, hífens e underscores, seguidos de uma barra opcional. Essa sequência pode se repetir zero ou mais vezes.
A aplicação é bem simples. Ela recebe um URL slug via query string e valida se o slug é válido.
O código do app está disponível aqui.
Agora, vamos simular um ReDoS.
Primeiro, vamos testar a aplicação com um URL slug válido:
curl "http://localhost:3000/slugs?url=hello-world"
Esse request deve retornar {"valid":true}
sem maiores problemas.
Agora, vamos testar a aplicação com um URL slug inválido:
curl "http://localhost:3000/slugs?url=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%21"
Bem, o request acima provavelmente não vai terminar.
Se você observar o consumo de CPU do processo do Node.js, você verá que ele estará consumindo 100% da CPU.
Verificando os resultados da simulação
Para simplificar a simulação, eu criei um docker compose que executa duas instâncias do app, uma com o regex seguro e outra com o regex vulnerável.
Além disso, eu criei um script que executa um teste de carga nas duas instâncias.
Para executar a simulação, você precisa ter o Docker e o Docker Compose instalados.
Primeiro, clone o repositório.
Depois, execute o seguinte comando:
docker compose up
O comando acima vai criar as duas instâncias do app e executar o script de teste de carga.
Depois de alguns segundos, você verá que a instância com o regex vulnerável vai consumir 100% da CPU, enquanto a instância com o regex seguro vai continuar funcionando normalmente.
Ao final da execução do script, nenhuma request para a instância com o regex vulnerável vai ter sido completada enquanto a instância com o regex seguro vai ter completado todas as requests.
Como se proteger de ReDoS
Para se proteger de ReDoS, você pode seguir algumas práticas:
- Evite regex complexos
- Use ferramentas de análise estática que identificam regex que podem causar ReDoS
- Verifique se a engine de regex que você está usando tem proteção contra ReDoS e explore alternativas
Conclusão
Regex é uma ferramenta poderosa, mas pode ser perigosa se não for usada corretamente.
O grande problema do ReDoS é que ele é difícil de ser identificado e não necessita de muitas requisições para derrubar um servidor.
Por isso, é importante ter cuidado ao usar regex e sempre verificar se o regex que você está usando é seguro.