安全地使用php中的eval函数:修改用户输入以避免安全问题

时间:2013-07-15 00:24:15

标签: php security eval

我接管了一些在php中使用eval()函数的webgame代码。我知道这可能是一个严重的安全问题,所以在我决定是否修改代码的这一部分之前,我想要一些帮助审查检查其参数的代码。目前我已经从游戏中删除了这部分代码,直到我确信它是安全的,但功能的丢失并不理想。我宁愿保证这一点,而不是重新设计整个段以避免使用eval(),假设这样的事情是可能的。下面是相关的代码片段,它可以防止恶意代码注入。 $ value是用户输入的字符串,我们知道它不包含“;”。

1 $value = eregi_replace("[ \t\r]","",$value);
2 $value = addslashes($value);
3 $value = ereg_replace("[A-z0-9_][\(]","-",$value);
4 $value = ereg_replace("[\$]","-",$value);
5 @eval("\$val = $value;");

到目前为止,我的理解是这样的:

1)从$ value

中删除所有空格

2)转义需要它进行数据库调用的字符(为什么需要这个我不清楚)

3)查找字母数字字符,然后立即使用\或(并用 - 替换它们的组合。可能这是为了删除字符串中类似函数调用的任何内容,但为什么它也删除前面的字符对我来说不清楚,这就是为什么它会在第2行显式添加后删除\。

4)用 - 替换$的所有实例,以避免类似于字符串中对php变量的引用。

那么:有没有漏洞留在这里?我是否误解了上面的任何正则表达式?最后,有没有任何方法可以安全地证明这一点而不排除(字符?要输入的字符串理想情况下是一个数学公式,并允许(允许操作操作顺序,这是目前不可能的。

3 个答案:

答案 0 :(得分:3)

  1. 评估VM中的代码 - 请参阅Runkit_Sandbox

  2. 或者为您的数学创建解析器。我建议你使用内置的tokenizer。您需要迭代令牌并跟踪括号T_DNUMBERT_LNUMBER,运算符和T_CONSTANT_ENCAPSED_STRING。忽略其他一切。然后,您可以安全地评估结果表达式。

  3. 快速谷歌搜索显示this library。它完全符合您的要求......


  4. 使用tokenizer的一个简单示例:

    $tokens = token_get_all("<?php {$input}");
    $expr = '';
    
    foreach($tokens as $token){
    
      if(is_string($token)){
    
        if(in_array($token, array('(', ')', '+', '-', '/', '*'), true))
          $expr .= $token;
    
       continue;   
      }
    
      list($id, $text) = $token;
    
      if(in_array($id, array(T_DNUMBER, T_LNUMBER)))
        $expr .= $text;
    }
    
    $result = eval("<?php {$expr}");
    

    test

    仅当输入是有效的数学表达式时才会起作用。否则,你的eval`d代码中会出现一个解析错误,因为空括号和类似的东西。如果您还需要处理它,那么清理另一个循环中的输出表达式。这应该照顾大多数无效部分:

    while(strpos($expr, '()') !== false)
      $expr = str_replace('()', '', $expr);
    
    $expr = trim($expr, '+-/*');
    

答案 1 :(得分:1)

匹配允许的内容而非删除某些字符是最好的方法。

我看到你没有过滤可用于执行系统命令的`(反引号)。上帝只知道还有什么不能通过尝试消毒字符串而被阻止......无论发现多少个洞,都无法保证不会有更多。

假设您的语言不是很复杂,那么在不使用eval的情况下自己实现它可能并不困难。

答案 2 :(得分:0)

以下代码是我们自己尝试回答相同类型的问题:

$szCode = "whatever code you would like to submit to eval";

/* First check against language construct or instructions you don't allow such as (but not limited to) "require", "include", ..." : a simple string search will do */
if ( illegalInstructions( $szCode ) )
{
   die( "ILLEGAL" );
}

/* This simple regex detects functions (spend more time on the regex to
   fine-tune the function detection if needed) */
if ( preg_match_all( '/(?P<fnc>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*) ?\(.*?\)/si',$szCode,$aFunctions,PREG_PATTERN_ORDER ) )
{
    /* For each function call */
    foreach( $aFunctions['fnc'] as $szFnc )
    {
        /* Check whether we can accept this function */
        if ( ! isFunctionAllowed( $szFnc ) )
        {
            die( "'{$szFnc}' IS ILLEGAL" );
        }   /* if ( ! q_isFncAllowed( $szFnc ) ) */
    }
}
/* If you got up to here ... it means that you accept the risk of evaluating
   the PHP code that was submitted */
eval( $szCode );