原文标题:Eval really is dangerous
原文链接:https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
原文作者:Ned Batchelder
本文地址:https://www.white-winds.com/post/eval really is dangerous/
翻译这篇文章的原因是在业务上很多使用
eval
来进行类型转换,比如eval("[1,2,3]")
,这些代码要么没有对传入的字符串进行过滤,要么就是简单的将 globals 置空eval("[1,2,3]", {})
。本文详细分析了eval
不安全的原因,如果只是为了类型转换,可以使用ast.literal_eval
来代替。
Python 的 eval()
函数用于将传入的字符串作为代码进行求值。
Python has an eval() function which evaluates a string of Python code:
assert eval("2 + 3 * len('hello')") == 17
它十分强大,但如果对不可信的字符串进行求值,就会非常危险。比如正在求值的字符串是 os.system('rm-rf /')
?它将真正删除计算机上的所有文件。(在下面的示例中,我将使用 clear
而不是 rm-rf /
以防意外。)
This is very powerful, but is also very dangerous if you accept strings to evaluate from untrusted input. Suppose the string being evaluated is “os.system(‘rm -rf /’)” ? It will really start deleting all the files on your computer. (In the examples that follow, I’ll use ‘clear’ instead of ‘rm -rf /’ to prevent accidental foot-shootings.)
一些人声称可以通过不提供全局变量,使 eval
变得安全。eval
的第二个参数是在求值期间使用的全局变量,为字典形式,如果没有提供,eval
将使用当前的全局变量,进而可以使用 os
模块。如果提供一个空字典,那就没有全局变量,触发异常: NameError: name 'os' is not defined
:
Some have claimed that you can make eval safe by providing it with no globals. eval() takes a second argument which are the global values to use during the evaluation. If you don’t provide a globals dictionary, then eval uses the current globals, which is why “os” might be available. If you provide an empty dictionary, then there are no globals. This now raises a NameError, “name ‘os’ is not defined”:
eval("os.system('clear')", {
})
但我们仍然可以通过 builtins 中的函数 __import__
来引入它们并使用,下面的代码可以执行成功:
But we can still import modules and use them, with the builtin function import. This succeeds:
eval("__import__('os').system('clear')", {
})
在 Python 2 中可以使用 __import__
和 open
函数的原因是它们位于全局变量 __builtins__
中,为了确保安全,我们尝试在 eval
中拒绝其访问 builtins。通过在全局变量中将 __builtins__
定义为空字典,我们可以显式指定不使用这些内置函数。现在下面的代码会抛出 NameError:
The next attempt to make things safe is to refuse access to the builtins. The reason names like _import_ and open are available to you in Python 2 is because they are in the _builtins_ global. We can explicitly specify that there are no builtins by defining that name as an empty dictionary in our globals. Now this raises a NameError:
eval("__import__('os').system('clear')", {
"__builtins__": {
}})
!!! note
eval
的函数定义为:eval(expression[, globals[, locals]])
你可能会疑惑,上面已经将 globals 置为 {}
了,为什么还要显式的将