我有一个在Raspbian Stretch上运行Apache 2的Web服务器。这将是一个编程竞赛网站,用户可以在其中通过HTML表单发送代码,并通过POST请求将其源代码发送到PHP。然后,PHP运行(使用exec()
)带有诸如提交的代码路径之类的参数的Python脚本。然后,脚本使用自定义输入执行代码(使用subprocess.run()
),并将其与预期输出进行比较。 所有这些都很好。
但是,我想确保没有人会发送恶意代码来覆盖诸如index.php之类的文件,或读取预期的输出。我想知道是否有任何方法可以防止由subprocess.run()
执行的应用程序读取,创建和写入stdin
,stdout
和{{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.php
,stdin
和stdout
以外的文件)否认。
有什么想法吗?
答案 0 :(得分:1)
安全地进行此操作很困难。如果您不太谨慎地设置它,即使逃脱chroot监狱也相对容易。基本上,Unix安全模型并不是为了使这种事情简单而构建的,并且假定事情大多是合作的
泊坞窗可能是我的建议,但是还有其他更轻量级的解决方案,例如chroot(但它们可能仍具有通过Web服务器的网络连接进行淘气的能力)或类似firejail < / p>
使用docker,您可能想要创建一个包含Python和任何合适库的最小docker映像/容器。然后,您可以使用volumes使用户提供的代码在运行时出现在VM中。您不想一直创建容器,这将需要大量清理工作
有关将Docker用作沙箱的更多信息,请参见https://security.stackexchange.com/q/107850/36536,除非您谨慎,否则基本上还有很多方法可以解决