PHP EVAL-解决不安全问题 [英] PHP EVAL - Fixing an Insecurity
问题描述
我们有一个必须执行用户输入提供的计算的系统.
We have a system that has to perform calculations that user input provides.
我发现做这些计算之一的最简单方法是eval -试图找出以下解析器:
The easiest way I have found to do one of those calculations is eval -- trying to figure out a parser for:
(3 + 6 ) / 2 + 27 * 5 / 2
似乎很难.如果有人对此有解决方案,我将很高兴听到.
Just seems difficult. If anyone has a solution to this -- I would be happy to hear it.
假设您要使用EVAL(我知道它具有可怕的功能),允许他们在该框中键入所需的内容将是一个主要的不安全因素.
Assuming you are going with EVAL (I know its the dreaded function) it would be a major insecurity to allow them to type whatever they want in that box.
因此,我提出一个问题,如果我进行了正则表达式,则删除了除数字,标准运算符(+-/*)和括号之外的所有内容,例如
So, I pose the question, if I did a regex removing everything besides numbers, standard operators (+ - / *) and parentheses, something like
$equation = preg_replace( '/[^0-9+-\/*()]/', '', $input_equation );
$result = eval( $equation );
系统可能会遭受任何伤害吗?
Is there any harm that could possibly happen to a system?
推荐答案
我最近编码了 BCMath 函数的rel ="nofollow">符合PEDMAS的解释器 :
I recently coded a PEDMAS compliant interpreter that uses BCMath functions:
function BC($string, $precision = 32)
{
if (extension_loaded('bcmath') === true)
{
if (is_array($string) === true)
{
if ((count($string = array_slice($string, 1)) == 3) && (bcscale($precision) === true))
{
$callback = array('^' => 'pow', '*' => 'mul', '/' => 'div', '%' => 'mod', '+' => 'add', '-' => 'sub');
if (array_key_exists($operator = current(array_splice($string, 1, 1)), $callback) === true)
{
$x = 1;
$result = @call_user_func_array('bc' . $callback[$operator], $string);
if ((strcmp('^', $operator) === 0) && (($i = fmod(array_pop($string), 1)) > 0))
{
$y = BC(sprintf('((%1$s * %2$s ^ (1 - %3$s)) / %3$s) - (%2$s / %3$s) + %2$s', $string = array_shift($string), $x, $i = pow($i, -1)));
do
{
$x = $y;
$y = BC(sprintf('((%1$s * %2$s ^ (1 - %3$s)) / %3$s) - (%2$s / %3$s) + %2$s', $string, $x, $i));
}
while (BC(sprintf('%s > %s', $x, $y)));
}
if (strpos($result = bcmul($x, $result), '.') !== false)
{
$result = rtrim(rtrim($result, '0'), '.');
if (preg_match(sprintf('~[.][9]{%u}$~', $precision), $result) > 0)
{
$result = (strncmp('-', $result, 1) === 0) ? bcsub($result, 1, 0) : bcadd($result, 1, 0);
}
else if (preg_match(sprintf('~[.][0]{%u}[1]$~', $precision - 1), $result) > 0)
{
$result = bcmul($result, 1, 0);
}
}
return $result;
}
return intval(version_compare(call_user_func_array('bccomp', $string), 0, $operator));
}
$string = array_shift($string);
}
$string = str_replace(' ', '', str_ireplace('e', ' * 10 ^ ', $string));
while (preg_match('~[(]([^()]++)[)]~', $string) > 0)
{
$string = preg_replace_callback('~[(]([^()]++)[)]~', __METHOD__, $string);
}
foreach (array('\^', '[\*/%]', '[\+-]', '[<>]=?|={1,2}') as $operator)
{
while (preg_match(sprintf('~(?<![0-9])(%1$s)(%2$s)(%1$s)~', '[+-]?(?:[0-9]++(?:[.][0-9]*+)?|[.][0-9]++)', $operator), $string) > 0)
{
$string = preg_replace_callback(sprintf('~(?<![0-9])(%1$s)(%2$s)(%1$s)~', '[+-]?(?:[0-9]++(?:[.][0-9]*+)?|[.][0-9]++)', $operator), __METHOD__, $string, 1);
}
}
}
return (preg_match('~^[+-]?[0-9]++(?:[.][0-9]++)?$~', $string) > 0) ? $string : false;
}
它支持以下运算符:
-
^
(战俘) -
*
-
/
-
%
(模数) -
+
-
-
-
=
,==
,<
,<=
,>
,>=
(比较)
^
(pow)*
/
%
(modulus)+
-
=
,==
,<
,<=
,>
,>=
(comparison)
您这样称呼它:
echo BC('(3 + 6 ) / 2 + 27 * 5 / 2');
我这样做是为了让我有一种简单的方法来执行任意长度的计算,但是,在您的情况下,您也可以剥离所有空白并使用以下正则表达式验证字符:
I did this so I had an easy way to perform arbitrary length calculations but, in your case, you might as well just strip all whitespace and validate the characters using the following regular expression:
if (preg_match('~^(?:[0-9()*/%+-<>=]+)$~', $expression) > 0) {
// safe to eval()
}
这篇关于PHP EVAL-解决不安全问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!