python - 从 Python 中的不安全用户输入评估数学方程

标签 python eval sandbox

我有一个网站,用户可以在其中输入数学方程式(表达式),然后根据网站提供的数据(常数)评估这些方程式。所需的数学运算包括符号、算术运算、min()max() 和其他一些基本函数。一个示例方程可以是:

max(a * b + 100, a / b - 200)

可以使用 Python 简单地 eval() ,但众所周知,这会导致网站受损。进行数学方程评估的安全方法是什么?

如果选择使用 Python 本身来评估表达式,是否有任何 Python 沙箱会限制 Python,因此只有用户供应商运算符和函数可用。成熟的 Python,就像定义函数一样,应该完全禁用。子流程没问题(见 PyPy sandbox)。特别是,应该关闭用于利用内存和 CPU 使用的 for 循环和其他漏洞。

最佳答案

免责声明:我是另一个答案的代码中提到的 Alexer。老实说,我只是半开玩笑地建议了字节码解析方法,因为我碰巧有 99% 的代码用于一个不相关的项目,因此可以在几分钟内完成 POC。也就是说,它本身不应该有任何问题。只是它是这项任务所需的更复杂的机器。事实上,您应该只需反汇编代码 [根据白名单检查操作码],检查常量和名称是否有效,然后使用普通的邪恶 eval 执行它,就可以逃脱惩罚.您应该失去在整个执行过程中插入偏执的额外检查的能力。 (另一个免责声明:我仍然不会觉得用 eval 来做这件事)

无论如何,我有一个无聊的时刻,所以我写了一些代码以聪明的方式做到这一点;使用 AST 而不是字节码。它只是 compile() 的一个额外标志。 (或者只是 ast.parse(),因为无论如何你都需要模块中的类型)

import ast
import operator

_operations = {
        ast.Add: operator.add,
        ast.Sub: operator.sub,
        ast.Mult: operator.mul,
        ast.Div: operator.div,
        ast.Pow: operator.pow,
}

def _safe_eval(node, variables, functions):
        if isinstance(node, ast.Num):
                return node.n
        elif isinstance(node, ast.Name):
                return variables[node.id] # KeyError -> Unsafe variable
        elif isinstance(node, ast.BinOp):
                op = _operations[node.op.__class__] # KeyError -> Unsafe operation
                left = _safe_eval(node.left, variables, functions)
                right = _safe_eval(node.right, variables, functions)
                if isinstance(node.op, ast.Pow):
                        assert right < 100
                return op(left, right)
        elif isinstance(node, ast.Call):
                assert not node.keywords and not node.starargs and not node.kwargs
                assert isinstance(node.func, ast.Name), 'Unsafe function derivation'
                func = functions[node.func.id] # KeyError -> Unsafe function
                args = [_safe_eval(arg, variables, functions) for arg in node.args]
                return func(*args)

        assert False, 'Unsafe operation'

def safe_eval(expr, variables={}, functions={}):
        node = ast.parse(expr, '<string>', 'eval').body
        return _safe_eval(node, variables, functions)

if __name__ == '__main__':
        import math

        print safe_eval('sin(a*pi/b)', dict(a=1, b=2, pi=math.pi), dict(sin=math.sin))

这与字节码版本相同;如果您根据白名单检查操作并检查名称和值是否有效,您应该能够在 AST 上调用 eval 。 (但同样,我仍然不会这样做。因为偏执狂。当涉及到 eval 时,偏执狂是好的)

关于python - 从 Python 中的不安全用户输入评估数学方程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26505420/

相关文章:

javascript - eval() 和 new Function() 是一回事吗?

ios - 沙盒中自动续订订阅 : renewal doesn't come after relaunch of App

Lua 沙箱 _ENV 与个人基础库(require、assert 等)

api - Instagram API 找不到沙盒用户任何喜欢的帖子

python - 如何通过使用arduino开发板和python脚本从电位计连续获取电压信号来调整音频文件的音量

python - Django 无效 block 标记 : 'else' , 预期 'endblock'

python - 在 python :3. 8.3 docker image 中找不到任何包

c - 破折号和 C : eval "$(<cmdfile)" and system ("eval\"\$(<cmdfile)\"") giving different results

scala - 标量代码作为数据

python:是否可以要求函数的参数都是关键字?