简体   繁体   English

试图不在php中使用eval()来评估带有条件语句的字符串

[英]Trying not to use eval() in php to evaluate a string with a condition statement

I'm creating a validator that will be able to handle complex conditions by allowing the dev to use a conditional statement in the conditions rules. 我正在通过允许dev在条件规则中使用条件语句来创建一个能够处理复杂条件的验证器。

Take this example with a set of rules: 以一组规则为例:

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

what the PHP will then do is loop through those requirements and evaluate them as code to return a boolean which will determine if the element is required or not. 然后,PHP将循环执行这些requirements并将它们作为代码进行评估,以返回一个布尔值,该布尔值将确定元素是否需要。

The problem with using the eval() function is quite obvious. 使用eval()函数的问题非常明显。 So I'm asking, given that condition statements are what will only be allowed, is there a safer way of doing this than: 所以我问,鉴于条件陈述是唯一允许的,是否有更安全的方式来做到这一点:

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

Thank you guys. 感谢你们。

---- UPDATE ----- ----更新-----

Thank you Mike and everyone for the ideas, I wish I could mark you all as the answer because to be quite honest, I ended up using a little bit of everyone's idea. 谢谢Mike和大家的想法,我希望我能把所有人都标记为答案,因为老实说,我最终使用了一点点每个人的想法。 More of Mike's so he gets it. 更多迈克,所以他得到了它。

So, this is probably something that is going to be looked at in the future because it's quite an attractive method of conditionally validating a field. 所以,这可能是将来要考虑的事情,因为它是有条件地验证字段的一种非常有吸引力的方法。 My goal is to create an intuitive way of going about this. 我的目标是创造一种直观的方式来实现这一目标。 I liked the idea of simply slapping in a conditional statement right in the json config file. 我喜欢简单地在json配置文件中使用条件语句。 Of course, this would involve some serious security risks or a super complex parsing engine, so I ended requiring the dev to learn our method of conditional language, but as you will see, I kept it quite similar to the original one. 当然,这将涉及一些严重的安全风险或超级复杂的解析引擎,所以我最终要求开发人员学习我们的条件语言方法,但正如您将看到的,我保持它与原始方法非常相似。 I think it's important to have simple APIs otherwise you'll deter development on your platform. 我认为拥有简单的API非常重要,否则你将无法在平台上进行开发。 Check it out: 看看这个:

"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']] 
     }
}

One solution would be to structure your JSON so that it limits the total set of operations that can be performed. 一种解决方案是构建您的JSON,以便它限制可以执行的总操作集。 For example 例如

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

and then in your PHP (psedo-codey but you can get the idea): 然后在你的PHP(psedo-codey,但你可以得到这个想法):

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

    }
}

This would mean coding more business logic but would ultimately be safer and would prevent injections like eval . 这意味着编写更多的业务逻辑,但最终会更安全,并防止像eval这样的注入。

To expand on dethtron5000's answer, one way I thought of doing this to prevent a ridiculously complex regex is to have the dev break up his conditionals into more of a multi-dimensional conditional thing and loop over it using a recursive function. 为了扩展dethtron5000的答案,我想要做的一个方法是防止一个荒谬复杂的正则表达式,就是让dev将条件分解成更多的多维条件事物并使用递归函数循环它。 At each level, you would have an "operator", which would be either "AND" or "OR" (at least I hope this is called an "operator", if not feel free to change it). 在每个级别,你将有一个“运算符”,它可以是“AND”或“OR”(至少我希望这被称为“运算符”,如果不随意更改它)。

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

(It looks like you're json_encoding the conditionals, so I started with the following PHP array and worked backwards from there. I'm assuming you can just json_decode what your dev provides you to get a valid PHP array). (看起来你是json_encoding条件,所以我开始使用下面的PHP数组并从那里开始向后工作。我假设你可以只使用json_decode你的dev为你提供一个有效的PHP数组。) The above example would be represented as the following PHP array: 上面的例子将表示为以下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',
                ),
            ),
        ),
    ),
);

You can then loop through the conditionals with a recursive function like this: 然后,您可以使用递归函数循环遍历条件,如下所示:

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

When I json_encode it, I get: 当我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"
            }]
        }
    }
}

I'm assuming this is what the dev would have to provide you. 我假设这是开发人员必须提供给你的东西。

To decompose the initial text, could you use json_decode()? 要分解初始文本,你能使用json_decode()吗? This won't run anything, but will covert your big string into an array structure. 这不会运行任何东西,但会将您的大字符串转换为数组结构。

For the actual individual expressions, do you have access to parsekit_compile_string ? 对于实际的单个表达式,您是否可以访问parsekit_compile_string

This would allow the raw text to be converted into php bytecode ops. 这将允许原始文本转换为php字节码操作。 It shouldn't be hard to interpret the ops via a switch statement. 通过switch语句解释ops应该不难。 Due to the constrained nature of what you said you expect in the requirements, this wouldn't be much code. 由于您在要求中所期望的性质受限,因此代码不会太多。

One could attempt regex without parsekit_compile_string , but this would be a more brittle solution. 可以在没有parsekit_compile_string情况下尝试正则表达式,但这将是一个更脆弱的解决方案。

Yes - you can use a switch statement vis: 是的 - 你可以使用switch语句:

switch($operator) 开关($运营商)

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

 etc...

 default:
 return false;
 break;

The key is how to send the parts to the switch - there are many ways after all. 关键是如何将部件发送到交换机 - 毕竟有很多方法。

It would be mad to expose eval() to untrusted user code. 这将是疯狂的,露出的eval(),以不信任的用户代码。 I can see its use in situations like Drupal, where the uber-users can create PHP pages which are then evaluated as such, but of course only trusted users can do that. 我可以在Drupal这样的情况下看到它的用法,超级用户可以在其中创建PHP页面然后进行评估,但当然只有受信任的用户才能这样做。 Far better to restrict the code that can be run in this way. 最好限制可以这种方式运行的代码。

EDIT: 编辑:

In order to handle multiple operators, you still run this as your arithmetic unit, but you need to evaluate each operator separately. 为了处理多个运算符,您仍然将其作为算术单位运行,但您需要单独评估每个运算符。 This may mean you have to evaluate an instruction string one character at a time to pick up such as brackets. 这可能意味着您必须一次评估一个字符的指令字符串,例如括号。 It may be better to do the math in javascript on client side. 在客户端javascript中进行数学运算可能会更好。 This is safe as long as the answer is not sent to your server (eval in JS can lead to DOM injection attacks) 只要答案没有发送到您的服务器,这是安全的(JS中的eval可能导致DOM注入攻击)

If I might make an impertinent suggestion, do you REALLY need to do it this way? 如果我提出一个无礼的建议,你真的需要这样做吗? Is there a better way, perhaps one that will deal with the input in smaller chunks? 有没有更好的方法,或许可以处理较小块的输入? If I were using this system, I'd probably pick up a desk calculator to do the work rather than use a website! 如果我使用这个系统,我可能会选择一个桌面计算器来完成工作而不是使用网站! Try to do more of your users' work for them. 尝试为他们完成更多用户的工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM