如何评估算术计算?

时间:2016-12-05 17:25:38

标签: php database security

我必须构建一个计算一些数据的应用程序。我不知道科学家可以提出什么计算方法。

例如, 用户A想要计算(A + 5)* 3 用户B想要计算(A + 14)²* pi

算术公式由科学家定义并由管理员存储在数据库中。

简单的方法是:

<?php

    //Formula is initialized by a query in database
    $formula= '(A + 3) * 5';
    //$ value is an integer entered by UserA and verify by Controller
    $value = 42;

    $arithmetic = str_replace('A', $formula, $value);

    $result = eval($arithmetic);

但是Eval是邪恶的,因为@ answer

中的@thpl解释了这一点

我有两个选择:

  1. 做大量的分析和转换公式的每个字符 创建一个伟大的计算类。 (找到每一侧的操作数 通过调用+方法等+并替换addition字符 等。
  2. 使用好的(安全?)正则表达式检查$formula并调用邪恶的eval函数。
  3. 第一种解决方案看起来更安全,但开发时间很长。

    对于第二个解决方案,我在php文档中找到了这个:

    <?php
        $test = '2+3*pi';
    
        // Remove whitespaces
        $test = preg_replace('/\s+/', '', $test);
    
        $number = '(?:\d+(?:[,.]\d+)?|pi|π)'; // What is a number
        $functions = '(?:sinh?|cosh?|tanh?|abs|acosh?|asinh?|atanh?|exp|log10|deg2rad|rad2deg|sqrt|ceil|floor|round)'; // Allowed PHP functions
        $operators = '[+\/*\^%-]'; // Allowed math operators
        $regexp = '/^((' . $number . '|' . $functions . '\s*\((?1)+\)|\((?1)+\))(?:' . $operators . '(?2))?)+$/'; // Final regexp, heavily using recursive patterns
    
        if (preg_match($regexp, $q)) {
            $test = preg_replace('!pi|π!', 'pi()', $test); // Replace pi with pi function
            eval('$result = ' . $test . ';');
        } else {
            $result = false;
        }
    

    第一个问题:第二个解决方案是否足够安全?

    我在互联网上搜索(当然),但找到的最佳解决方案是之前的代码。是否有一些PHP函数,珍珠或PECL库来帮助我?一个arithmetic_eval函数?

    (我在Security.SE没有问,因为我的问题仅涉及php)

1 个答案:

答案 0 :(得分:1)

我认为第一个解决方案(自定义解析器)非常复杂且容易出错。最大的风险是仍然允许攻击者运行任意代码的错误。也许你可以做对,但很容易犯错误。

第二种解决方案(建议的基于正则表达式的验证)可能是好还是不好,很难说。分析PHP语法需要花费大量时间,将其与问题中的正则表达式进行比较,看看PHP中有哪些微妙的方式来编写语句和表达式等等。乍一看它看起来并不是灾难性的,没有人没有批次的分析就能说它是安全的。在那之前使用它会非常危险。

您可能决定接受其中任何一种的风险,因为您说这些公式将由管理员存储。管理员可以查看公式是否是真正的数学公式,似乎不包含任何代码。虽然在看起来像公式的东西中掩盖一些微妙的代码执行可能并非不可能,但如果受信任的管理员在实际使用和评估之前检查所有内容,则风险会低得多。

让我提出一些不同的东西。如果使用沙箱评估表达式会怎样?例如,查看this。您可以轻松地将代码可用的功能限制为数学函数,只要您信任沙箱,就可以放心,不会发生任何恶意操作。这会将问题卸载到第三方(您必须信任,这是一个重要的决定!),您的代码将保持非常简单但相当安全。您可能还想要探索其他沙箱。