使用Python的eval()与ast.literal_eval()

299 投票
6 回答
439148 浏览
提问于 2025-04-17 17:50

我遇到了一段代码,里面提到了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 个回答

87

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()变得更加严格。任意数字的加法和减法不再被允许。链接

159

ast.literal_eval() 这个函数只会把一小部分Python的语法当作有效的内容:

你提供的字符串或节点只能包含以下这些Python的基本结构:字符串、字节、数字、元组、列表、字典、集合、布尔值和 None

如果你把 __import__('os').system('rm -rf /a-path-you-really-care-about') 这样的内容传给 ast.literal_eval(),它会报错,但 eval() 就会毫不犹豫地删除你的文件。

因为看起来你只是想让用户输入一个普通的字典,所以使用 ast.literal_eval() 是个好主意。它安全地完成你想要的事情,而不会做其他的事情。

318

datamap = eval(input('Provide some data here: ')) 这段代码的意思是,你在判断代码是否安全之前,就已经执行了这段代码。也就是说,一旦这个函数被调用,代码就会立刻被执行。你可以查看一下eval的危险性

ast.literal_eval 这个函数会在输入不是有效的Python数据类型时抛出一个异常,也就是说,如果输入不合法,代码就不会被执行。

在需要使用eval的时候,建议使用ast.literal_eval。通常情况下,你不应该执行简单的Python语句。

撰写回答