Python 中的“eval”更安全?
Safer `eval` in Python?
我怎样才能使这段代码更安全?这是一个更复杂代码的最小可重现示例,其中允许内部用户读取代码中的一些字典,他们的名字是事先知道的。该示例与 eval
一起正常工作,并防止 某些 恶意用户输入,例如对 rm -rf /
的系统调用。但我一直在寻找比 eval
.
更安全的方法
import re
# In the minimal example, I have 2 dicts that the users need
# read access to, and keys match this regex: ^\w+$
dct_a = {'foo': 1, 'bar': 2}
dct_b = {'baz': 3, 'bletch': 4}
# User input, e.g.:
lst = ["dct_a['foo']", "dct_b['baz']"]
for item in lst:
# Make safer, prevent a few obvious hacks:
if not re.findall(r"^[\w\]\[']+$", item):
raise Exception(f'Unsafe item: {item}')
# do something with item, e.g.:
print(eval(item))
# Prints:
# 1
# 3
我知道 eval
is dangerous,无需重复警告。
相关:
Python: make eval safe
与其从 Python 的全部功能开始并试图控制它(这是愚蠢的差事),不如从你需要的开始,这不允许用户做任何其他事情因为它不能做任何其他事情。您不希望您的用户能够 运行 Python 代码,您希望您的用户能够以某种非常有限的方式指定“变量值”。所以,例如:
vals = {
'a': {'foo': 1, 'bar': 2},
'b': {'baz': 3, 'bletch': 4}
}
# User input, e.g.:
lst = ["a.foo", "b.baz"]
for item in lst:
dct, k = item.split('.')
print(vals[dct][k])
如果这就是您的用户需要做的所有事情,那么这就是您所需要的。从这里开始,您当然可以开始编写自己的迷你语言,以获得越来越强大但仍然非常有限和受限的表达方式。为此目的,有像 pyparsing 这样的库。
要访问变量(#safely)你可以使用vars()
函数
并使用正则表达式为其添加更多功能:
import re
dct_a = {'foo': 1, 'bar': 2}
dct_b = {'baz': 3, 'bletch': 4}
for _ in range(5):
try:
cmd = input(">>> ")
cmds = re.findall(r'(\w+)\[\'?\"?(\w+)\'?\"?\]',cmd)
if cmds:
print(vars()[cmds[0][0]][cmds[0][1]])
else:
print(vars()[cmd])
except Exception as e:
print(cmd,'Not in Scope')
vars()
returns 一个字典,所有变量名作为 keys 和它们的值作为 values,在那个范围内。在这种情况下,因为我使用了没有任何参数的 vars
,所以它等同于 locals()
,并且也可以用来代替 vars
。
globals()
是一个类似的方法,但是顾名思义,它只访问全局变量。
输出:
>>> dct_a
{'foo': 1, 'bar': 2}
>>> dct_a['bar']
2
>>> dct_b
{'baz': 3, 'bletch': 4}
>>> dct_b["baz"]
3
>>> dct_c
dct_c Not in Scope
我怎样才能使这段代码更安全?这是一个更复杂代码的最小可重现示例,其中允许内部用户读取代码中的一些字典,他们的名字是事先知道的。该示例与 eval
一起正常工作,并防止 某些 恶意用户输入,例如对 rm -rf /
的系统调用。但我一直在寻找比 eval
.
import re
# In the minimal example, I have 2 dicts that the users need
# read access to, and keys match this regex: ^\w+$
dct_a = {'foo': 1, 'bar': 2}
dct_b = {'baz': 3, 'bletch': 4}
# User input, e.g.:
lst = ["dct_a['foo']", "dct_b['baz']"]
for item in lst:
# Make safer, prevent a few obvious hacks:
if not re.findall(r"^[\w\]\[']+$", item):
raise Exception(f'Unsafe item: {item}')
# do something with item, e.g.:
print(eval(item))
# Prints:
# 1
# 3
我知道 eval
is dangerous,无需重复警告。
相关:
Python: make eval safe
与其从 Python 的全部功能开始并试图控制它(这是愚蠢的差事),不如从你需要的开始,这不允许用户做任何其他事情因为它不能做任何其他事情。您不希望您的用户能够 运行 Python 代码,您希望您的用户能够以某种非常有限的方式指定“变量值”。所以,例如:
vals = {
'a': {'foo': 1, 'bar': 2},
'b': {'baz': 3, 'bletch': 4}
}
# User input, e.g.:
lst = ["a.foo", "b.baz"]
for item in lst:
dct, k = item.split('.')
print(vals[dct][k])
如果这就是您的用户需要做的所有事情,那么这就是您所需要的。从这里开始,您当然可以开始编写自己的迷你语言,以获得越来越强大但仍然非常有限和受限的表达方式。为此目的,有像 pyparsing 这样的库。
要访问变量(#safely)你可以使用vars()
函数
并使用正则表达式为其添加更多功能:
import re
dct_a = {'foo': 1, 'bar': 2}
dct_b = {'baz': 3, 'bletch': 4}
for _ in range(5):
try:
cmd = input(">>> ")
cmds = re.findall(r'(\w+)\[\'?\"?(\w+)\'?\"?\]',cmd)
if cmds:
print(vars()[cmds[0][0]][cmds[0][1]])
else:
print(vars()[cmd])
except Exception as e:
print(cmd,'Not in Scope')
vars()
returns 一个字典,所有变量名作为 keys 和它们的值作为 values,在那个范围内。在这种情况下,因为我使用了没有任何参数的 vars
,所以它等同于 locals()
,并且也可以用来代替 vars
。
globals()
是一个类似的方法,但是顾名思义,它只访问全局变量。
输出:
>>> dct_a
{'foo': 1, 'bar': 2}
>>> dct_a['bar']
2
>>> dct_b
{'baz': 3, 'bletch': 4}
>>> dct_b["baz"]
3
>>> dct_c
dct_c Not in Scope