Python:使用sympy.sympify对数学函数执行安全的eval()

时间:2022-05-14 20:40:17

I am writing a program where users need to be able to use self written mathematical functions containing functions from numpy and scipy, eg. scipy.special.wofz().

我正在编写一个程序,用户需要能够使用包含numpy和scipy函数的自编数学函数,例如。 scipy.special.wofz()。

These functions will be stored in files and imported as strings by the program. I looked around and saw, that eval() or exec() are not a safe way to do it. eg. here.

这些函数将存储在文件中,并由程序作为字符串导入。我环顾四周,看到eval()或exec()不是一种安全的方法。例如。这里。

The security issue would be that good users load a file from evil users who get access to the good users system.

安全问题是好的用户从访问好用户系统的恶意用户加载文件。

I was thinking about doing something like this:

我在考虑做这样的事情:

#!/bin/python
from scipy.special import *
from numpy import *
import sympy

# Define variable a
vars = {"a":1}
# This is the string I get from a file
string = "wofz(a)"

parsed_string = sympy.sympify(string)
parsed_string.evalf(subs=vars)

However, this does not work. It only returns:

但是,这不起作用。它只返回:

wofz(a)

wofz(a) is not evaluated. Is this even supposed to work that way?

wofz(a)未经评估。这甚至应该以这种方式工作吗?

I had another idea: So I thought, once this mathematical function got through sympify, it should be safe. I could just simply do something like this:

我有另一个想法:所以我想,一旦这个数学函数得到了认可,就应该是安全的。我可以简单地做这样的事情:

globals = {wofz:wofz}
eval(str(parsed_string), vars, globals)

which works fine and returns:

哪个工作正常并返回:

(0.36787944117144233+0.60715770584139372j)

Is that safe? I know it's not nice.

这样安全吗?我知道这不好。

Please help.

请帮忙。

2 个解决方案

#1


4  

Use sympy, it's a way safer option.

使用sympy,这是一种更安全的选择。

import sympy
from sympy.core.function import Function
from sympy.core import S
from sympy import sympify
from sympy.functions import im
from scipy.special import wofz

class Wofz(Function):
    is_real = True
    @classmethod
    def _should_evalf(csl,arg):
        return True
    def as_base_exp(cls):
        return cls,S.One

    def _eval_evalf(cls, prec):
        return sympy.numbers.Number(im(wofz(float(cls.args[0]))))

print sympify("Wofz(2)",{'Wofz':Wofz}).evalf()

Output (you'll have to handle the imaginary part somehow):

输出(你必须以某种方式处理虚部):

0.340026217066065

#2


0  

If the string is the only untrusted information, I think the following should be safe:

如果字符串是唯一不受信任的信息,我认为以下内容应该是安全的:

To use eval() with a restricted vocabulary, pass it a second argument that is a dictionary of allowed names, where __builtins__ is defined to something harmless (see http://docs.python.org/library/functions.html#eval).

要使用带有限制词汇表的eval(),请传递第二个参数,该参数是允许名称的字典,其中__builtins__被定义为无害的(参见http://docs.python.org/library/functions.html#eval) 。

>>> import numpy as np
>>> d = dict(linspace=np.linspace, range=range, __builtins__=None)
>>> eval("str(1), 2+2")
('1', 4)
>>> eval("str(1)", d)
Traceback (most recent call last)
NameError: name 'str' is not defined

>>> eval("{'a': linspace(0, 0.5, 6)}, range(2)", d)
({'a': array([ 0. ,  0.1,  0.2,  0.3,  0.4,  0.5])}, [0, 1])
>>> eval("linspace.__dict__", d)
Traceback (most recent call last)
RuntimeError: function attributes not accessible in restricted mode

#1


4  

Use sympy, it's a way safer option.

使用sympy,这是一种更安全的选择。

import sympy
from sympy.core.function import Function
from sympy.core import S
from sympy import sympify
from sympy.functions import im
from scipy.special import wofz

class Wofz(Function):
    is_real = True
    @classmethod
    def _should_evalf(csl,arg):
        return True
    def as_base_exp(cls):
        return cls,S.One

    def _eval_evalf(cls, prec):
        return sympy.numbers.Number(im(wofz(float(cls.args[0]))))

print sympify("Wofz(2)",{'Wofz':Wofz}).evalf()

Output (you'll have to handle the imaginary part somehow):

输出(你必须以某种方式处理虚部):

0.340026217066065

#2


0  

If the string is the only untrusted information, I think the following should be safe:

如果字符串是唯一不受信任的信息,我认为以下内容应该是安全的:

To use eval() with a restricted vocabulary, pass it a second argument that is a dictionary of allowed names, where __builtins__ is defined to something harmless (see http://docs.python.org/library/functions.html#eval).

要使用带有限制词汇表的eval(),请传递第二个参数,该参数是允许名称的字典,其中__builtins__被定义为无害的(参见http://docs.python.org/library/functions.html#eval) 。

>>> import numpy as np
>>> d = dict(linspace=np.linspace, range=range, __builtins__=None)
>>> eval("str(1), 2+2")
('1', 4)
>>> eval("str(1)", d)
Traceback (most recent call last)
NameError: name 'str' is not defined

>>> eval("{'a': linspace(0, 0.5, 6)}, range(2)", d)
({'a': array([ 0. ,  0.1,  0.2,  0.3,  0.4,  0.5])}, [0, 1])
>>> eval("linspace.__dict__", d)
Traceback (most recent call last)
RuntimeError: function attributes not accessible in restricted mode