使用python的eval()与ast.literal_eval()?


176

我遇到了一些代码,eval()将其作为一种可能的解决方案。现在,我以前从未使用eval()过,但是,我遇到了很多有关它可能引起的潜在危险的信息。也就是说,我对使用它非常谨慎。

我的情况是我有一个用户输入:

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

哪里datamap需要一本字典。我四处搜寻,发现eval()可以解决这个问题。我认为我可以在尝试使用数据之前检查输入的类型,这将是可行的安全预防措施。

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

我通读了文档,但仍不清楚这是否安全。eval是否在输入数据后或datamap调用变量后立即评估数据?

ast模块是.literal_eval()唯一安全的选择吗?

Answers:


190

datamap = eval(raw_input('Provide some data here: '))意味着您实际上认为代码不安全之前对其进行了评估。调用该函数后,它将立即评估代码。另请参阅的危险eval

ast.literal_eval 如果输入不是有效的Python数据类型,则会引发异常,因此如果输入无效,则不会执行代码。

使用ast.literal_eval时,你需要eval。通常,您不应该评估文字Python语句。


20
这不是100%正确的建议,因为任何按位运算符(或重载的运算符)都会失败。例如。ast.literal_eval("1 & 1")会抛出一个错误,但eval("1 & 1")不会。
丹尼尔·范·弗莱门

1
只是好奇。我们不应该使用表达式解析器还是期望“ 1&1”之类的东西吗?
thelinuxer

@thelinuxer,你还是应该的;您将无法使用ast.literal_eval类似的功能(例如,您可以手动实现解析器)。
波动率

104

ast.literal_eval() 仅认为Python语法的一小部分有效:

提供的字符串或节点只能由以下Python文字结构组成:字符串,数字,元组,列表,字典,布尔值和无。

传递__import__('os').system('rm -rf /a-path-you-really-care-about')ast.literal_eval()将引发一个错误,但eval()会愉快地擦拭您的驱动器。

由于看起来您只是让用户输入普通字典,所以请使用ast.literal_eval()。它可以安全地执行您想要的操作,仅此而已。


也支持字节字符串(类字节)。例如。b'Hello World'–
XChikuX

52

eval: 此功能非常强大,但是如果您接受字符串以从不受信任的输入中求值,则也非常危险。假设要评估的字符串是“ os.system('rm -rf /')”?它将真正开始删除计算机上的所有文件。

ast.literal_eval: 安全地评估表达式节点或包含Python文字或容器显示的字符串。提供的字符串或节点只能由以下Python文字结构组成:字符串,字节,数字,元组,列表,字典,集合,布尔值,无,字节和集合。

句法:

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],对象本身就是什么。现在我们实例化了所有子类,这里我们的主要enter code here目标是从中找到一个名为n的类。

我们需要code对象和function实例化的子类的对象。这是CPython访问对象子类并附加系统的另一种方法。

从python 3.7开始,ast.literal_eval()更加严格了。不再允许对任意数字进行加减。链接


1
我正在使用python 2.7,我只是在python 3.x上检查了它的工作状况。我的坏我一直试图在它的python 2.7
Mourya

3
ast.literal_eval("1+1")在python 3.7中不起作用,并且如前所述,literal_eval应该限于少数几个数据结构的文字。它应该不能解析二进制操作。
塞斯舒'18 -10-3

您能解释一下您的KABOOM代码吗?在这里找到它:KABOOM
winklerrr

2
@winklerrr KABOOM在这里得到了很好的解释:nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
Elijas

41

Python 渴望进行评估,因此无论eval(raw_input(...))用户eval随后对数据进行什么操作,只要它点击,就将评估用户的输入。因此,这是不安全的,尤其是在eval用户输入时。

使用ast.literal_eval


例如,在提示符下输入此命令对您非常不利:

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

3

如果您需要的只是用户提供的词典,则可能是更好的解决方案json.loads。主要限制是json dict需要字符串键。另外,您只能提供文字数据,但情况也是如此literal_eval


1

我被困住了ast.literal_eval()。我在IntelliJ IDEA调试器中尝试过它,并一直None在调试器输出中返回。

但是稍后,当我将其输出分配给变量并以代码打印时。工作正常。共享代码示例:

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

其python版本3.6。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.