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 知道运行的过程是否出现异常
但无法捕捉到详细异常信息
附上测试范例代码:
<?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. 结尾分号
由于代码最后可能会漏掉分号,可能需要在执行完一段后以防万一补上一个分号。