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

标签 php python linux

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

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

我曾尝试使用 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 文件将被覆盖。

我所需要的只是一种执行用户发送的代码的方式,它会尝试读取或写入文件(stdinstdoutstderr) 被拒绝。

有什么想法吗?

最佳答案

简而言之,安全地执行此操作很困难。如果您不小心设置它,即使是 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/

相关文章:

python - Django 查询查找具有大于零的特定值的行数,按用户分组

android - 如何将openssl集成到Android中?

c - 如何使基于磁盘的缓冲区像内存一样?

php - htmlentities 的替代方案($string,ENT_SUBSTITUTE)

javascript - 如何在 Algolia 搜索页面结果的摘录中显示我的 Wordpress 自定义字段属性?

php - 注意 : Undefined offset: 0, PHP/MySQL

python - 使用数据流的 GCS 文件流式传输(apachebeam python)

php - mysql查询在选择单选按钮时未从数据库获取值

python - 正则表达式与变量精确模式匹配

linux - 用于迁移配置文件变量的 Shell 脚本