PHP果然容易上手,今天從零開始學了半天能感覺已經能寫點簡單的東西了。。。
0 起因:
公司裡做一個PHP外掛程式,經常需要更新。雖然現在已經有php寫的update的程式,但由於使用者的ftp寫入權限限制等的原因,無法及時地更新外掛程式。
因此考慮如下流程:
將代碼以二進位的形式存到MySQL資料庫(不是存在緩衝裡)
-》每次載入的時候就從資料庫裡讀取指令碼代碼檔案
-》由index.php來以類似Eval('code')的方式執行動態讀取的代碼
這樣以後每次只需要更新MySQL資料庫裡的代碼就可以了。
1 可能遇到的技術問題:
開始研究之前我自己先設想了這麼幾個問題:
1. PHP與HTML混雜的代碼是否可以成功跑通
http://stackoverflow.com/questions/2520344/php-eval-issue-with-php-html-code
2. 對於包含PHP include和PHP require的,是否可行
需要考慮include once
3. 有哪些特殊字元是需要轉義的
4. 如果有return的話是否可能出問題
5. 是否執行許可權上會有差異。
6. 如果拋出異常的話,是否能正確處理
如果還可能有什麼技術要點我沒有列舉的話請和我說一下。
2
附件範例
根據如上的問題,我做了如下的7個範例
經過測試,證明了PHP Eval可以實現:
2.1
運行普通string
例如echo(“aaa”);
2.2
運行帶<? php開頭的字串
例如<? php echo(“aaa”);
2.3
運行<? Php開頭,?>結尾的字串
2.4
運行HTML與PHP混合的字串
2.5
從MySQL讀取代碼(字串形式),並動態載入
2.6
運行包含include和request的代碼
2.7
知道啟動並執行過程是否出現異常
但無法捕捉到詳細異常資訊
附上測試範例代碼:
Eval範例
<?php//<editor-fold defaultstate="collapsed" desc="user-description">//</editor-fold>//<editor-fold desc="Sample 1: simplest php code">$sample1="echo(\"sample 1\");echo(\"\n<br />\");";//</editor-fold>//<editor-fold desc="Sample 2: PHP code with head">$sample2="<?php echo(\"sample 2\");echo(\"\n<br />\");?>";//</editor-fold>//<editor-fold desc="Sample 3: PHP code with head and no end">$sample3="<?php echo(\"sample 3\");echo(\"\n<br />\");";//</editor-fold>//<editor-fold desc="Sample 4: PHP code combined massively with HTML">$sample4="<div>HTML Start<br /> <?php echo(\"sample 3\");echo(\"\n<br />\"); ?> HTMLEnd<br />";//</editor-fold>//<editor-fold desc="Sample 5: Mimic read PHP code from MySQL database">$phpfiles1 = [ "phpfile1" => "<?php echo(\"I'm php file 1\");echo(\"\n<br />\");?>", "folder1\\phpfile2" => "<?php echo(\"I'm php file 2\");echo(\"\n<br />\");?>",];//</editor-fold>//<editor-fold desc="Sample 6: PHP Code with include">$phpfiles2 = [ "phpfile1" => "<?php include 'phpfile2' echo(\"I'm php file 1\");echo(\"\n<br />\"); ?>", "phpfile2" => "<?php echo(\"I'm php file 2\");echo(\"\n<br />\");?>",];//</editor-fold>//<editor-fold desc="Sample 7: Exception Handling">$sample7="<?php echo(\"sample 2\")echo(\"\n<br />\");?>";//</editor-fold>///////////////////////////////////////////////////////////////////////////////////////<editor-fold desc="eval comment">echo("First of all, before codes are stored into databases, it requires replacing escape character<br />");echo("replace \" with \\\"<br />");echo("replace \\ with \\\\<br />");echo("and other escape character<br />");echo("<br />");//</editor-fold>//<editor-fold desc="eval sample1">echo("Sample 1: simplest php code<br />");eval($sample1);echo("<br />==================<br />");//</editor-fold>//<editor-fold desc="eval sample2">echo("Sample 2: PHP code with < ?php head");echo("<br />");eval('?> ' .$sample2. ' <?php ');echo("<br />==================<br />");//</editor-fold>//<editor-fold desc="eval sample3">echo("Sample 3: PHP code with < ?php head and no ? >end");echo("<br />");eval('?> ' .$sample3);echo("<br />==================<br />");//</editor-fold>//<editor-fold desc="eval sample4">echo("Sample 4: PHP code combined massively with HTML");eval('?> ' .$sample4. ' <?php ');echo("<br />==================<br />");//</editor-fold>//<editor-fold desc="eval sample5">echo("Sample 5: Mimic read PHP code from MySQL database");echo("<br />");$sample5_1=$phpfiles1["phpfile1"];eval('?> ' .$sample5_1. ' <?php ');$sample5_2=$phpfiles1["folder1\\phpfile2"];eval('?> ' .$sample5_2. ' <?php ');echo("<br />==================<br />");//</editor-fold>//<editor-fold desc="eval sample6">echo("Sample 6: PHP Code with include");echo("<br />");$sample6 = $phpfiles2["phpfile1"];$sample6_includefile;$sample6_include_1stquote_pos;$sample6_include_2ndquote_pos;if (strpos($sample6, "'") !== FALSE) { global $sample6_includefile; global $sample6_include_1stquote_pos; global $sample6_include_2ndquote_pos; $sample6_include_1stquote_pos = strpos($sample6, "'"); $sample6_include_2ndquote_pos = strpos($sample6, "'", strpos($sample6, "'") + 1); $sample6_includefile = substr($sample6 , $sample6_include_1stquote_pos + 1 , $sample6_include_2ndquote_pos - $sample6_include_1stquote_pos - 1 );}echo("Included file: " . $sample6_includefile . "<br />");$sample6_2 = $phpfiles2[$sample6_includefile];if (substr($sample6_2, 0, 2) == "<?") { global $sample6_2; $sample6_2 = substr($sample6_2, 6);}if (substr($sample6_2, strlen($sample6_2) - 2, 2) == "?>") { global $sample6_2; $sample6_2 = substr($sample6_2, 0, strlen($sample6_2) - 2);}$sample6 = \str_replace("include '" . $sample6_includefile . "' ", $sample6_2, $sample6);eval('?> ' . $sample6 . ' <?php ');echo("Should use some logic here, to judge if a include file is include or request more than once.<br />");echo("If the php file included starts with < ?php and ends with ? >, < ?php and ? > should be removed.<br />");echo("<br />==================<br />");//</editor-fold>//<editor-fold desc="Sample 7: Exception Handling">$evalresult=eval($sample7);if($evalresult!==null){ echo("Exception occurs");}echo("<br />");echo("No further exception message can be captured.");//</editor-fold>
3
注意點
1. 安全性
http://php.net/manual/en/function.eval.php
If you have
carefully verified that there is no other option than to use this construct,
pay special attention not to pass any user provided data into
it without properly validating it beforehand.
根據PHP官方網站的提示,需要注意不要在驗證安全性之前,將任何使用者提供的代碼放到eval的部分裡。不然就有風險可能會執行惡意代碼。
2. 結尾分號
由於代碼最後可能會漏掉分號,可能需要在執行完一段後以防萬一補上一個分號。