关于范围:Python eval(编译(…),沙箱),全局变量在沙箱中除非在def中,为什么?

Python eval(compile(…), sandbox), globals go in sandbox unless in def, why?

考虑下面的:P></

1
2
3
4
5
6
7
8
9
10
11
12
def test(s):
    globals()['a'] = s

sandbox = {'test': test}
py_str = 'test("Setting A")
globals()["b"] ="Setting B"'

eval(compile(py_str, '<string>', 'exec'), sandbox)

'a' in sandbox # returns False, !What I dont want!
'b' in sandbox # returns True, What I want
'a' in globals() # returns True, !What I dont want!
'b' in globals() # returns False, What I want

我甚至不确定我想问how to,but the Global to be the scope for a函数的环境中运行它有没有打算在编译eval function to the the。这是可能的吗?P></

thanks for any输入P></

解决方案P></

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def test(s):
    globals()['a'] = s

sandbox = {}

# create a new version of test() that uses the sandbox for its globals
newtest = type(test)(test.func_code, sandbox, test.func_name, test.func_defaults,
                     test.func_closure)

# add the sandboxed version of test() to the sandbox
sandbox["test"] = newtest

py_str = 'test("Setting A")
globals()["b"] ="Setting B"'

eval(compile(py_str, '<string>', 'exec'), sandbox)

'a' in sandbox # returns True
'b' in sandbox # returns True
'a' in globals() # returns False
'b' in globals() # returns False


在Python中调用函数时,它看到的全局变量始终是在其中定义的模块的全局变量。(如果这不是真的,那么函数可能不起作用——它可能实际上需要一些全局值,而您不一定知道这些值是什么。)指定一个带有execeval()的全局字典只会影响代码为execeval()所看到的全局。

如果您希望函数看到其他全局变量,那么您确实需要在传递给execeval()的字符串中包含函数定义。当您这样做时,函数的"module"是它编译时使用的字符串,它有自己的全局变量(即您提供的那些全局变量)。

您可以通过创建一个新的函数来解决这个问题,该函数与您所调用的函数具有相同的代码对象,但具有指向全局dict的不同func_globals属性,但这是相当高级的黑客行为,可能不值得这样做。不过,您可以这样做:

1
2
3
4
5
6
7
8
9
# create a sandbox globals dict
sandbox = {}

# create a new version of test() that uses the sandbox for its globals
newtest = type(test)(test.func_code, sandbox, test.func_name, test.func_defaults,
                     test.func_closure)

# add the sandboxed version of test() to the sandbox
sandbox["test"] = newtest


exec的沙盒代码通过提供替代的全局/局部的方法有很多警告:

  • 可选的全局/局部变量只适用于沙盒中的代码。它们不会影响它之外的任何事物,也不会影响它之外的任何事物,如果它们能够,就没有意义了。

    换句话说,所谓的"沙盒"将对象test传递给exec运行的代码。为了改变test所看到的全局,它还必须修改对象,而不是按原样传递它。这是不可能的,以任何方式来保持它的工作,更不用说在一个对象将继续做一些有意义的事情的方式。

  • 通过使用替代全局,sandbox中的任何内容都将看到内置的。如果要隐藏沙盒内的代码中的部分或所有内置项,则需要在字典中添加一个指向None(禁用所有内置项)或其版本的"__builtins__"键。这也限制了对象的某些属性,例如访问函数的func_globals属性将被禁用。

  • 即使移除内置组件,沙箱仍然不安全。沙盒代码是您首先信任的。

这是一个简单的概念证明:

1
2
3
4
5
6
7
import subprocess
code ="""[x for x in ().__class__.__bases__[0].__subclasses__()
           if x.__name__ == 'Popen'][0](['ls', '-la']).wait()"""

# The following runs"ls"...
exec code in dict(__builtins__=None)
# ...even though the following raises
exec"(lambda:None).func_globals" in dict(__builtins__=None)


外部执行上下文是在python中静态定义的(f.func_globals是只读的),所以我认为您想要的是不可能的。原因是函数可能变成无效的python,它的定义上下文在运行时被更改。如果语言允许,这将是向库调用中注入恶意代码的非常简单的途径。

1
2
3
4
def mycheck(s):
    return True

exec priviledged_code in {'check_password':mycheck}