Usando eval() de Python versus ast.literal_eval()
Tengo una situación con algún código que eval()
surgió como posible solución. Nunca antes había tenido que consumirlo eval()
, pero he encontrado mucha información sobre el peligro potencial que puede causar. Dicho esto, soy muy cauteloso al usarlo.
Mi situación es que un usuario me ha dado una entrada:
datamap = input('Provide some data here: ')
¿ Dónde datamap
tiene que haber un diccionario? Busqué por ahí y descubrí que eval()
esto podía solucionarlo. Pensé que podría comprobar el tipo de entrada antes de intentar utilizar los datos y que sería una medida de seguridad viable.
datamap = eval(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 datamap
de llamar a la variable?
¿ Es el ast
módulo .literal_eval()
la única opción segura?
datamap = eval(input('Provide some data here: '))
significa que realmente evalúa el código antes de considerarlo inseguro o no. Evalúa el código tan pronto como se llama a la función. Vea también los peligros deeval
.
ast.literal_eval
genera una excepción si la entrada no es un tipo de datos Python válido, por lo que el código no se ejecutará si no lo es.
Úselo ast.literal_eval
cuando lo necesite eval
. Por lo general, no deberías evaluar declaraciones literales de Python.
ast.literal_eval()
sólo considera válido un pequeño subconjunto de la sintaxis de Python:
La cadena o nodo proporcionado solo puede consistir en las siguientes estructuras literales de Python: cadenas, bytes, números, tuplas, listas, dictados, conjuntos, booleanos y
None
.
__import__('os').system('rm -rf /a-path-you-really-care-about')
Al pasar ast.literal_eval()
a generará un error, pero eval()
felizmente eliminará sus archivos.
Dado que parece que solo le permite al usuario ingresar un diccionario simple, use ast.literal_eval()
. Hace de forma segura lo que quieres y nada más.
eval:
esto es muy poderoso, pero también muy peligroso si acepta cadenas para evaluar desde entradas que no son de confianza. Supongamos que la cadena que se está evaluando es "os.system('rm -rf /')". Realmente comenzará a eliminar todos los archivos de 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 nodo proporcionado solo puede consistir en las siguientes estructuras literales de Python: cadenas, bytes, números, tuplas, listas, dictados, 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 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.__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 mismo. Ahora creamos instancias de todas las subclases , aquí nuestro enter code here
objetivo principal es encontrar una clase llamada n a partir de ella.
Necesitamos code
objetar y function
objetar desde subclases instanciadas. Esta es una forma alternativa de CPython
acceder a subclases de objetos y adjuntarlos al sistema.
Desde Python 3.7, ast.literal_eval() ahora es más estricto. Ya no se permiten la suma y resta de números arbitrarios. enlace