如何评估用户提供的代码?

5 php evaluation eval expression-evaluation

我需要使用 PHP 在服务器上处理一些用户提供的代码。该代码将涵盖一些非常基本的编程功能,例如:变量、文字、(最好)函数和一些相关操作。

一种选择是使用 的危险函数eval()。对于我的具体情况,除了安全问题和性能瓶颈之外,它的功能极其丰富且冗余。使用消毒令牌可以token_get_all()防止墨菲,而不是马基雅维利!不管它有什么缺点,它确实能够实现我一直渴望实现的目标。

我还检查了 Symphony 的ExpressionLanguage;它有一些缺点:

  • 它无法自行检测“变量”(应事先引入并了解它们)
  • 它缺乏基本的变量功能(仅初始化它们:没有赋值功能)
  • 它专为“单行”表达而设计

唉! 更复杂的ExpressionLanguage就足够了。

我正在寻找一种能够为用户提供一些非常基本的“编程”能力的东西。有这样的事吗,如果有的话,那是什么?(即使它是用另一种语言编写的,但可以在服务器上以某种方式使用。)如果这样的东西不存在,那么我应该如何对待eval()不吸引我?!或者,作为最后的手段,我如何设计这样一个简单的编程功能?(请详细说明事项:)


根据下面的评论,这里是代码语法需要支持的“编程”功能的列表。除了ExpressionLanguage 系统提供的内容之外,如果还支持以下内容就足够了:

  • 顺序流:一个接一个地执行指令(与 ExpressionLanguage 的“单行”性质相反)
  • 局部变量声明(当然还有之后的检测)
  • 表达式中的变量赋值(文字、函数调用、运算符的任意组合)
  • 将变量传递给函数
  • 流程控制结构:至少一个条件结构(例如:)if和一个重复结构(例如:for循环)

Nic*_*coM 3

也许你可以看一下 Docker。例如,您可以将用户代码复制到服务器上的文件中(而不执行它),然后在容器内运行它。这将使您能够:

  • 将代码运行到特定的专用容器中,该容器可以在脚本执行后销毁
  • 使用不同版本的 PHP 运行代码

一些例子:

docker run -v "$PWD":/usr/src -w /usr/src --rm php:7.4.5 php ./myScript.php
Run Code Online (Sandbox Code Playgroud)

这将在基于 php 7.4.5 的新容器中执行文件 myScript.php,完成后容器将被删除。

使用另一个 PHP 版本同样的事情:

docker run -v "$PWD":/usr/src -w /usr/src --rm php:5.6 php ./myScript.php
Run Code Online (Sandbox Code Playgroud)

有用的链接:

关于表演的编辑:

显然,使用容器运行代码比直接从 PHP 运行代码要长。

例如,我们可以运行以下代码来测试它:

<?php

function reverseArray(array $array): array {
    for ($i = 0; $i < count($array) / 2; $i++) {
        $tmp = $array[$i];
        $array[$i] = $array[count($array) -1 - $i]; 
        $array[count($array) - 1 - $i] = $tmp;
    }
    return $array;
}   


$tabToReverse = [5, 8, 95, 10, 6, 17, 42, 20];
echo 'Reversed array : '."\n";
echo  implode(' ', reverseArray($tabToReverse));
Run Code Online (Sandbox Code Playgroud)

相同的代码,但有语法错误:

<?php

// syntax error
function reverseArray($array: array): array {
    for ($i = 0; $i < count($array) / 2; $i++) {
        $tmp = $array[$i];
        $array[$i] = $array[count($array) -1 - $i]; 
        $array[count($array) - 1 - $i] = $tmp;
    }
    return $array;
}   


$tabToReverse = [5, 8, 95, 10, 6, 17, 42, 20];
echo 'Reversed array : '."\n";
echo  implode(' ', reverseArray($tabToReverse));
Run Code Online (Sandbox Code Playgroud)

以下 PHP 代码将比较两个执行(加上一个有语法错误的执行):

<?php

/**
 * Run code using eval
 */
$start = microtime(true);
$code = str_replace('<?php', '', file_get_contents('./reverseArray.php'));
echo eval($code)."\n";
$end = microtime(true);
$duration = $end - $start;
echo "Duration using eval $duration\n"; 


/**
 * Run code using container
 */
$start = microtime(true);
$cmd = 'docker run -v "$PWD":/usr/src -w /usr/src --rm php:7.4.5 php ./reverseArray.php';
exec($cmd, $result);
echo implode("\n", $result)."\n";
$end = microtime(true);
$duration = $end - $start;
echo "Duration using container $duration\n";

/**
 * Run code using container with an error
 */
$start = microtime(true);
$cmd = 'docker run -v "$PWD":/usr/src -w /usr/src --rm php:7.4.5 php ./reverseArrayWithError.php';
exec($cmd, $resultWithError);
echo implode("\n", $resultWithError)."\n";
$end = microtime(true);
$duration = $end - $start;
echo "Duration using container $duration\n";
Run Code Online (Sandbox Code Playgroud)

我的笔记本电脑上的结果是:

php ./runCode.php 
Reversed array : 
20 42 17 6 10 95 8 5
Duration using eval 0.00031089782714844
Reversed array :
20 42 17 6 10 95 8 5
Duration using container 0.79519391059875

Parse error: syntax error, unexpected ':', expecting ')' in /usr/src/reverseArrayWithError.php on line 3
Duration using container 0.81346988677979
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,运行容器需要时间。但代码已经在特定区域执行了。

在所有情况下,前端部分都是相同的,它将使用一些 Ajax 查询来将数据 POST 到服务器上,等待结果并显示它。

注 1:即使代码在特定容器中执行,也必须先进行清理,因为用户输入永远不应该被信任。

注2:使用此架构需要管理正在运行的容器以防止过载。如果 10 000 个用户同时提交代码会发生什么?但我认为这是另一个话题。