Existe-t-il un moyen de mettre du code malveillant dans une expression régulière?

138

Je souhaite ajouter une fonction de recherche d'expression régulière à ma page Web publique. À part le codage HTML de la sortie, dois-je faire quelque chose pour me prémunir contre les entrées d'utilisateurs malveillantes?

Les recherches Google sont submergées par des personnes qui résolvent le problème inverse - en utilisant des expressions régulières pour détecter les entrées malveillantes - ce qui ne m'intéresse pas. Dans mon scénario, l'entrée utilisateur est une expression régulière.

J'utiliserai la bibliothèque Regex dans .NET (C #).

MatthewMartin
la source
4
Cela peut dépendre de la langue et / ou de la bibliothèque de regex que vous utilisez.
aschepler
Un peu plus de matériel de lecture: ReDoS sur OWASP , ReDoS sur Wikipedia
joeytwiddle

Réponses:

216

Problèmes de déni de service

La préoccupation la plus courante avec les expressions rationnelles est une attaque par déni de service via des schémas pathologiques qui deviennent exponentiels - voire super-exponentiels! - et semblent donc prendre une éternité à résoudre. Celles-ci ne peuvent apparaître que sur des données d'entrée particulières, mais on peut généralement en créer une dans laquelle cela n'a pas d'importance.

Ceux-ci dépendront quelque peu de l'intelligence du compilateur regex que vous utilisez, car certains d'entre eux peuvent être détectés pendant la compilation. Les compilateurs Regex qui implémentent la récursivité ont généralement un compteur de profondeur de récursivité intégré pour vérifier la non-progression.

L'excellent article de Russ Cox de 2007 sur la correspondance d'expressions régulières peut être simple et rapide (mais lent en Java, Perl, PHP, Python, Ruby, ...) parle des façons dont la plupart des NFA modernes, qui semblent tous dériver du code de Henry Spencer , souffrent d'une grave dégradation des performances, mais là où un NFA de style Thompson ne rencontre pas de tels problèmes.

Si vous n'admettez que des modèles qui peuvent être résolus par les DFA, vous pouvez les compiler en tant que tels, et ils s'exécuteront plus rapidement, voire beaucoup plus rapidement. Cependant, cela prend du temps . Le document de Cox mentionne cette approche et ses problèmes associés. Tout se résume à un compromis temps-espace classique.

Avec un DFA, vous passez plus de temps à le construire (et à allouer plus d'états), alors qu'avec un NFA, vous passez plus de temps à l'exécuter, car il peut s'agir de plusieurs états en même temps, et le retour en arrière peut manger votre déjeuner - et votre processeur.

Solutions de déni de service

La façon la plus raisonnable d'aborder ces modèles qui sont sur le côté perdant d'une course avec la mort par la chaleur de l'univers est de les envelopper avec une minuterie qui place effectivement un temps maximum alloué à leur exécution. Habituellement, ce sera beaucoup, beaucoup moins que le délai d'expiration par défaut fourni par la plupart des serveurs HTTP.

Il existe différentes façons de les implémenter, allant d'une forme simple alarm(N)au niveau C, à une sorte de try {}blocage des exceptions de type alarme de capture, jusqu'à la création d'un nouveau thread spécialement créé avec une contrainte de synchronisation intégrée.

Légendes de code

Dans les langages regex qui admettent les appels de code, un mécanisme pour autoriser ou interdire ceux-ci de la chaîne que vous allez compiler doit être fourni. Même si les légendes de code ne servent qu'à coder dans la langue que vous utilisez, vous devez les restreindre; ils n'ont pas besoin d'appeler du code externe, mais s'ils le peuvent, vous avez des problèmes beaucoup plus importants.

Par exemple, en Perl, on ne peut pas avoir d'appels de code dans les expressions régulières créées à partir d'une interpolation de chaîne (comme ce serait le cas, comme ils sont compilés pendant l'exécution) à moins que le pragma spécial à portée lexique soit use re "eval";actif dans la portée actuelle.

De cette façon, personne ne peut se faufiler dans une légende de code pour exécuter des programmes système comme rm -rf *, par exemple. Parce que les légendes de code sont si sensibles à la sécurité, Perl les désactive par défaut sur toutes les chaînes interpolées, et vous devez faire tout votre possible pour les réactiver.

Défini par l'utilisateur \ P {roperties}

Il reste une question plus sensible à la sécurité liée aux propriétés de style Unicode - comme \pM, \p{Pd}, \p{Pattern_Syntax}ou \p{Script=Greek}- qui peuvent exister dans certains compilateurs regex que le soutien que la notation.

Le problème est que dans certains d'entre eux, l'ensemble des propriétés possibles est extensible par l'utilisateur. Cela signifie que vous pouvez avoir des propriétés personnalisées qui sont de véritables appels de code vers des fonctions nommées dans un espace de nom particulier, comme \p{GoodChars}ou \p{Class::Good_Characters}. La façon dont votre langue gère ces problèmes pourrait valoir la peine d'être examinée.

Bac à sable

En Perl, un compartiment sandbox via le Safemodule donnerait le contrôle sur la visibilité de l'espace de noms. D'autres langages proposent des technologies de sandboxing similaires. Si de tels périphériques sont disponibles, vous voudrez peut-être les examiner, car ils sont spécifiquement conçus pour une exécution limitée de code non approuvé.

tchrist
la source
4
La conversion NFA-> DFA peut produire une explosion d'état exponentielle, transformant un temps DoS en un espace DoS, ainsi que le coût en temps de génération du nombre exponentiel d'états.
Barry Kelly
mais il n'aura probablement pas besoin de toutes les capacités d'expressions régulières, que pensez-vous de la limitation de la puissance des expressions régulières comme l'a fait Google: google.com/intl/en/help/faq_codesearch.html#regexp
systemsfault
1
@Barry Tout à fait raison. J'avais pensé à la stratégie de Russ Cox décrite dans l'un de ses articles consistant à compiler progressivement des parties de la NFA dans un DFA équivalent, mais à la jeter si elle devenait trop grande. Mais il n'y a pas de solution miracle dans un DFA, même si Thompson l'a prouvé équivalent à un NFA, car vous devez payer le joueur de cornemuse à un moment ou à un autre. Le temps passé à demander plus d'espace au système d'exploitation, et les coûts de mise en place de la table de pages qui en découlent, peuvent parfois pousser l'échelle d'équilibrage dans l'autre sens et rendre la conversion du temps vers l'espace moins attrayante qu'elle ne le serait.
tchrist
20

Ajoutant à l'excellente réponse de tchrist: le même Russ Cox qui a écrit la page "Expression régulière" a également publié du code! re2 est une bibliothèque C ++ qui garantit une exécution O (length_of_regex) et une limite d'utilisation de la mémoire configurable. Il est utilisé dans Google afin que vous puissiez taper une expression régulière dans la recherche de code Google - ce qui signifie qu'il a été testé au combat.

Brian Bloniarz
la source
2
En effet. Vous pouvez échanger re2 dans le moteur regex de Perl avec un module, et il utilisera re2 si possible et Perl sinon. Fonctionne assez bien.
tchrist
6

Vous voudrez lire cet article:

Changement de contexte non sécurisé: inoculation d'expressions régulières pour la survie Le papier est plus sur ce qui peut mal tourner avec les moteurs d'expressions régulières (par exemple PCRE), mais il peut vous aider à comprendre ce à quoi vous êtes confronté.

Bruce Ediger
la source
1
Voici un avis de sécurité sur le code regcomp (3) de la libc GNU: securityreason.com/achievement_securityalert/93 Comme c'est opportun! Au moins sous Linux, la vulnérabilité est facile à démontrer: grep -E ". * {10,} {10,} {10,} {10,} {10,}"
Bruce Ediger
5

Vous devez non seulement vous soucier de la correspondance elle-même, mais également de la manière dont vous la faites. Par exemple, si votre entrée passe par une sorte de phase d'évaluation ou de substitution de commande sur son chemin vers le moteur d'expression régulière, il peut y avoir du code qui est exécuté à l'intérieur du modèle. Ou, si votre syntaxe d'expression régulière permet des commandes intégrées, vous devez également vous en méfier. Comme vous n'avez pas spécifié la langue dans votre question, il est difficile de dire avec certitude quelles sont toutes les implications en matière de sécurité.

Bryan Oakley
la source
1

Un bon moyen de tester vos RegEx pour les problèmes de sécurité (au moins pour Windows) est l' outil de fuzzing SDL RegEx publié récemment par Microsoft. Cela peut aider à éviter une construction RegEx pathologiquement mauvaise.

RandomNickName42
la source