5

我正在创建一个验证器,它能够通过允许开发人员在条件规则中使用条件语句来处理复杂的条件。

以这个带有一组规则的例子为例:

...
"element_name":{
     "required": "conditional",
     "conditions" : {
         "requirements" : "(4 < 5)",
         "requirements" : "('something' == 'something_else')" 
     }
}
...

然后 PHP 将做的是遍历这些requirements并将它们评估为代码以返回一个布尔值,该布尔值将确定是否需要该元素。

使用该eval()功能的问题非常明显。所以我在问,鉴于条件语句是只允许的,有没有比这样做更安全的方法:

$result = eval(element_name->requirements[0]);

感谢你们。

- - 更新 - - -

谢谢迈克和大家的想法,我希望我能把你们都标记为答案,因为老实说,我最终使用了每个人的一点点想法。更多的迈克,所以他明白了。

因此,这可能是将来要考虑的事情,因为它是有条件地验证字段的一种非常有吸引力的方法。我的目标是创建一种直观的方式来解决这个问题。我喜欢在 json 配置文件中简单地添加一个条件语句的想法。当然,这会涉及一些严重的安全风险或超级复杂的解析引擎,所以我最终要求开发人员学习我们的条件语言方法,但正如你所见,我保持它与原始方法非常相似。我认为拥有简单的 API 很重要,否则你会阻碍平台上的开发。一探究竟:

"element_name":{
     "required": "conditional",
     "conditions" : {
         "<" : ['4', '5'],
         "==" : [':element_id', ':other_element'], // will grab the values of those elements and compare
         "exp" : [['something', '==', 'something_else'], 'OR', [':other_element', '!=', '0']] 
     }
}
4

4 回答 4

4

一种解决方案是构建 JSON 结构,使其限制可以执行的操作的总集。例如

"element_name":{
 "required": "conditional",
 "requirements" : [
        {
            "condition1": 4,
            "condition1": 5,
            "operation": "greaterthan"
        }
}

然后在你的 PHP 中(psedo-codey,但你可以明白):

foreach($requirements as $key => $test){
    switch($test->operation) {
        case 'greaterthan':
            return ($test->condition1 > $test->condition2);
        /// put other comparison types here

    }
}

这将意味着编写更多的业务逻辑,但最终会更安全,并且可以防止像eval.

于 2013-07-26T00:54:49.343 回答
3

为了扩展 dethtron5000 的答案,我想到这样做以防止出现异常复杂的正则表达式的一种方法是让开发人员将他的条件分解成更多的多维条件事物并使用递归函数对其进行循环。在每个级别,您都会有一个“运算符”,它可以是“AND”或“OR”(至少我希望这被称为“运算符”,如果不能随意更改它)。

在您的示例中,您有:(32 < 40 AND 10 > 5 OR 20 == 10)

(看起来您正在对条件进行 json_encoding,所以我从以下 PHP 数组开始,然后从那里向后工作。我假设您可以通过json_decode您的开发人员提供的内容来获得有效的 PHP 数组)。上面的示例将表示为以下 PHP 数组:

$arr = array(
    'required' => 'conditional',
    'conditions' => array(
        'requirements' => array(
            'operator' => 'OR', // this makes it so that if any conditions at this level are true, it returns true
            0 => array(
                'operator' => 'AND', // this makes it so that all the conditions at this sub-level need to be satisfied to return true
                array(
                    'conditional1' => 32,
                    'conditional2' => 40,
                    'operation' => 'lessthan',
                ),
                array(
                    'conditional1' => 10,
                    'conditional2' => 5,
                    'operation' => 'greaterthan',
                ),
            ),
            1 => array(
                // Since there is only one condition here, it is not necessary to specify "AND" or "OR"
                array(
                    'conditional1' => 20,
                    'conditional2' => 10,
                    'operation' => 'equals',
                ),
            ),
        ),
    ),
);

然后,您可以使用如下递归函数遍历条件:

function check_req(Array $reqs) {
    $operator = (isset($reqs['operator'])) ? $reqs['operator'] : 'AND';
    unset($reqs['operator']);
    foreach ($reqs as $req) {
        if (isset($req['operation'])) {
            switch ($req['operation']) {
                case 'lessthan':
                    $valids[] = $req['conditional1'] < $req['conditional2'];
                    break;
                case 'greaterthan':
                    $valids[] = $req['conditional1'] > $req['conditional2'];
                    break;
                case 'equals':
                    $valids[] = $req['conditional1'] == $req['conditional2'];
                    break;
            }
        }
        else {
            $valids[] = check_req($req);
        }
    }
    if ($operator == 'OR') {
        foreach ($valids as $valid) {
            if ($valid == true) {
                return true;
            }
        }
        return false;
    }
    else {
        foreach ($valids as $valid) {
            if ($valid == false) {
                return false;
            }
        }
        return true;
    }
}

var_dump(check_req($arr['conditions']['requirements'])); // true in this case

当我对其进行 json_encode 编码时,我得到:

{
    "required":"conditional",
    "conditions":{
        "requirements":{
            "operator":"OR",
            "0":{
                "operator":"AND",
                "0":{
                    "conditional1":32,
                    "conditional2":40,
                    "operation":"lessthan"
                },
                "1":{
                    "conditional1":10,
                    "conditional2":5,
                    "operation":"greaterthan"
                }
            },
            "1":[{
                "conditional1":20,
                "conditional2":10,
                "operation":"equals"
            }]
        }
    }
}

我假设这是开发人员必须为您提供的。

于 2013-07-26T02:24:43.360 回答
2

要分解初始文本,可以使用 json_decode() 吗?这不会运行任何东西,但会将您的大字符串转换为数组结构。

对于实际的单个表达式,您可以访问parsekit_compile_string吗?

这将允许将原始文本转换为 php 字节码操作。通过 switch 语句来解释操作应该不难。由于您在要求中所说的内容的限制性质,这不会是太多的代码。

可以在没有 的情况下尝试正则表达式parsekit_compile_string,但这将是一种更脆弱的解决方案。

于 2013-07-26T01:20:27.020 回答
1

是的 - 您可以使用 switch 语句:

开关($操作员)

 case "==":
 return $a==$b;
 break;
 case "+":
 return $a+$b;
 break;

 etc...

 default:
 return false;
 break;

关键是如何将零件发送到交换机 - 毕竟有很多方法。

将 eval() 暴露给不受信任的用户代码会很疯狂。我可以看到它在 Drupal 等情况下的使用,超级用户可以创建 PHP 页面,然后对其进行评估,但当然只有受信任的用户才能这样做。最好限制可以以这种方式运行的代码。

编辑:

为了处理多个运算符,您仍然将其作为算术单元运行,但您需要分别评估每个运算符。这可能意味着您必须一次评估一个字符的指令字符串才能拾取例如括号。在客户端用javascript做数学可能会更好。只要答案不发送到您的服务器,这是安全的(JS 中的 eval 会导致 DOM 注入攻击)

如果我可能会提出一个无礼的建议,你真的需要这样做吗?有没有更好的方法,也许可以处理更小块的输入?如果我使用这个系统,我可能会拿起桌面计算器来完成这项工作,而不是使用网站!尝试为他们做更多用户的工作。

于 2013-07-26T00:54:41.533 回答