我想禁止从客户端代码访问文件系统,所以我想我可以覆盖open function
env = {
'open': lambda *a: StringIO("you can't use open")
}
exec(open('user_code.py'), env)
但我得到了这个
unqualified exec is not allowed in function 'my function' it contains a
nested function with free variables
我也试试
def open_exception(*a):
raise Exception("you can't use open")
env = {
'open': open_exception
}
但得到相同的例外(不是“你不能用开”)
我想阻止:
执行此操作:
"""def foo():
return open('some_file').read()
print foo()"""
并评估此
"open('some_file').write('some text')"
我还使用session来存储之前评估过的代码,所以我需要阻止执行它:
"""def foo(s):
return open(s)"""
然后评估此
"foo('some').write('some text')"
我不能使用正则表达式,因为有人可以使用(在字符串中使用eval)
"eval(\"opxx('some file').write('some text')\".replace('xx', 'en')"
有没有办法阻止访问exec / eval中的文件系统? (我需要两个)
答案 0 :(得分:11)
无法阻止访问exec / eval中的文件系统。这是一个示例代码,演示了用户代码调用始终有效的其他受限类的方法:
import subprocess
code = """[x for x in ().__class__.__bases__[0].__subclasses__()
if x.__name__ == 'Popen'][0](['ls', '-la']).wait()"""
# Executing the `code` will always run `ls`...
exec code in dict(__builtins__=None)
不要考虑过滤输入,特别是使用正则表达式。
你可以考虑一些选择:
ast.literal_eval
如果你只能将自己限制在简单的表达方式答案 1 :(得分:5)
您无法将exec()
和eval()
转变为安全沙箱。只要sys
模块可用,您就可以随时访问内置模块::
sys.modules[().__class__.__bases__[0].__module__].open
即使sys
不可用,您仍然可以通过基本相同的方式访问任何导入模块中定义的任何新样式类。这包括io
中的所有IO类。
答案 2 :(得分:5)
实际上可以完成。
也就是说,实际上你所描述的内容可以在Linux上完成,与此处的其他答案相反。也就是说,您可以实现一种设置,在该设置中您可以进行exec
类似的调用,该调用在安全性下运行不受信任的代码,这些代码难以穿透,并且允许输出结果。除了阅读Python vm和标准库的特定允许部分外,不允许不受信任的代码访问文件系统。
如果它足够接近你想要的东西,请继续阅读。
我正在构想一个系统,其中类似exec的函数在非常严格的AppArmor配置文件下生成子流程,例如Straitjacket使用的配置文件(请参阅here和here) 。这将限制内核级别的所有文件系统访问,而不是特别允许读取的文件。这还将限制进程的堆栈大小,最大数据段大小,最大驻留集大小,CPU时间,可排队的信号数以及地址空间大小。该进程将锁定内存,内核,flock / fcntl锁,POSIX消息队列等,完全不允许。如果要允许在临时区域中使用大小受限的临时文件,可以mkstemp
它并使其可用于子进程,并允许在某些条件下写入(确保绝对不允许使用硬链接)。您希望确保清除子进程环境中的任何有趣内容并将其放入新的会话和进程组中,并关闭除了stdin / stdout / stderr之外的子进程中的所有FD,如果要允许与那些。
如果您希望能够从不受信任的代码中取出Python对象,可以将其包装在将结果repr
打印到stdout的内容中,并在检查其大小后,将其评估为ast.literal_eval()
。这严重限制了可以返回的对象的可能类型,但实际上,任何比这些基本类型更复杂的东西都可能存在意图在您的进程中触发的sekrit恶意攻击的可能性。在任何情况下都不应将pickle
用于进程之间的通信协议。
答案 3 :(得分:3)
由于@Brian建议覆盖开放不起作用:
def raise_exception(*a):
raise Exception("you can't use open")
open = raise_exception
print eval("open('test.py').read()", {})
这显示文件的内容但是这个(合并@Brian和@lunaryorn答案)
import sys
def raise_exception(*a):
raise Exception("you can't use open")
__open = sys.modules['__builtin__'].open
sys.modules['__builtin__'].open = raise_exception
print eval("open('test.py').read()", {})
会抛出这个:
Traceback (most recent call last):
File "./test.py", line 11, in <module>
print eval("open('test.py').read()", {})
File "<string>", line 1, in <module>
File "./test.py", line 5, in raise_exception
raise Exception("you can't use open")
Exception: you can't use open
Error in sys.excepthook:
Traceback (most recent call last):
File "/usr/lib/python2.6/dist-packages/apport_python_hook.py", line 48, in apport_excepthook
if not enabled():
File "/usr/lib/python2.6/dist-packages/apport_python_hook.py", line 23, in enabled
conf = open(CONFIG).read()
File "./test.py", line 5, in raise_exception
raise Exception("you can't use open")
Exception: you can't use open
Original exception was:
Traceback (most recent call last):
File "./test.py", line 11, in <module>
print eval("open('test.py').read()", {})
File "<string>", line 1, in <module>
File "./test.py", line 5, in raise_exception
raise Exception("you can't use open")
Exception: you can't use open
您可以通过open
__open
外部用户代码
答案 4 :(得分:1)
“嵌套函数”指的是它在另一个函数内声明,而不是它是一个lambda。在模块的顶层声明你的open
覆盖,它应该按照你想要的方式工作。
另外,我认为这不是完全安全的。如果你想沙箱Python,预防open
只是你需要担心的事情之一。