You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何限制subprocess.run()执行的用户代码仅使用标准流,禁止文件读写?

针对你的树莓派编程竞赛网站的安全解决方案

不用Docker也能搞定!针对你担心的用户恶意代码读写文件问题,这里有几个轻量、易用的方案,适配树莓派的资源限制:

1. 用Bubblewrap打造完全隔离的沙箱

Bubblewrap(简称bwrap)是个超轻量的Linux沙箱工具,资源占用极低,专门用来给进程创建隔离环境,完美适配树莓派。它能彻底切断用户代码和主机文件系统的联系,只保留stdin/stdout/stderr三个IO通道。

操作步骤:

首先安装它:

sudo apt update && sudo apt install bubblewrap

然后修改你的Python执行逻辑:
原来你是直接调用python3执行用户代码,现在用bwrap把它包起来,让用户代码完全看不到主机的文件系统:

def run_cmd(args, v_stdin, timelimit=10):
    p = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, input=v_stdin, encoding='utf-8', timeout=timelimit)
    return p.stdout.strip(), p.stderr.strip()

# 替换原来的run_cmd调用
# 注意:把输入文件的内容通过stdin传递,不再传文件路径(因为沙箱里看不到)
with open('example-input.in', 'r') as f:
    input_content = f.read()

# 构造bwrap命令:完全隔离文件系统,只保留必要的IO和Python运行依赖
sandbox_args = [
    'bwrap',
    '--dev-null', '/',  # 把所有路径指向/dev/null,无法读写任何文件
    '--bind', '/usr/lib/python3.5', '/usr/lib/python3.5',  # 替换成你树莓派上的Python版本路径
    '--bind', '/usr/lib/arm-linux-gnueabihf', '/usr/lib/arm-linux-gnueabihf',  # 依赖的系统库
    '--unshare-all',  # 取消所有命名空间共享(网络、PID等)
    '--stdin', 'fd:0',  # 保留标准输入
    '--stdout', 'fd:1',  # 保留标准输出
    '--stderr', 'fd:2',  # 保留标准错误
    'python3', sys.argv[1]  # 执行用户代码
]

out, err = run_cmd(sandbox_args, input_content, timelimit=int(sys.argv[4]))

这样用户代码就算写了open('/var/www/html/index.php', 'w'),也会因为找不到文件路径(所有路径都是/dev/null)而失败,彻底杜绝文件读写。

为啥选它?

  • 配置简单,命令行参数就能搞定,不用复杂的配置文件
  • 资源占用极低,比Docker小太多,树莓派毫无压力
  • 安全性拉满,完全隔离文件系统,还能限制网络、PID等其他资源

2. 用Seccomp过滤系统调用(纯Python实现)

Seccomp是Linux内核自带的安全特性,可以精准控制进程能调用的系统函数。通过python-seccomp库,你可以在Python代码里直接禁止用户代码调用任何和文件操作相关的系统调用,比如open、creat、unlink等。

操作步骤:

先安装依赖:

sudo apt install python3-seccomp

然后修改你的run_cmd函数,给子进程加上Seccomp规则:

import seccomp

def setup_sandbox():
    # 创建一个过滤器,默认禁止所有系统调用
    filter = seccomp.SyscallFilter(defaction=seccomp.KILL)
    # 只允许维持进程运行和IO的必要系统调用
    allowed_calls = [
        'read', 'write', 'exit', 'exit_group',
        'brk', 'mmap', 'munmap', 'mprotect',
        'rt_sigreturn', 'sigaltstack'
    ]
    for call in allowed_calls:
        filter.add_rule(seccomp.ALLOW, call)
    # 加载规则,生效
    filter.load()

def run_cmd(args, v_stdin, timelimit=10):
    # 在子进程启动前应用沙箱规则
    p = subprocess.run(
        args,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        input=v_stdin,
        encoding='utf-8',
        timeout=timelimit,
        preexec_fn=setup_sandbox  # 子进程启动时执行这个函数
    )
    return p.stdout.strip(), p.stderr.strip()

这样用户代码一旦尝试调用文件相关的系统调用,内核会直接终止这个进程,根本不会执行恶意操作。

优点:

  • 完全在Python代码里实现,不需要额外安装命令行工具
  • 精准控制,只允许必要的系统调用,资源占用几乎为0

注意:

不同Python版本可能需要调整允许的系统调用列表,如果用户代码运行报错,可以查一下缺失的系统调用,加到允许列表里。


3. 低权限用户+权限控制(最简便的应急方案)

如果上面两个方案对你来说有点复杂,你可以先通过Linux用户权限来快速限制用户代码的访问,虽然安全性不如沙箱,但能直接解决网站文件被篡改的问题。

操作步骤:

  1. 创建一个无权限的专用用户:
sudo useradd -m -s /bin/false sandbox
  1. 让www-data用户(Apache运行的用户)可以无密码切换到sandbox用户执行Python脚本:
    编辑sudoers文件:
sudo visudo

添加一行:

www-data ALL=(sandbox) NOPASSWD: /usr/bin/python3 /path/to/your/verify.py
  1. 修改PHP的调用命令,切换到sandbox用户执行:
$cmd = "sudo -u sandbox python3 verify.py $uploadedFile $questionID $uploadedFileExtension $questionTimeLimit 2>&1";
  1. 设置文件权限:
  • 把用户上传的代码文件放在临时目录,权限设置为只有sandbox用户可读:
chown sandbox:sandbox /path/to/uploaded/files/
chmod 700 /path/to/uploaded/files/
  • 确保网站目录(/var/www/html)的权限是www-data所有,sandbox用户没有读写权限:
chown www-data:www-data /var/www/html/contest/
chmod 755 /var/www/html/contest/

这样用户代码就算想写index.php,也因为sandbox用户没有权限而失败,虽然不能完全禁止读取系统文件,但能有效保护你的网站文件。


方案推荐

  • 追求最高安全性和轻量:选Bubblewrap,隔离彻底,资源占用低
  • 想纯Python代码实现:选Seccomp,精准控制系统调用
  • 快速解决问题,不想折腾:选低权限用户方案,配置简单

内容的提问来源于stack exchange,提问作者Lucas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:22:37