我有一个在 Raspbian Stretch 上运行 Apache 2 的 Web 服务器。它将成为一个编程竞赛网站,用户可以在其中通过 HTML 表单发送代码,通过 POST 请求将源代码发送到 PHP。然后 PHP 运行(使用 exec()
)带有参数的 Python 脚本,例如提交的代码路径。然后脚本使用自定义输入执行代码(使用 subprocess.run()
)并将其与预期输出进行比较。 所有这些都很好。
但是,我想确保没有人会发送恶意代码来覆盖 index.php 等文件,或读取预期的输出。我想知道是否有任何方法可以防止 subprocess.run()
正在执行的应用程序读取、创建和写入 stdin
以外的文件, stdout
和 stderr
。
我曾尝试使用 Docker 但没有成功,因为当我使用 PHP 的 exec()
构建和运行 Dockerfile 时,它到达了第 2/4 步并停止了。我的 Dockerfile 应该将脚本、代码和预期输出复制到图像,cd 到新位置并执行代码,但这不是很相关,因为我想避免使用 Docker,因为它无法正常工作。
我正在考虑使用 chroot jail,但我仍在寻找其他不太复杂的方法。
这是我正在使用的 PHP 代码。它调用 Python 3 代码验证器(变量是从 HTML 表单和 SQL 查询中检索的,这些不相关):
$cmd = "python3 verify.py $uploadedFile $questionID $uploadedFileExtension $questionTimeLimit 2>&1";
这是执行提交代码的 Python 3 代码:
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]))
这两个程序的组合工作得很好。它使用模拟输入运行代码,将 stdout
与预期输出进行比较,并将状态字符串返回给 PHP 代码。但是,如果发送的代码带有恶意代码,比如
open('/var/www/html/contest/index.php', 'w').write('oops!')
index.php
文件将被覆盖。
我所需要的只是一种执行用户发送的代码的方式,它会尝试读取或写入文件(stdin
、stdout
和 stderr
) 被拒绝。
有什么想法吗?
最佳答案
简而言之,安全地执行此操作很困难。如果您不小心设置它,即使是 chroot jail 也相对容易逃脱。基本上,Unix 安全模型并不是为了使这类事情变得容易而构建的,并且假定事情大多是合作的
docker 可能是我的建议,但还有其他更轻量级的解决方案,如 chroot(但它们可能仍然有能力使用 Web 服务器的网络连接做一些顽皮的事情)或者类似 firejail 的东西
使用 docker,您可能希望创建一个最小的 docker 图像/容器,其中包含 Python 和任何合适的库。然后你会使用 volumes使用户提供的代码在运行时出现在 VM 中。你不想一直创建容器,那会需要大量的清理工作
参见 https://security.stackexchange.com/q/107850/36536有关将 docker 用作沙箱的更多信息,除非您小心,否则基本上还有很多方法可以摆脱它
关于php - 在 Python 中使用 subprocess.run() 运行脚本,阻止所有文件读/写尝试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56949488/