Regex Denail of Service - O que é e como funciona ?

Estava "passeando" na internet quando me deparei com um vídeo: "Ataque com Regex - Negação de serviço usando expressões regulares", achei incríveil o conteúdo, então resolvi apresentar esse problema de segurança para vocês de um modo... um pouco mais simples. Vamos lá!

O que é ReDoS?

ReDoS (Regex Denail of Service), nada mais é do que um simples "erro lógico (ou falta de atenção)" na expressão regular, o que resulta num problema bem grande, podendo até tirar a aplicação do ar.

Como funciona o ReDoS?

Vamos fazer um exemplo prático usando o PHP. Iremos usar um site muito famoso por nos dar a possibilida de criar e depurar expressões regulares: regex101. Ao acessar o site logo na esquerda vemos algumas linguagens, como dito anteriormente, iremos usar o PHP, adicionando a seguinte Regex /^([a-z]+)+$/ e testando com uma string consideravelmente grande com o último caractere inválido, nos deparamos com isso: catastrophic backtracking, mas... o que seria isso, e por que aconteceu?. Se formos para o terminal interativo do php e executar o seguinte código abaixo veremos algo interessante.

var_dump(preg_match('/^([a-z]-)+$/', 'sjsjdjdjjaajyehajajsjdsA'));

O retorno não é um inteiro como diz a documentação, e sim um false, e quando isso acontence é porque ocorreu um erro; Para fins didáticos vamos ver o erro gerado:

preg_last_error();

Temos a seguinte mensagem: Backtrack limit was exhausted, isso quer dizer que o limite de backtracking explodiu (passou do limite).

O problema

Quando adionamos esta regex /^([a-z]+)+$/, estamos dizendo que queremos uma correspondência ou mais, e caso esse grupo seja encontrado, continue a procurar. E é exatamente aí que está o problema... não especificamos a quantidade de caracteres nem grupos que estávamos procurando, e quando passamos uma string muito grande com um caractere incorreto o tempo de processamento da string atingiu seu limite. A aplicação precisa verificar cada caractere com os outros, e como o último caractete é inválido, essa busca retornará a posição e tentará com os outros caracteres, nesse bagunça existem inúmeros caminhos a se fazer para encontrar as correspondências, enfim acontece o travamento da aplicação ou do navegador (no caso do Javascript, que só executa a próxima instrução quando a anterior terminar).

Obs: Algumas linguagens possuem um mecanismo que impede que o teste da string continue, que é o caso do php, evitando o pior dos problemas, mas... com o JavaScript que é orientado a eventos isso não acontece, portanto pode ocorrer o travamento do navegador ou da aplicação interna (caso esteja usandi o NodeJS).

Como resolver?

Essa resolução foi feita para a Regex apresentada neste tópico, mas pode ser consideradas para as outras inúmeras Regex (inválidas). Para resolvermos é bem simples, primeiramente iremos definir o limite de caracteres que queremos encontrar, depois iremos remover os sinais de + da Regex para evitar a continuidade.

Outras formas de resolução

Também podemos adicinar mais uma camada de segurança na aplicação, simplesmente verificando a quantidade de caracteres antes de chegar na Regex.

#segurança #boaspraticas

Interessante, então o usuário se quiser, ele pode acabar travando o servidor mandando várias requisições burlando o regex?

Não exatamente, como o Regex não possui limite de caracteres o invasor pode mandar uma `string` grande ou apenas uma pequena `string` com o último caracter inválido, desse modo o sistema irá travar por causa da quantidade de caminhos que ele pode fazer para chegar na correspondência final