web-dev-qa-db-fra.com

Utiliser eval () contre ast.literal_eval ()?

J'ai une situation avec du code où eval() est apparu comme une solution possible. Maintenant, je n'ai jamais eu à utiliser eval() auparavant, mais j'ai découvert beaucoup d'informations sur le danger potentiel que cela peut causer. Cela dit, je suis très prudent quant à l’utiliser.

Ma situation est que j'ai une entrée donnée par un utilisateur:

datamap = raw_input('Provide some data here: ')

datamap doit être un dictionnaire. J'ai cherché autour et trouvé que eval() pourrait résoudre ce problème. Je pensais pouvoir vérifier le type d'entrée avant d'essayer d'utiliser les données, ce qui constituerait une précaution de sécurité viable.

datamap = eval(raw_input('Provide some data here: ')
if not isinstance(datamap, dict):
    return

J'ai lu la documentation et je ne sais toujours pas si ce serait sécuritaire ou non. Eval évalue-t-il les données dès qu'elles sont entrées ou après l'appel de la variable datamap?

Est-ce que .literal_eval() du module ast est la seule option sûre?

144
tijko

datamap = eval(raw_input('Provide some data here: ')) signifie que vous évaluez réellement le code avant si vous estimez qu'il est dangereux ou non. Il évalue le code dès que la fonction est appelée. Voir aussi les dangers de eval .

ast.literal_eval déclenche une exception si l'entrée n'est pas un type de données Python valide. Le code ne sera donc pas exécuté s'il ne l'est pas.

Utilisez ast.literal_eval à chaque fois que vous avez besoin de eval. En règle générale, vous ne devriez pas évaluer les instructions littérales Python.

158
Volatility

ast.literal_eval() considère qu'un seul sous-ensemble de la syntaxe de Python est valide:

La chaîne ou le noeud fourni ne peut contenir que les structures littérales Python suivantes: chaînes, nombres, n-uplets, listes, dicts, booléens et aucun.

Le fait de passer __import__('os').system('rm -rf /a-path-you-really-care-about') dans ast.literal_eval() provoquera une erreur, mais eval() effacera votre disque avec joie.

Comme il semble que vous ne laissiez que l'utilisateur entrer un dictionnaire simple, utilisez ast.literal_eval(). Il fait ce que vous voulez en toute sécurité et rien de plus.

91
Blender

eval: C'est très puissant, mais aussi très dangereux si vous acceptez que les chaînes soient évaluées à partir d'entrées non fiables. Supposons que la chaîne évaluée soit "os.system ('rm -rf /')"? Il va vraiment commencer à supprimer tous les fichiers sur votre ordinateur.

ast.literal_eval: Évaluez en toute sécurité un noeud d'expression ou une chaîne contenant un Python littéral ou un affichage de conteneur. La chaîne ou le nœud fourni ne peut contenir que les structures littérales Python suivantes: chaînes, octets, nombres, tuples, listes, plans, ensembles, booléens, Aucun, octets et ensembles.

Syntaxe:

eval(expression, globals=None, locals=None)
import ast
ast.literal_eval(node_or_string)

Exemple:

# python 2.x - doesn't accept operators in string format
import ast
ast.literal_eval('[1, 2, 3]')  # output: [1, 2, 3]
ast.literal_eval('1+1') # output: ValueError: malformed string


# python 3.0 -3.6
import ast
ast.literal_eval("1+1") # output : 2
ast.literal_eval("{'a': 2, 'b': 3, 3:'xyz'}") # output : {'a': 2, 'b': 3, 3:'xyz'}
# type dictionary
ast.literal_eval("",{}) # output : Syntax Error required only one parameter
ast.literal_eval("__import__('os').system('rm -rf /')") # output : error

eval("__import__('os').system('rm -rf /')") 
# output : start deleting all the files on your computer.
# restricting using global and local variables
eval("__import__('os').system('rm -rf /')",{'__builtins__':{}},{})
# output : Error due to blocked imports by passing  '__builtins__':{} in global

# But still eval is not safe. we can access and break the code as given below
s = """
(lambda fc=(
lambda n: [
    c for c in 
        ().__class__.__bases__[0].__subclasses__() 
        if c.__== n
    ][0]
):
fc("function")(
    fc("code")(
        0,0,0,0,"KABOOM",(),(),(),"","",0,""
    ),{}
)()
)()
"""
eval(s, {'__builtins__':{}})

Dans le code ci-dessus, ().__class__.__bases__[0] rien que l'objet lui-même. Maintenant nous avons instancié toutes les sous-classes , ici notre objectif principal enter code here est de trouver une classe nommée n à partir de cela.

Nous avons besoin de code object et de function object de sous-classes instanciées. C'est une manière alternative de CPython d'accéder aux sous-classes d'objet et d'attacher le système.

De python 3.7 ast.literal_eval () est maintenant plus strict. L'addition et la soustraction de nombres arbitraires ne sont plus autorisées. lien

43
Kiran Kumar Kotari

Python impatient dans son évaluation, donc eval(raw_input(...)) évaluera l'entrée de l'utilisateur dès qu'il aura atteint la eval, quel que soit le résultat. vous faites avec les données après. Par conséquent, ceci n'est pas sûr, en particulier lorsque vous eval entrée utilisateur.

Utilisez ast.literal_eval.


Par exemple, entrer ceci à l'invite sera très, très mauvais pour vous:

__import__('os').system('rm -rf /a-path-you-really-care-about')
41
nneonneo

Si vous avez simplement besoin d'un dictionnaire fourni par l'utilisateur, la meilleure solution possible est json.loads. La principale limitation est que json dicts nécessite des clés de chaîne. De plus, vous ne pouvez fournir que des données littérales, mais c'est également le cas pour literal_eval.

3
Chinasaur

J'étais coincé avec ast.literal_eval(). Je l’essayais dans le débogueur IntelliJ IDEA et il renvoyait sans cesse None à la sortie du débogueur.

Mais plus tard, lorsque j'ai assigné sa sortie à une variable et que je l'ai imprimée dans le code. Cela a bien fonctionné. Exemple de code de partage:

import ast
sample_string = '[{"id":"XYZ_GTTC_TYR", "name":"Suction"}]'
output_value = ast.literal_eval(sample_string)
print(output_value)

Son python version 3.6.

1
M Haziq