本文目录导读:

- 方案一:使用扩展
php.ini配置(最轻量,但不安全) - 方案二:使用
Laravel/Symfony的进程隔离(较安全,推荐) - 方案三:使用第三方扩展
php-sandbox(已淘汰) - 方案四:使用
HHVM或Zephir(技术性替代) - 方案五:使用外部隔离容器(最安全,但工程量大)
- 重点:沙箱必须防护的内容
- 总结建议
在 PHP 中实现沙箱(Sandbox)是一个复杂且有争议的话题,因为 PHP 本身并没有内置的官方沙箱机制,完全隔离和限制 PHP 代码执行非常困难,尤其是在共享环境下。
以下是几种常见的 PHP 沙箱实现方案,从简单到复杂,以及它们各自的优缺点:
使用扩展 php.ini 配置(最轻量,但不安全)
这是最简单的“伪沙箱”方式,通过修改 php.ini 或 ini_set() 禁用危险函数。仅适用于隔离自己写的代码或防止误操作,不适用于运行不可信的第三方代码。
<?php
// 在脚本开头禁用危险函数
ini_set('disable_functions', 'exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source,highlight_file');
ini_set('open_basedir', '/tmp/sandbox');
ini_set('allow_url_fopen', 'Off');
// 你的代码
echo "Hello Safe World";
?>
缺点:
- 可以被
dl()或putenv()绕过(如果未禁用)。 - 内存、CPU、执行时间无法限制。
- 无法阻止死循环导致的资源耗尽。
使用 Laravel/Symfony 的进程隔离(较安全,推荐)
原理: 将用户代码放到一个单独的 PHP 进程(或 php-cli)中执行,父进程只负责监控和传递输入/输出,通过操作系统级别的权限控制(如 chroot、systemd 限制)来隔离。
实现思路:
- 父进程获取用户代码。
- 将代码写入一个临时文件。
- 使用
proc_open()或exec()调用/usr/bin/php /tmp/user_code.php(注意:必须禁用父进程的exec,否则会被绕过)。 - 使用
set_time_limit(0)和memory_limit限制子进程资源。
代码示例(核心逻辑):
<?php
// 用户提交的代码(假设已经过滤了无限循环)
$userCode = $_POST['code'];
// 创建临时文件
$tmpFile = tempnam(sys_get_temp_dir(), 'sandbox_');
file_put_contents($tmpFile, $userCode);
// 使用 proc_open 执行,设置超时和限制
$descriptors = [
0 => ["pipe", "r"], // 标准输入
1 => ["pipe", "w"], // 标准输出
2 => ["pipe", "w"], // 标准错误
];
$process = proc_open(
'timeout 5 php -d memory_limit=128M ' . escapeshellarg($tmpFile),
$descriptors,
$pipes
);
// 等待执行并读取输出
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
proc_close($process);
unlink($tmpFile); // 删除临时文件
echo "输出: " . $stdout . "\n错误: " . $stderr;
?>
优点: 利用操作系统隔离,比 disable_functions 安全得多。
缺点: 进程开销大,无法限制共享内存等高级功能。
使用第三方扩展 php-sandbox(已淘汰)
以前有一个著名的扩展叫做 php-sandbox (由 Frost 开发),它允许通过 inlcude 函数运行受限代码,但该项目已停止维护,且与 PHP 7+ 不兼容,不建议使用。
使用 HHVM 或 Zephir(技术性替代)
- HHVM (HipHop Virtual Machine) 提供了
Sandbox指令,但 HHVM 已基本淡出主流。 - Zephir 可以将 PHP 代码编译为 C 扩展,但这不是沙箱。
使用外部隔离容器(最安全,但工程量大)
这是最推荐的方案,如果你要运行完全不可信的第三方代码。
技术栈:
- Docker / Podman:为每个请求启动一个临时容器,挂载安全的 PHP 环境。
- Firejail / bubblewrap:轻量级安全沙箱工具,限制系统调用和文件系统访问。
- 系统调度工具:使用
cgroup限制 CPU 和内存。
PHP 调用示例(简单模拟):
<?php
$userCode = $_POST['code'];
// 将代码写入宿主机的一个临时目录
$containerCodePath = '/var/sandbox/data/';
file_put_contents($containerCodePath . 'main.php', $userCode);
// 通过 Docker CLI 运行容器(真实场景中建议使用 Docker API 或 SDK)
$result = shell_exec(
'docker run --rm --memory=256m --cpus=0.5 -v /var/sandbox/data:/app:ro php:8.2-cli php /app/main.php 2>&1'
);
echo "结果: " . $result;
?>
优点: 真正的完全隔离,可以限制一切资源。 缺点:
- 需要 Docker 环境,性能开销大(每次启动容器约 100-300ms)。
- 需要处理容器内的网络、权限、依赖问题。
重点:沙箱必须防护的内容
无论哪种方案,以下几类操作必须被禁止或限制:
- 文件系统:读取
/etc/passwd、写入 Web 目录等。 - 网络:访问内网、发送垃圾邮件(
curl、fsockopen)。 - 执行系统命令:
exec、system、shell_exec、passthru等。 - 资源耗尽:无限循环、大数组、高内存分配。
- 反射与元编程:
ReflectionClass、eval()、assert()(在 PHP 5 中容易被利用)。 - 信息泄露:
phpinfo()、get_defined_vars()。
总结建议
| 场景 | 推荐方案 |
|---|---|
| 运行自己写的、可控的代码 | 使用 disable_functions + open_basedir |
| 运行受信任用户的代码(如在线教育平台) | 进程隔离(方案二)+ 限制函数 |
| 运行完全不可信的第三方代码 | Docker/Firejail 容器隔离(方案五) |
| 需要跨语言隔离(如 JS 转 PHP) | 考虑改用 WebAssembly (WASM) |
强烈建议: 在没有专业安全团队和安全基础防护的情况下,不要尝试用纯 PHP 实现完整沙箱,采用 Docker 容器隔离 是当前业界认可的唯一可靠方案(如 Judge0、LeetCode 的判题系统)。