使用Python的eval()与ast.literal_eval()
我遇到了一段代码,里面提到了eval()
这个函数,可能可以用来解决我的问题。不过我之前从来没有用过eval()
,但我看到很多关于它可能带来的危险的信息。所以我对使用它非常谨慎。
我的情况是,用户会输入一些数据:
datamap = input('Provide some data here: ')
这里的datamap
需要是一个字典。我查了一下,发现eval()
可能可以处理这个问题。我想在使用这些数据之前,先检查一下输入的类型,这样可能是一个有效的安全措施。
datamap = eval(input('Provide some data here: ')
if not isinstance(datamap, dict):
return
我看了文档,但还是不太清楚这样做是否安全。eval()
是立刻评估输入的数据,还是在调用datamap
变量的时候才评估?
那ast
模块里的.literal_eval()
是唯一安全的选择吗?
6 个回答
eval:
这个功能非常强大,但如果你接受来自不可信输入的字符串来进行评估,那就非常危险了。想象一下,如果被评估的字符串是“os.system('rm -rf /')”,那么它真的会开始删除你电脑上的所有文件。
ast.literal_eval:
这个功能可以安全地评估一个表达式节点或者一个包含Python字面量或容器显示的字符串。提供的字符串或节点只能包含以下Python字面量结构:字符串、字节、数字、元组、列表、字典、集合、布尔值、None、字节和集合。
语法:
eval(expression, globals=None, locals=None)
import ast
ast.literal_eval(node_or_string)
示例:
# 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__':{}})
在上面的代码中,().__class__.__bases__[0]
只是对象本身。
现在我们实例化了所有的子类,我们的主要目标是从中找到一个名为n的类。
我们需要从实例化的子类中获取code
对象和function
对象。这是通过CPython
访问对象的子类并附加系统的另一种方式。
从Python 3.7开始,ast.literal_eval()变得更加严格。任意数字的加法和减法不再被允许。链接
ast.literal_eval()
这个函数只会把一小部分Python的语法当作有效的内容:
你提供的字符串或节点只能包含以下这些Python的基本结构:字符串、字节、数字、元组、列表、字典、集合、布尔值和
None
。
如果你把 __import__('os').system('rm -rf /a-path-you-really-care-about')
这样的内容传给 ast.literal_eval()
,它会报错,但 eval()
就会毫不犹豫地删除你的文件。
因为看起来你只是想让用户输入一个普通的字典,所以使用 ast.literal_eval()
是个好主意。它安全地完成你想要的事情,而不会做其他的事情。
datamap = eval(input('Provide some data here: '))
这段代码的意思是,你在判断代码是否安全之前,就已经执行了这段代码。也就是说,一旦这个函数被调用,代码就会立刻被执行。你可以查看一下eval的危险性。
ast.literal_eval
这个函数会在输入不是有效的Python数据类型时抛出一个异常,也就是说,如果输入不合法,代码就不会被执行。
在需要使用eval
的时候,建议使用ast.literal_eval
。通常情况下,你不应该执行简单的Python语句。