Desempenho e validação com regex: evite padrões ambíguos
Expressões regulares são úteis para verificar uma forma local: um prefixo, separadores ou comprimento. Elas não provam que um dado está correto, seguro ou barato de processar. Um padrão pode aceitar uma cadeia inadequada e outro, aparentemente simples, pode levar tempo demais diante de uma entrada quase correspondente. Este guia usa exemplos sintéticos para rever desempenho e validação sem transformar um padrão lento numa receita de ataque.
O problema
O risco surge quando várias partes da expressão podem consumir o mesmo texto e o motor tenta muitas divisões antes de falhar. Quantificadores aninhados, alternativas sobrepostas e finais ambíguos são sinais comuns. Num motor com backtracking, uma entrada longa quase válida pode obrigar retornos repetidos. O resultado pode ser uma interface bloqueada, consumo de CPU ou um serviço indisponível. A OWASP chama esta família de ReDoS quando outra pessoa consegue controlar a entrada.
Parênteses e asteriscos não tornam toda regex perigosa. O custo depende do motor, da expressão completa, dos limites de entrada e da chamada. Mesmo assim, esperar por um incidente é mau desenho. Um campo deve dizer que formato precisa, qual tamanho máximo aceita e o que acontece quando o valor não cabe. Se a regra representa identidade, decisão de negócio ou montante, também precisa de verificações semânticas fora da expressão.
/regex-tester ajuda a inspecionar correspondências com amostras não sensíveis. Não demonstra que uma regra é segura em qualquer tamanho nem substitui as defesas do sistema que recebe a entrada. Registe motor, flags e limites; sintaxe e comportamento variam entre linguagens. A especificação ECMAScript descreve regex em JavaScript, mas um servidor pode usar outra biblioteca.
Exemplo prático
Considere a expressão de demonstração ^(a+)+$. O problema é que o grupo repetido contém outro quantificador e pode dividir uma sequência de letras a de muitas maneiras. Estude-a apenas com um valor curto que deve corresponder, como aaaa, e um valor quase válido que termina num carácter diferente, como aaaa!. Não aumente o comprimento agressivamente nem execute a amostra em pedidos reais. O objetivo é reconhecer ambiguidade, não medir o dano possível.
Para o requisito inventado “de uma a sessenta letras a”, uma alternativa limitada é ^a{1,60}$. Ela explicita o máximo e deixa uma forma relevante de testar cada carácter. aaaa é válido; aaaa!, uma cadeia vazia e 61 letras são inválidos. Sessenta não é recomendação geral: pertence apenas a este campo fictício. Um identificador real precisa de alfabeto, normalização e comprimento definidos pelo respetivo contrato.
O contraste mostra que sintaxe compacta não é semântica clara. A versão limitada revela o máximo para quem revê. E um resultado booleano não explica por que um valor é aceitável. Uma regex pode exigir oito caracteres alfanuméricos, mas não prova que uma conta exista, que duas datas formem intervalo coerente ou que um valor seja autorizado. Essas perguntas exigem dados e regras adicionais.
Procedimento
Descreva o valor em linguagem comum antes de escrever a regex. Indique se aceita vazio, Unicode, espaços no início ou fim, separadores e todos os limites de comprimento. Decida onde a normalização ocorre; cortar espaços, mudar maiúsculas ou normalizar Unicode depois da validação pode alterar significado. Se a regra não pode ser explicada sem mostrar a expressão, formato e política de negócio provavelmente estão misturados.
Crie uma pequena tabela de amostras: uma válida normal, mínimo, máximo, caracteres parecidos mas proibidos, vazio e longa demais. Mantenha-as sintéticas. Execute-as no motor real da aplicação e registe correspondência, duração aproximada e grupos capturados quando necessários. Testes de regressão devem verificar resultados, não um número exato de milissegundos que muda entre máquinas.
Revise a estrutura antes de otimizar. Procure repetições dentro de repetições, alternativas em que uma opção é prefixo de outra e curingas sem limite antes de delimitador incerto. Pergunte se um quantificador pode virar intervalo finito, um separador pode ser explícito ou a análise pode ser dividida em passos. Evite copiar padrões largos da internet para campos restritos: eles frequentemente aceitam mais do que o produto precisa.
Acrescente defesas em volta da regex. Rejeite entrada longa antes da avaliação, limite o corpo do pedido e aplique orçamento de tempo quando a plataforma permitir. No navegador, não execute regra custosa a cada tecla sem máximo; limite tamanho e adie trabalho quando necessário. O serviço precisa dos mesmos limites mesmo que o cliente já tenha validado, pois um pedido pode ignorar a interface.
Explicação técnica
Um motor de backtracking escolhe uma rota e, se o final não encaixa, volta a uma escolha anterior para tentar outra. Em ^(a+)+$, cada grupo interno pode tomar quantidades diferentes de a; ao chegar a !, o motor explora combinações que falham. A visual anexa não promete tempos universais: mostra por que uma rota ambígua cresce muito mais depressa que uma verificação fixa. Alguns motores otimizam casos particulares, mas segurança não deve depender de otimização não garantida.
As âncoras ^ e $ mostram intenção de validar o texto todo, embora opções multilinha e API importem. Procurar uma substring não é exigir correspondência completa. Também importa se um objeto regex tem estado e se a entrada é transformada antes da comparação. Leia a documentação da linguagem e teste o código de integração, não apenas um padrão colado numa página.
Regex funciona melhor para gramáticas regulares pequenas: prefixos permitidos, separadores, caracteres e limites. A validação semântica vem depois: converta número e confirme intervalo; interprete data e confirme calendário; consulte valores permitidos; compare permissões no servidor. Separar fases produz erros úteis e evita expressão gigante que ninguém consegue rever com confiança.
Erros frequentes
Erro comum é tratar padrão copiado para email, URL ou senha como especificação completa. Muitos formatos têm exceções, internacionalização ou regras mutáveis. Regex rígida demais rejeita pessoas legítimas; ampla demais transfere trabalho difícil para outra camada. Defina a política do produto e seus limites em vez de afirmar que uma expressão implementa padrão externo inteiro.
Também falha testar só entradas felizes. Uma regra pode corresponder a dez exemplos normais e degradar-se num valor longo que falha no último carácter. Acrescente casos de fronteira e teste no contexto real local. Não publique nem automatize cargas crescentes contra sistemas que não controla. Detete ambiguidade numa cópia local e substitua-a por regra limitada.
Validação no navegador não é fronteira de segurança. Ajuda a pessoa usuária, mas um pedido pode ser construído sem interface. O servidor deve verificar novamente formato, comprimento, permissões e negócio. Codificação de saída continua necessária: corresponder a regex não torna um dado seguro para HTML, SQL, caminho ou comando do sistema.
Considerações
Prefira limites que venham do domínio: 64 caracteres porque o campo define, quatro segmentos porque o protocolo exige ou lista finita mantida. Meça entrada antes de converter ou guardar. Se o dado é grande por desenho, use analisador adequado ou processe por partes; não tente descrever documento inteiro com regex de uma linha.
Documente motor e flags junto da regra. Em JavaScript, modo Unicode, maiúsculas e classes de caracteres alteram resultado. Reveja versões de dependências quando o padrão chega a componente de terceiros. Para regras relevantes de segurança, outra pessoa deve conseguir ler intenção, amostras e limites sem decorar cada metacarácter.
Observe erros e latência sem guardar valores sensíveis completos. Contadores de rejeição por tamanho ou formato podem mostrar que uma regra precisa de explicação melhor. Se o tempo parecer anómalo, preserve amostras artificiais que reproduzam a forma e minimize o caso antes da correção. Observação ajuda a priorizar; não substitui limite preventivo.
Limitações
Este guia não certifica que uma regex concreta esteja livre de ReDoS nem oferece timeout para todos navegadores, servidores e motores. Desempenho depende de implementação, hardware, concorrência e entrada. O exemplo aninhado serve apenas para revisão controlada; não deve ser implantado como validador ou usado para testar serviços de terceiros.
Expressão limitada também não resolve autenticação, autorização, normalização de identidade, injeção ou privacidade. Cada contexto de saída requer codificação apropriada e cada decisão sensível requer controlos confiáveis. Consulte documentação do motor e orientações de segurança aplicáveis antes de usar padrão em fronteira exposta.
Lista de verificação
Descreva o dado e os limites antes de escrever regex. Teste amostras válidas, inválidas, vazias e longas demais no motor real. Evite quantificadores aninhados e alternativas concorrentes; prefira classes explícitas e intervalos finitos. Limite tamanho antes de avaliar e valide de novo no servidor. Separe formato e semântica, registe intenção e flags, observe falhas sem reter segredos e reveja qualquer padrão de rota pública.