有没有一种方法可以在 PHP 中的沙盒中执行 PHP 代码?

22

我希望能够在php中执行一个脚本,该脚本将使用已定义的不同常量和不同版本的类。

是否有一个沙盒php模块,我只需要:

sandbox('script.php'); // run in a new php environment

代替

include('script.php'); // run in the same environment

还是proc_open()是唯一的选择吗?

PS:该脚本无法通过Web访问,因此fopen('http://host/script.php')不是选项。

7个回答

10

runkit,但如果您不需要主进程和子进程之间的任何交互,您可能会发现通过命令行调用脚本(使用shell_exec)更简单。


2
关于 runkit;根据描述,它似乎无法很好地进行沙盒隔离,或者我应该说不太容易。你可以禁止函数,但我更愿意禁止除提供的列表中的函数之外的所有函数。如果用户需要一个函数,我可以在请求时手动评估其安全性。看起来唯一的方法是编写一个定制的解释器。如果速度是一个问题,你可以让它将其 AST 转换为 PHP 或其他语言,这实际上是我现在要做的,因为我找不到现成的解决方案。干杯!附:是的,我知道这个问题很旧了。 - Frank
好吧..祝你好运找到能将PHP解析为AST的东西;) 不过我同意你的观点。 - troelskn
FYI,runkit 似乎已经被放弃了,如果你想在现代版本的 PHP 上运行它,你可能需要编译 CVS 版本或其中一个补丁版本(http://github.com/tricky/runkit)。 - Eli

6

2
此外,您还应该查看反引号运算符。 (链接)
$sOutput = `php script_to_run.php`;

这将允许您检查正在运行的脚本的输出。但是请注意,脚本将使用您拥有的特权运行,但您可以通过在Linux上使用sudo来规避此问题。
这种方法还假定您已安装PHP CLI,但并非总是如此。

2

有一个名为Runkit_Sandbox的PHP扩展,您可以尝试使用它。我认为这是最好的选择。

但是,您可能需要创建自己的“沙盒”,例如通过重置您使用的超全局变量状态来实现。

class SandboxState
{
    private $members = array('_GET', '_POST');
    private $store = array();
    public function save() {
        foreach($members as $name) {
            $this->store[$name] = $$name;
            $$name = NULL;
        }
    }
    public function restore() {
        foreach($members as $name) {
            $$name = $this->store[$name];
            $this->store[$name] = NULL;
        }

    }
}

使用方法:

$state = new SanddboxState();
$state->save();

// compile your get/post request by setting the superglobals
$_POST['submit'] = 'submit';
...

// execute your script:
$exec = function() {
    include(func_get_arg(0)));
};
$exec('script.php');

// check the outcome.
...

// restore your own global state:
$state->restore();

2
动态插件函数执行允许加载的文件和函数执行它想要的任何操作,但它只能接受和返回可以被 "json_encode" 的变量。
function proxyExternalFunction($fileName, $functionName, $args, $setupStatements = '') {
  $output = array();
  $command = $setupStatements.";include('".addslashes($fileName)."');echo json_encode(".$functionName."(";
  foreach ($args as $arg) {
    $command .= "json_decode('".json_encode($arg)."',true),";
  }
  if (count($args) > 0) {
    $command[strlen($command)-1] = ")";//end of $functionName
  }
  $command .= ");";//end of json_encode
  $command = "php -r ".escapeshellarg($command);

  exec($command, $output);
  $output = json_decode($output,true);
}

外部代码完全被隔离,您可以通过执行sudo -u restricedUser php -r ...来应用任何权限限制。


1
我已经为此开发了一个BSD许可的沙箱类。它利用PHPParser库分析沙盒代码,检查用户可配置的白名单和黑名单,并提供广泛的配置选项以及合理的默认设置。对于您的需求,您可以轻松地重新定义在沙盒代码中调用的类并将它们路由到不同的类。该项目还包括一个沙盒工具包(仅在本地计算机上使用!),可用于尝试沙盒设置,并提供完整的手册和API文档。

https://github.com/fieryprophet/php-sandbox


有没有办法在不通过composer安装您的类的情况下使用它。我不想在生产服务器上安装它,只是使用纯类? - Obaid Maroof

0

我知道这不完全是与主题相关的,但可能对某些人有用 n__n

function require_sandbox($__file,$__params=null,$__output=true) {

    /* original from https://dev59.com/flDTa4cB1Zd3GeqPM-S7#3850454 */

    if($__params and is_array($__params))
     extract($__params);

    ob_start();
    $__returned=require $__file;
    $__contents=ob_get_contents();
    ob_end_clean();

    if($__output)
     echo $__contents;
    else
     return $__returned;

};

如果$__file尝试定义已经定义的常量,它将无法工作。如果$__file使用global关键字或$GLOBALS数组引用全局作用域中的变量,它还可能破坏全局变量。 - DrLightman
我更新了代码,现在试试吧 :D 对我很有用 :D 可能不完美,但对我来说很有用,因为我需要一些在执行期间创建变量的 PHP 文件,我不希望它们在全局范围内 :D - AgelessEssence
这更像是一个template_require,而不是一个沙盒。 - Tofandel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接