2010-01-14 9 views
4

Je veux prendre une entrée de l'utilisateur peut être comme foo() > 90 and boo() == 9 or do() > 100 et utiliser eval sur le côté serveur pour évaluer cette expression.Parsing, sécurisation de l'expression python avant de passer à eval()

Pour la sécurité, je veux restreindre l'utilisateur à ajouter des fonctions et des opérateurs limités en gardant une vérification (contre une structure de données) avant de passer à la fonction eval.

PS: entrée provient d'une page Web

Merci

+0

L'analyser de la même manière qu'un interpréteur Python le ferait? –

Répondre

1

la façon plus sûre est de tout faire à l'arrière. Les utilisateurs saisissent simplement les paramètres nécessaires. Par exemple, vous pouvez les inviter à saisir des valeurs numériques pour foo(), boo() et do(). Ensuite, à l'arrière, transmettez ces valeurs aux fonctions appropriées pour effectuer les calculs.

1

La vérification la plus simple serait peut-être de regarder tous les mots de l'expression et de les comparer à une liste blanche. Rejeter l'expression si l'un des mots ne figure pas dans la liste blanche.

import re 

expr = "foo() > 90 and boo() == 9 or do() > 100" 
whitelist = "and or foo boo do".split() 
for word in re.findall(r"[a-zA-Z_]\w+", expr): 
    if word not in whitelist: 
     raise Exception("Warning! Warning!") 

Cela fonctionne parce que vous avez un domaine limité que vous avez besoin que les utilisateurs puissent s'exprimer, et aussi parce que je ne pense pas qu'il y ait un moyen de causer des dommages avec eval sans utiliser des identificateurs.

Vous devez toutefois faire attention à ce que votre liste blanche n'inclue pas par mégarde des identifiants Python potentiellement malveillants.

+1

Vous pourriez faire quelque chose d'un peu plus compliqué que de simplement diviser sur des limites non-mot, afin de garder les fonctions de parens sur les identifier comme des fonctions? conserver les fonctions, les mots-clés (et les variables spéciales?) dans des pools distincts. – Roboprog

1

Vous devez verrouiller le format d'entrée, ou ce sera un trou de sécurité béant. Soit implémenter un analyseur complet, comme le suggère lpthnc, avec un ensemble d'opérations raisonnable (mais pas plus), ou au moins utiliser une expression régulière (ou plusieurs motifs regex dans une hiérarchie et/ou une boucle de correspondance) pour éliminer les motifs reconnus; et rejeter les entrées suspectes comme "non autorisées".

3

Fondamentalement, la seule façon de faire est de l'analyser vous-même. Vous naviguez dans l'arbre d'analyse pour garantir que chaque partie est dans une liste blanche d'opérations parfaitement bénignes et sécurisées, rendant l'expression entière sûre par construction. La réponse de Ned Batchelder est en fait une (simple) forme de ceci. Vous pourriez passer à eval() après cela, bien que, quel serait le point? Vous pouvez calculer la valeur de chaque sous-expression dans le cadre de la vérification (c'est particulièrement une bonne idée car cela rend votre parser résistant aux changements de syntaxe Python et ainsi de suite). Cette liste blanche doit être extrêmement petite, et il y a beaucoup de choses que vous pourriez penser correctes, mais qui ne le sont pas (par exemple, l'opérateur d'appel général, la fonction getattr). Tu dois être très prudent.

Une liste noire est absolument hors de question (comme la suggestion de "rejeter les entrées suspectes"). Rejeter tout ce qui n'est pas forcément bon. Si vous ne le faites pas, il sera trivial de contourner votre filtre et de donner une expression qui fait quelque chose de mal, sauf la possibilité improbable que votre code soit meilleur que tout autre filtre blacklisté pour Python jamais créé.

Il y a eu des tentatives pour restreindre l'exécution de Python, l'un est le module infâme et maintenant désactivé (parce qu'il n'a pas fonctionné) rexec (et compagnie), et l'autre est PyPy's sandbox. Cette deuxième option ne fait pas exactement ce que vous avez demandé, mais elle mérite certainement d'être étudiée. C'est probablement ce que j'utiliserais - cela signifie simplement que ce ne sera pas aussi facile que eval(safematize(user_input)).