PHP's eval()
is general considered insecure with any user input.
However, with filtering of input (i.e. [-+/*0-9]
) you can make eval()
secure for a small subset of PHP; mathematical expressions.
I've tried to think of a way to extend that by allowing a limited set of math functions and I believe I've found a secure way of doing it.
function expression($expression) {
static $function_map = array(
'floor' => 'floor',
'ceil' => 'ceil',
'round' => 'round',
'sin' => 'sin',
'cos' => 'cos',
'tan' => 'tan',
'asin' => 'asin',
'acos' => 'acos',
'atan' => 'atan',
'abs' => 'abs',
'log' => 'log',
'pi' => 'pi',
'exp' => 'exp',
'min' => 'min',
'max' => 'max',
'rand' => 'rand',
'fmod' => 'fmod',
'sqrt' => 'sqrt',
'deg2rad' => 'deg2rad',
'rad2deg' => 'rad2deg',
);
// Remove any whitespace
$expression = strtolower(preg_replace('~\s+~', '', $expression));
// Empty expression
if ($expression === '') {
trigger_error('Empty expression', E_USER_ERROR);
return null;
}
// Illegal function
$expression = preg_replace_callback('~\b[a-z]\w*\b~', function($match) use($function_map) {
$function = $match[0];
if (!isset($function_map[$function])) {
trigger_error("Illegal function '{$match[0]}'", E_USER_ERROR);
return '';
}
return $function_map[$function];
}, $expression);
// Invalid function calls
if (preg_match('~[a-z]\w*(?![\(\w])~', $expression, $match) > 0) {
trigger_error("Invalid function call '{$match[0]}'", E_USER_ERROR);
return null;
}
// Legal characters
if (preg_match('~[^-+/%*&|<>!=.()0-9a-z,]~', $expression, $match) > 0) {
trigger_error("Illegal character '{$match[0]}'", E_USER_ERROR);
return null;
}
return eval("return({$expression});");
}
It's use would be simple: `$result = expression('floor(3 / 2)') and anything but math/boolean operators, numbers and a limited set of functions should be filtered out.
I can't find a way of hacking this expression()
function, but I'd really like to have a second opinion on this.
Is the above expression
function secure for user input?