Reply to Re: eval() is insecure, but what are the alternatives?

Your name:

Reply:


Posted by Jim Michaels on 02/15/06 10:36

http://muparser.sourceforge.net/
if you want to convert the code. fairly complex. better make this one a DLL
extension of PHP.

"Pedro Graca" <hexkid@dodgeit.com> wrote in message
news:slrndt3300.h04.hexkid@ID-203069.user.individual.net...
> werner wrote:
>> I agree with the parser suggestion and have also
>> previously searched for an expression parser, as this would be the best
>> approach.
>>
>> I am also looking at maybe porting an existing Java solution, what do
>> you think? It's just going to take some time, and I sadly don't have
>> much of that left.
>
> Use bc! :-)

http://us2.php.net/manual/en/ref.bc.php (does not have eval-like function)
instead of using backticks, use the library?

http://www.bestcode.com/html/bcparserx.html COM control that evals math,
co$t$ money.
http://www.freevbcode.com/ShowCode.asp?ID=2090 vree vb parser, not too
complicated. I am converting it now. here.

<?php
//Attribute VB_Name = "Module1"
/*
' Acclaimer and Disclaimer!
'*********************************************************************************************
'* This code is written by Jonathan Adamit (c) 20th in December
2000. *
'* I Am in no way responsible for anything that occures as a result of
implementing *
'* this code or by using it in any way.
*
'* This code is given to you for free to do whatever you want to do with
it. *
'* My only request is that if you use it - you give me the credit for it.
*
'* I will also be very greatful if you E-mail me any improvements or
changes you made to it.*
'* Any comments or changes are to be sent to:
adamit@bgumail.bgu.ac.il *
'*********************************************************************************************
version 1.0

'
' Example of use and remarks!
'*****************************************************************************************************************
'* On Error Resume Next
*
'* Dim X As Double
*
'* X = Parse("2+3*4^2*2*cos[3]+2")
*
'* if Err.Number = 11 Then MsgBox "Division by Zero", , "Uncalculatable"
*
'* if Err.Number = vbObjectError + 555 Then MsgBox "Specified Text Sequence
Not Allowed!", , "Invalid Equation" *
'*---------------------------------------------------------------------------------------------------------------*
'* cos/sin/tan/atn must be enclosed with [] as the example shows.
*
'* Use of absolute signs (|) is allowed.
*
'*****************************************************************************************************************
*/

function RemSpaces($Exp) {
return str_replace(" ","",$Exp);
}

function RemPlusMinus($Exp) {
$NewExp="";
$RPM = $Exp;
$Place = strpos($Exp, "+-");
if ($Place === false) { } else {
$NewExp = substr($Exp, 0, ($Place - 1)+1) . "-" . substr($Exp, -1,
(strlen($Exp) - $Place - 1)+1);
if (strpos($NewExp, "+-") === false) { } else {
$NewExp = RemPlusMinus($NewExp);
}
$RPM = $NewExp;
}
$Place = strpos($NewExp, "--");
if ($Place === false) { } else {
$NewExp = substr($NewExp, 0, ($Place - 1)+1) . "+" . substr($NewExp, -1,
(strlen($NewExp) - $Place - 1)+1);
if (strpos($NewExp, "--") === false) { } else {
$NewExp = RemPlusMinus($NewExp);
}
return $NewExp;
}
return $RPM;
}

function isnumeric($x) {
return 1==preg_match("/[\deE\.]/", $x);
}

function IsValid($Exp) {
for ($X = 0; $X < strlen($Exp); $X++) {
switch ($Exp{$X}) {
case "[": case "]": case "(": case ")": case "*": case "/": case "+": case
"-": case "^":
break;
case "|":
if (substr($Exp, $X, 2) == "||") {
echo "mdlParse: Specified Text Sequence Not Allowed";
return false;
}
break;
default:
switch(isnumeric($Exp{$X})) {
case false:
switch($X) {
case 0://1:
$Temp = substr($Exp, $X, 3);
if (($Temp != "tan") && ($Temp != "cos") && ($Temp != "sin") && ($Temp
!= "atn")) {
echo "mdlParse: Specified Text Sequence Not Allowed";
return false;
}
break;
case 1://2:
$Temp = substr($Exp, $X - 1, 3);
if (($Temp != "tan") && ($Temp != "cos") && ($Temp != "sin") && ($Temp
!= "atn")) {
echo "mdlParse: Specified Text Sequence Not Allowed";
return false;
}
break;
default://case Is > 2
$Temp = substr($Exp, $X - 2, 6);
if ((strpos($Temp, "cos[") === false) && (strpos($Temp, "sin[") ===
false) && (strpos($Temp, "tan[") === false) && (strpos($Temp, "atn[") ===
false)) {
echo "mdlParse: Specified Text Sequence Not Allowed";
return false;
}
break;
}
break;
case true:
$Temp = substr($Exp, $X, 2);
//if ((strpos($Temp, "[") >= 0) || (strpos($Temp, "(") >= 0) ||
(strpos($Temp, "s") >= 0) || (strpos($Temp, "c") >= 0) || (strpos($Temp,
"t") >= 0) || (strpos($Temp, "a") >= 0)) {
if ((strpos($Temp, "[") === false) && (strpos($Temp, "(") === false) &&
(strpos($Temp, "s") === false) && (strpos($Temp, "c") === false) &&
(strpos($Temp, "t") === false) && (strpos($Temp, "a") === false)) { } else {
echo "mdlParse: Specified Text Sequence Not Allowed";
return false;
}
break;
} //end switch $X
//Ok:
} //end switch $Exp{$X}
} //end for
return true;
}



function Mul($Exp) {
//Dim Place As Integer //'Where the sign is found
//Dim X As Integer //'Travel backward and forward on the string
//Dim Y As Integer //'Counter
//Dim Before As String //'What number goes before the sign
//Dim After As String //'What number goes after the sign
//Dim NewExp As String
//Dim BeforeStr As String //'Used to save the text that goes before our
calculation
$Exp = RemPlusMinus($Exp); //'Make sure previous calculation didn't leave
+- or --
$M = $Exp; //'Incase there's nothing to do, make sure the
function returns correct information
$Place = strpos($Exp, "*");
if ($Place === false) { } else {
$Y = 0;
$X = $Place;
do {//'loop backwards on the string to find out the number before the sign
$Y++;
$X--;
if ($X < 0) break; //'incase we got to the start of the string
if (($Exp{$X} != "+") && ($Exp{$X} != "-" || $Y == 1) && ($Exp{$X} !=
"/")) {
$Before = substr($Exp, $X, $Y);
}
} while (!(($Exp{$X} == "+") || ($Exp{$X} == "-" && $Y != 1) || ($Exp{$X}
== "/"))); //'As Long as we didn't get to the start or to another sign -
continue traveling backwards.
$BeforeStr = substr($Exp, 0, $X+1);
$Y = 0;
$X = $Place;
do {
$Y++;
$X++;
if (($Exp{$X} != "+") && ($Exp{$X} != "-" || $Y == 1) && ($Exp{$X} !=
"*") && ($Exp{$X} != "/")) {
$After = substr($Exp, $Place + 1, $Y+1); //+1 or -1? I think it's +1.
}
} while (!(($Exp{$X} == "+") || ($Exp{$X} == "-" && $Y != 1) || ($Exp{$X}
== "*") || ($Exp{$X} == "/") || ($X >= strlen($Exp)-1))); //'As long as we
didn't get to the end or to another sign - continue traveling forward.
$NewExp = $BeforeStr . ((double)$Before * (double)$After) . substr($Exp,
$X+1);
if (strpos($NewExp, "*") === false) { } else {
$NewExp = Mul($NewExp); //'Recurse incase there are *'s left
}
return $NewExp;
} //end if
return $M;
}

function Add($Exp) {
$Exp = RemPlusMinus($Exp);
$A = $Exp;
$Place = strpos($Exp, "+");
if ($Place === false) { } else {
$Y = 0;
$X = $Place;
do { //'loop backwards on the string to find out the number before the
sign
$Y++;
$X--;
if ($X < 0) break; //'incase we got to the start of the string
if (($Exp{$X} != "-" || $X == 0) && ($Exp{$X} != "*") && ($Exp{$X} !=
"/")) {
$Before = substr($Exp, $X, $Y);
}
} while (!(($Exp{$X} == "*") Or ($Exp{$X} == "-" && $X != 0) || ($Exp{$X}
== "/")));
$BeforeStr = substr($Exp, 0, $X+1);
$Y = 0;
$X = $Place;
do {
$Y++;
$X++;
if (($Exp{$X} != "+") && ($Exp{$X} != "-") && ($Exp{$X} != "*") &&
($Exp{$X} != "/")) {
$After = substr($Exp, $Place + 1, $Y);
}
} while (($Exp{$X} != "+") && ($Exp{$X} != "-") && ($Exp{$X} != "*") &&
($Exp{$X} != "/") && ($X < strlen($Exp)-1));
$NewExp = $BeforeStr . ((double)$Before + (double)$After) . substr($Exp,
$X+1);
if (strpos($NewExp, "+") === false) { } else {
$NewExp = Add($NewExp); //'Recurse incase there are +'s left
}
return $NewExp;
} //end if
return $A;
}

function Subt($Exp) {
$Exp = RemPlusMinus($Exp);
$S = $Exp;
$Place = strpos($Exp, "-");
if ($Place === 0) $Place = strpos($Exp, "-", 1);
if ($Place > 0) {
$Y = 0;
$X = $Place;
do { //'loop backwards on the string to find out the number before the
sign
$Y++;
$X--;
if ($X < 0) break; //'incase we got to the start of the string
if (($Exp{$X} != "+") && ($Exp{$X} != "*") && ($Exp{$X} != "/")) {
$Before = substr($Exp, $X, $Y);
}
} while (!(($Exp{$X} == "*") || ($Exp{$X} == "+") || ($Exp{$X} == "/")));
$BeforeStr = substr($Exp, 0, $X+1); //+1 added afterwards by Jim
$Y = 0;
$X = $Place;
do {
$Y++;
$X++;
if (($Exp{$X} != "+") && ($Exp{$X} != "-") && ($Exp{$X} != "*") &&
($Exp{$X} != "/")) {
$After = substr($Exp, $Place + 1, $Y+1);
}
} while (!(($Exp{$X} == "+") || ($Exp{$X} == "-") || ($Exp{$X} == "*") ||
($Exp{$X} = "/") || ($X >= strlen(Exp)-1)));
$NewExp = $BeforeStr . ((double)$Before - (double)$After) . substr($Exp,
$X+1);
if (strpos($NewExp, "-") === false) { } else {
$NewExp = Subt($NewExp); //'Recurse incase there are -'s left
}
return $NewExp;
}
return $S;
}

function Parentheses($Exp) {
$Exp = RemPlusMinus($Exp);
$P = $Exp;
$MiddleStr="";
$Place = strpos($Exp, ")");
if ($Place === false) { } else {
$Y = 0;
$X = $Place;
do { //'loop to find the inside of the parentheses
$Y++;
$X--;
if ($X < 0) break; //'incase we got to the start of the string
if ($Exp{$X} != "(") {
$MiddleStr = substr($Exp, $X, $Y);
}
} while ($Exp{$X} != "(");
$BeforeStr = ""; //'if it's the beginning of the string - there is nothing
before.
if ($X >= 0) $BeforeStr = substr($Exp, 0, ($X - 1)+1);
$NewExp = $BeforeStr . Parse($MiddleStr) . substr($Exp, $Place + 1);
if (strpos($Exp, ")") === false) { } else {
$NewExp = Parentheses($NewExp); //'Recurse incase there are more
parentheses
}
return $NewExp;
}
return $P;
}

function Div($Exp) {
//On Error Resume Next
$Exp = RemPlusMinus($Exp);
$D = $Exp;
$Place = strpos($Exp, "/");
if ($Place === false) { } else {
$Y = 0;
$X = $Place;
do {//'loop backwards on the string to find out the number before the sign
$Y++;
$X--;
if ($X < 0) break; //'incase we got to the start of the string
if (($Exp{$X} != "+") && ($Exp{$X} && "-" || $Y == 1) && ($Exp{$X} !=
"*")) {
$Before = substr($Exp, $X, $Y);
}
} while (!(($Exp{$X} == "+") || ($Exp{$X} == "-" && $Y != 1) || ($Exp{$X}
== "*")));
$BeforeStr = substr($Exp, 0, $X+1);
$Y = 0;
$X = $Place;
do {
$Y++;
$X++;
if (($Exp{$X} != "+") && ($Exp{$X} != "-" Or Y == 1) && ($Exp{$X} != "*")
&& ($Exp{$X} != "/")) {
$After = substr($Exp, $Place + 1, $Y+1);
}
} while (!(($Exp{$X} == "+") || ($Exp{$X} == "-" && $Y != 1) || ($Exp{$X}
== "*") || ($Exp{$X} == "/") || ($X >= strlen($Exp)-1)));
if ((double)$After==0.0) {
return "Impossible";
}
$NewExp = $BeforeStr . ((double)$Before / (double)$After) . substr($Exp,
$X+1);
if (strpos($NewExp, "/") === false) { } else {
$NewExp = Div($NewExp); //'Recurse incase there are /'s left
}
return $NewExp;
}
return $D;
}

function Power($Exp) {
$Exp = RemPlusMinus($Exp);
$P = $Exp;
$Place = strpos($Exp, "^");
if ($Place === false) { } else {
$Y = 0;
$X = $Place;
do { //'loop backwards on the string to find out the number before the
sign
$Y++;
$X--;
if ($X < 0) break; //'incase we got to the start of the string
if (($Exp{$X} != "+") && ($Exp{$X} != "-" || X == 0) && ($Exp{$X} != "*")
&& ($Exp{$X} != "/")) {
$Before = substr($Exp, $X, $Y);
}
} while (!(($Exp{$X} == "+") || ($Exp{$X} == "-" && X != 0) || ($Exp{$X}
== "*") || ($Exp{$X} == "/")));
$BeforeStr = substr($Exp, 0, $X+1);
$Y = 0;
$X = $Place;
do {
$Y++;
$X++;
if (($Exp{$X} != "+") && ($Exp{$X} != "-") && ($Exp{$X} != "*") &&
($Exp{$X} != "/")) {
$After = substr($Exp, $Place + 1, $Y+1);
}
} while (!(($Exp{$X} == "+") || ($Exp{$X} == "-") || ($Exp{$X} == "*") ||
($Exp{$X} == "/") || ($X >= strlen($Exp)-1)));
$NewExp = $BeforeStr . bcpow($Before, $After) . substr($Exp, $X+1);
if (strpos($NewExp, "^") === false) { } else {
$NewExp = Power($NewExp); //`'Recurse incase there are ^'s left
}
return $NewExp;
}
return $P;
}

function Abso($Exp) {
$A = $Exp;
$First = strpos($Exp, "|");
if ($First === false) return NULL;
$Second = strpos($Exp, "|", $First + 1);
if ($Second === false) return NULL;
$NewExp = substr($Exp, $First + 1, $Second - $First - 1);
$NewExp = Parse($NewExp);
return substr($Exp, 0, ($First - 1)+1) . abs((double)$NewExp) .
substr($Exp, $Second + 1);
}

function CSTA($Exp) {
//Dim X As Integer 'CSTA = Cos Sin Tan Atn
$MiddleStr="";
$Exp = RemPlusMinus($Exp);
$C = $Exp;
$Place = strpos($Exp, "]");
if ($Place === false) { } else {
$Y = 0;
$X = $Place;
do { //'loop to find the inside of the brackets
$Y++;
$X--;
if ($X < 0) break; //'incase we got to the start of the string
if ($Exp{$X} != "[") {
$MiddleStr = substr($Exp, $X, $Y);
}
} while ($Exp{$X} != "[");
$BeforeStr = ""; //'if it's the beginning of the string - there is nothing
before.
if ($X > 0) $BeforeStr = substr($Exp, 0, ($X - 4)+1);
if ($X > 0) $XType = substr($Exp, $X - 3, 3);
switch($XType) {
case "cos":
$NewExp = $BeforeStr . cos((double)Parse($MiddleStr)) . substr($Exp,
$Place + 1);
break;
case "sin":
$NewExp = $BeforeStr . sin((double)Parse($MiddleStr)) . substr($Exp,
$Place + 1);
break;
case "tan":
$NewExp = $BeforeStr . tan((double)Parse($MiddleStr)) . substr($Exp,
$Place + 1);
break;
case "atn":
$NewExp = $BeforeStr . atan((double)Parse($MiddleStr)) . substr($Exp,
$Place + 1);
break;
}
if (strpos($Exp, "]") === false) { } else {
$NewExp = CSTA($NewExp); // 'Recurse incase there are more brackets
}
return $NewExp;
}
return $C;
}


function Parse($Exp) {
/*'**************************************************************************************** '*This is the main function. In Mul function you will find detailedexplanation. No need* '*to explain others because they are pretty much the same but with littlechanges. * '****************************************************************************************/ $Exp = RemSpaces($Exp); //'Remove Spaces $Exp = RemPlusMinus($Exp); //'Change +- to - and -- to + if (IsValid($Exp)) { //'Invalid equations trapping //'The if's are to make sure the code is run only if neccessary if (strpos($Exp, "[") === false) {} else { $Exp = CSTA($Exp);}//'Eliminate Cos/Sin/Tan/Atn from equation. if (strpos($Exp, "|") === false) { } else { $Exp = Abso($Exp);}//'Eliminate Absolute signs from equation. if (strpos($Exp, "(") === false) {} else { $Exp = Parentheses($Exp);}//'Eliminate Parentheses from equation. if (strpos($Exp, "^") === false) {} else { $Exp = Power($Exp);}//'Eliminate Exponentials from equation. if (strpos($Exp, "*") === false) {} else { $Exp = Mul($Exp);}//'Eliminate Multiplications from equation. if (strpos($Exp, "/")=== false) {} else { $Exp = Div($Exp);}//'Eliminate Divisions from equation. if ($Exp == "Impossible") { //'if Division byZero echo "Division by 0 error\n";//'let the calling procedure know what happened return NULL; } if (strpos($Exp, "+") === false) { } else { $Exp = Add($Exp);}//'Eliminate Addition from equation. if (strpos($Exp, "-", 1) === false) { } else { $Exp = Subt($Exp);}//'Eliminate Subtraction from equation. (why 1?) return (double)$Exp; } else { return NULL; }}//print Parse("|-2|-(4-2)*sin[-3.14]"); //chokes on this in the subtractionarea.//print Parse("2-2-2");//print Parse("|-2|-(4-2)");//print Parse("(4-2)*sin[-3.14]");?>>>>> There is bc under Unix. Although inserting user-supplied data into a>>> shell command might be even more dangerous.>> My example (below) looks safe. It could be made tighter by checking for> long lines, disabling single quotes and "strange" characters (ESC, NUL,> ...), and whatever else you might think of.>> ... but bc is, perhaps, best discussed on gnu.utils.help.>>> That is also an interesting approach, but I do feel that I would like>> to keep it a native php solution.>>> <?php> $formula = <<<FORMULA> /*> * find the hypotenuse of a right triangle> * with height = 5 and area = 25> */>> /* !!!! ATTENTION !!! */> rm -rf * ## I tried it!!> ## it seems perfectly safe (???)> /* !!! ATTENTION !!! */>> scale = 8 ## work with 8 decimals>> /* area = h * w / 2 */> h = 5 ## height> area = 25 ## area> w = 2*area / h ## compute width>> /* hypotenuse = sqrt(h*h + w*w) */> hyp = sqrt(h*h + w*w) ## compute hypotenuse> hyp ## print (and return) it> FORMULA;>> $value = escapeshellarg($formula);> $calculated = `echo $value | bc`; /* backticks! */>> // with "rm" in the formula, $calculated will have the error message> // from bc; it might be possible to remove all messages with a> // simple regular expression, I didn't look into that.> echo $calculated;> ?>>> --> If you're posting through Google read <http://cfaj.freeshell.org/google>

[Back to original message]


Удаленная работа для программистов  •  Как заработать на Google AdSense  •  England, UK  •  статьи на английском  •  PHP MySQL CMS Apache Oscommerce  •  Online Business Knowledge Base  •  DVD MP3 AVI MP4 players codecs conversion help
Home  •  Search  •  Site Map  •  Set as Homepage  •  Add to Favourites

Copyright © 2005-2006 Powered by Custom PHP Programming

Сайт изготовлен в Студии Валентина Петручека
изготовление и поддержка веб-сайтов, разработка программного обеспечения, поисковая оптимизация