你能用 exec() 保护 python 变量吗?

Bix*_*Bix 3 python

这是一个有点奇怪的 Python 问题。

考虑以下 Python 代码:

def controlled_exec(code):
  x = 0
  def increment_x():
    nonlocal x
    x += 1

  globals = {"__builtins__": {}} # remove every global (including all python builtins)
  locals = {"increment_x": increment_x} # expose only the increment function

  exec(code, globals, locals)

  return x
Run Code Online (Sandbox Code Playgroud)

我希望这个函数能够提供一个受控代码 API,它可以简单地计算调用次数increment_x()。我尝试了一下,得到了正确的行为。

# returns 2
controlled_exec("""\
increment_x()
increment_x()
""")
Run Code Online (Sandbox Code Playgroud)

我认为这种做法并不安全,但出于好奇我想知道。我可以x通过执行代码来设置任意值(比如负数)吗controlled_exec(...)?我该怎么做呢?

Bri*_*ian 5

是的,x可以通过 执行的代码设置为任意值controlled_exec

考虑以下演示:

def controlled_exec(code):
  x = 0
  def increment_x():
    nonlocal x
    x += 1
    print(f"{x=}")

  globals = {"__builtins__": {}} # remove every global (including all python builtins)
  locals = {"increment_x": increment_x} # expose only the increment function

  exec(code, globals, locals)

  return x


controlled_exec("""\
increment_x()
increment_x.__closure__[0].cell_contents = -100
increment_x()
""")
Run Code Online (Sandbox Code Playgroud)

这输出

x=1
x=-99
Run Code Online (Sandbox Code Playgroud)

这实在是没办法“保全” exec。无论您做什么,最终您都会执行任意 Python 代码,这些代码与您编写的代码一样可以自由访问和修改 Python 解释器的状态。

为了强调有意义的安全有多困难exec,请注意,尽管您尝试从执行的代码中隐藏内置函数,但攻击者仍然可以通过以下方式访问任何内置函数:

increment_x.__globals__['__builtins__']
Run Code Online (Sandbox Code Playgroud)

这只是试图利用此功能的人可以使用的数十种技巧之一。

我们可以修改变量这一事实x确实是一个温和的例子。实际上没有什么可以阻止传递的代码controlled_exec执行更多灾难性的操作,例如擦除硬盘驱动器或下载恶意软件。