在Python中使用subprocess.run()运行脚本,阻止所有文件读/写尝试

时间:2019-07-09 09:30:02

标签: php python linux

我有一个在Raspbian Stretch上运行Apache 2的Web服务器。这将是一个编程竞赛网站,用户可以在其中通过HTML表单发送代码,并通过POST请求将其源代码发送到PHP。然后,PHP运行(使用exec())带有诸如提交的代码路径之类的参数的Python脚本。然后,脚本使用自定义输入执行代码(使用subprocess.run()),并将其与预期输出进行比较。 所有这些都很好。

但是,我想确保没有人会发送恶意代码来覆盖诸如index.php之类的文件,或读取预期的输出。我想知道是否有任何方法可以防止由subprocess.run()执行的应用程序读取,创建和写入stdinstdout和{{1以外的文件}}。

我尝试使用Docker,但是没有成功,因为当我使用PHP的stderr构建和运行Dockerfile时,它到达步骤2/4并停止。我的Dockerfile应该将脚本,代码和预期的输出复制到映像中,然后将cd复制到新位置并执行代码,但这并不是很相关,因为我想避免使用Docker,因为它无法正常工作。

我正在考虑使用chroot监狱,但我仍在寻找其他不那么复杂的方法。

这是我正在使用的PHP代码。它调用Python 3代码验证程序(变量是从HTML表单和SQL查询中检索出来的,这些无关紧要):

exec()

这是执行提交的代码的Python 3代码:

$cmd = "python3 verify.py $uploadedFile $questionID $uploadedFileExtension $questionTimeLimit 2>&1";

两个程序的组合工作正常。它使用模拟输入运行代码,将def runCmd(args, vStdin, timelimit = 10): p = subprocess.run(args, stdout = subprocess.PIPE, stderr = subprocess.PIPE, input = vStdin, encoding = 'utf-8', timeout=timelimit) vStdout = p.stdout vStderr = p.stderr if vStdout.endswith('\n'): vStdout = vStdout[:-1] if vStderr.endswith('\n'): vStderr = vStderr[:-1] return vStdout, vStderr ... # Assuming it is a .py file # Its path is given by PHP's exec. runCmd(['python3', sys.argv[1], 'simulatedinput.in', int(sys.argv[4])) 与预期输出进行比较,然后将状态字符串返回到PHP代码。但是,如果发送的代码包含恶意代码,例如

stdout

open('/var/www/html/contest/index.php', 'w').write('oops!') 文件将被覆盖。

我所需要做的是一种执行用户发送的代码的方式,以使其尝试读取或写入文件(index.phpstdinstdout以外的文件)否认。

有什么想法吗?

1 个答案:

答案 0 :(得分:1)

简而言之,

安全地进行此操作很困难。如果您不太谨慎地设置它,即使逃脱chroot监狱也相对容易。基本上,Unix安全模型并不是为了使这种事情简单而构建的,并且假定事情大多是合作的

泊坞窗可能是我的建议,但是还有其他更轻量级的解决方案,例如chroot(但它们可能仍具有通过Web服务器的网络连接进行淘气的能力)或类似firejail < / p>

使用docker,您可能想要创建一个包含Python和任何合适库的最小docker映像/容器。然后,您可以使用volumes使用户提供的代码在运行时出现在VM中。您不想一直创建容器,这将需要大量清理工作

有关将Docker用作沙箱的更多信息,请参见https://security.stackexchange.com/q/107850/36536,除非您谨慎,否则基本上还有很多方法可以解决