¿Usando eval () de python vs. ast.literal_eval ()?

Tengo una situación con algún código donde eval() surgió como una posible solución. Ahora nunca he tenido que usar eval() antes, pero he encontrado mucha información sobre el peligro potencial que puede causar. Dicho esto, soy muy cauteloso acerca de su uso.

Mi situación es que tengo un input dado por un usuario:

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

Donde datamap necesita ser un diccionario. Busqué y encontré que eval() podría resolver esto. Pensé que podría ser capaz de verificar el tipo de entrada antes de intentar usar los datos y eso sería una precaución de seguridad viable.

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

Leí los documentos y todavía no tengo claro si esto sería seguro o no. ¿Eval evalúa los datos tan pronto como se ingresan o después de datamap variable datamap ?

¿Es el .literal_eval() del módulo ast la única opción segura?

datamap = eval(raw_input('Provide some data here: ')) significa que usted realmente evalúa el código antes de que lo considere inseguro o no. Evalúa el código tan pronto como se llama la función. Vea también los peligros de eval .

ast.literal_eval genera una excepción si la entrada no es un tipo de datos válido de Python, por lo que el código no se ejecutará si no lo es.

Use ast.literal_eval siempre que necesite eval . Normalmente no deberías evaluar sentencias de Python literales.

ast.literal_eval() solo considera válido un pequeño subconjunto de la syntax de Python:

La cadena o el nodo proporcionado solo puede consistir en las siguientes estructuras literales de Python: cadenas, números, tuplas, listas, dados, booleanos y ninguno.

Pasar __import__('os').system('rm -rf /a-path-you-really-care-about') a ast.literal_eval() generará un error, pero eval() borrará felizmente su disco.

Como parece que solo estás permitiendo que el usuario ingrese un diccionario simple, usa ast.literal_eval() . Con seguridad hace lo que quieras y nada más.

Python está ansioso por su evaluación, por lo que eval(raw_input(...)) evaluará la entrada del usuario tan pronto como eval la eval , independientemente de lo que haga con los datos posteriormente. Por lo tanto, esto no es seguro , especialmente cuando eval la entrada del usuario.

Utilice ast.literal_eval .


Como ejemplo, ingresar esto en el indicador será muy, muy malo para usted:

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

eval: Esto es muy poderoso, pero también es muy peligroso si acepta cadenas para evaluar a partir de entradas no confiables. Supongamos que la cadena que se está evaluando es “os.system (‘rm -rf /’)”? Realmente empezará a eliminar todos los archivos en su computadora.

ast.literal_eval: evalúa de forma segura un nodo de expresión o una cadena que contiene un literal de Python o una visualización de contenedor. La cadena o el nodo proporcionado solo puede consistir en las siguientes estructuras literales de Python: cadenas, bytes, números, tuplas, listas, dados, conjuntos, booleanos, Ninguno, bytes y conjuntos.

Sintaxis:

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

Ejemplo:

 # python 2.x - doesn't accepts operators in the string 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.__name__ == n ][0] ): fc("function")( fc("code")( 0,0,0,0,"KABOOM",(),(),(),"","",0,"" ),{} )() )() """ eval(s, {'__builtins__':{}}) 

En el código anterior ().__class__.__bases__[0] nada más que el objeto en sí. Ahora creamos una instancia de todas las subclases , aquí nuestro principal objective de enter code here es encontrar una clase llamada n de ella.

Necesitamos code objeto y function objeto de subclases instanciadas. Esta es una forma alternativa de CPython para acceder a las subclases de objeto y adjuntar el sistema.

Desde python 3.7 ast.literal_eval () ahora es más estricto. Ya no se permite la sum y resta de números arbitrarios. enlazar

Si todo lo que necesita es un diccionario proporcionado por el usuario, la mejor solución posible es json.loads . La principal limitación es que json dicts requiere claves de cadena. También solo puede proporcionar datos literales, pero ese es también el caso de literal_eval .

Yo estaba atrapado con ast.literal_eval() . Lo estaba probando en el depurador IntelliJ IDEA, y seguía devolviendo None en la salida del depurador.

Pero más tarde, cuando asigné su salida a una variable y la imprimí en código. Funcionó bien Compartir código de ejemplo:

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

Su versión de python 3.6.