在此示例中,`eval()` 为何不是 'dangerous'

How `eval()` is not 'dangerous' in this example

我正在检查一个计算器示例,在示例中使用了 eval() 这通常是危险的,但这里是该示例的一部分;

if button == "=":
    #Check carefully how we using the 'dangerous' eval()
    total = eval(str1,{"__builtins__":None},{})
    str1 = str(total)
    print (str1)

我查了,没看懂; eval(str1,{"__builtins__":None},{})怎么不危险?明明是{"__builtins__":None},{}这部分,但是我没看懂

注意:str1 是一个字符串,我们正在添加数字和符号,如 4+5。然后 eval() 处理它。

之所以这是一种更安全的执行 eval() 的方法,是因为它明确限制了允许哪些内置方法(在本例中为 none)。您可以使用该参数来指定任何允许的内置函数。 Here 是关于该主题的更多信息

根据the documentation of eval

eval(expression, globals=None, locals=None)

The expression argument is parsed and evaluated as a Python expression using the globals and locals dictionaries as global and local namespace. If the globals dictionary is present and lacks '__builtins__', the current globals are copied into globals before expression is parsed. This means that expression normally has full access to the standard builtins module and restricted environments are propagated. If the locals dictionary is omitted it defaults to the globals dictionary. If both dictionaries are omitted, the expression is executed in the environment where eval() is called.

所以您显示的代码的尝试是 eval 在没有潜在危险函数可用的上下文中的表达式。例如,eval('print("bad stuff")') 将打印错误内容,但如果您传递一个空的全局命名空间,即使没有内置 print.

也不会。

不要把这种安全感想得太过分。即使在这些限制范围内,不受信任的代码也会破坏您的程序。例如,以下字符串 if eval()d 会因超出其递归堆栈而使您的 Python 解释器崩溃:

(lambda f : f(f))(lambda f : f(f))

正如 Håken Lid 在 , a safer approach would be to use ast.literal_eval 中提到的那样,它就是为此而制作的。作为一般规则:最好使用功能最弱的命令来完成工作,而不是使用功能强大的命令并尝试手动限制它。忘记的东西太多了。

代码一点都不安全。仅通过访问文字的属性就可以相对容易地访问 builtins 模块。

例如

result = eval("""[klass for klass in ''.__class__.__base__.__subclasses__()
            if klass.__name__ == "BuiltinImporter"][0].load_module("builtins")""",
    {"__builtins__":None},{})
assert result is __builtins__

细分:

  • ''.__class__.__base__ 是 shorthand 对于 object
  • object.__subclasses__() 列出解释器中 object 的所有子classes(这包括导入机器
  • 使用的 classes
  • [klass for klass in ... if klass.__name__ == "BuiltinImporter"][0] -- select BuiltinImporter class.
  • load_module("builtins") 使用 BuiltinImporter 获得对 builtins 模块的访问权限——这正是您试图限制访问的内容。