简体   繁体   中英

Python: Using sympy.sympify to perform a safe eval() on mathematical functions

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() .

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 .

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?

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.

Use sympy, it's a way safer option.

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

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 ).

>>> 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM