在乌云漏洞平台上发现很多开源PHP程序存在漏洞。大部分漏洞都是因为对PHP中的变量过滤不够严格和未转义造成,直接使用了$_GET,$_PUT,$_COOKIE传过来的值,造成了sql注入(sql injection)和跨站攻击(xss)。事实上这些漏洞完全可以避免的。
先说sql注入。通常注入情况发生在参数过滤不严谨和拼接sql,所以尽量不要用sql拼接,尽量使用PDO或Mysqli。先看下面一段代码(test.php):
$database="test"; $duser="root"; $dpassword="*********"; $server="localhost"; $link=mysql_connect($server,$duser,$dpassword) or die("连接不上服务器:".mysql_error()); mysql_select_db($database,$link) or die("不能选择数据库"); $id=$_GET['id']; $sql="select id,account,nickname,password from user where id='".$id."'"; echo $sql; $res=mysql_query($sql); $arr=mysql_fetch_array($res,MYSQL_ASSOC);
然后访问http://127.0.0.1/test.php?id=1,获取用户信息。这是可以通过构造参数进行攻击比如
//访问:http://127.0.0.1/test.php?id=1%27%20OR%201%20=%201--%27 //将可以查询出所有的表数据。 $sql="select id,account,nickname,password from user where id='1' OR 1 = 1--''"; //访问:http://127.0.0.1/test.php?id=1%27%20UNION%20ALL%20%28SELECT%20NULL,NULL,NULL,%27%3C?php%20system%28$_GET[%22command%22]%29;%20?%3E%27%20INTO%20OUTFILE%20%27D:/www/shell.php%27%29%20--%20-%27 //在知道网站路径情况下,在网站目录下生成shell.php,将能够在对用户的网站进行挂马,提权的操作。 $sql="select id,account,nickname,password from user where id='1' UNION ALL (SELECT NULL,NULL,NULL,'' INTO OUTFILE 'D:/www/shell.php') -- -''";
甚至还可以进行表数据的增加,修改,删除,数据库的删除!所以永远不要相信用户的输入,永远要过滤输入,转义输出。对传递过来的数字型变量要进行严格的匹配,对字符串型的变量进行长度限制。注意过滤用户传递过来的变量是否包含引号,/*,#,—等特殊字符或转义字符,script,drop,不要给用户超级权限,最好进行分隔。
//使用addslashes过滤单引号,双引号和斜杠 $uname = addslashes( $_GET['id'] ); $query = 'SELECT username FROM users WHERE id = "' . $uname . '"; //使用mysql_real_escape_string过滤特殊字符,比如\x00, \n, \r, \, ', ",\x1a //mysql_real_escape_string使用的是本地单字节字符集,而我们传递多字节编码的变量时,有可能还是会造成SQL注入漏洞 //如chr(0xbf) . chr(0x27),所以mysql服务端与php客户端通信尽量使用utf-8而不是gbk $uname = mysql_real_escape_string( $_GET['id'] ); $query = 'SELECT username FROM users WHERE id = "' . $uname . '"; //使用is_numeric检查数字型变量 $id = $_GET['id']; ( is_numeric( $id ) ? TRUE : FALSE ); //使用sprintf格式化数字型变量 $id = $_GET['id']; $query = sprintf("SELECT username FROM users WHERE id = '%d' ", $id); //使用htmlentities($var, ENT_QUOTES)转义所有的html标记 $id = $_GET['id']; $id = htmlentities( $id, ENT_QUOTES, 'UTF-8' ); $query = 'SELECT username FROM users WHERE id = "' . $id . '"'; //使用filter_var过滤script标签,防止xss //$str = strip_tags($input); $str = filter_var($input, FILTER_SANITIZE_STRING);
如果PHP支持PDO可以的话,就不要拼接sql,尽量使用PDO或Mysqli。使用PDO要在PDO的DSN中指定charset属性,要设置PDO::ATTR_EMULATE_PREPARES参数为false,禁止php进行本地转义,造成双字节转义攻击,如果没有必要,msyql服务端与php客户端应该都是用utf-8进行通信,而不是gbk。
$pdo = new PDO("mysql:host=192.168.0.1;dbname=test;","root"); $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $st = $pdo->prepare("select * from info where id =? and name = ?"); $id = 21; $name = 'zhangsan'; $st->bindParam(1,$id); $st->bindParam(2,$name); $st->execute(); $st->fetchAll();
除了过滤,变量务必初始化,不要自动获得变量,用户将可以构造任何变量进行攻击。
不要这样随意,将造成文件包含攻击
<?php if($_GET['page']) {//if(file_exists($_GET['page'])) {非严格的检测 http://localhost/index.php?page=/etc/passwd%00 include($_GET['page']); } ?>
要进行严格的匹配,或者做映射
<?php if($_GET['page'] == 'news') { include('news.php'); } ?>
关闭浏览器输出的错误报告,避免泄漏文件目录路径,错误报告应输出到日记文件中
#php.ini display_errors = 'off' #apache2.conf php_flag display_errors off
php当中
ini_set('display_errors', false);
页面的访问要设置权限,特别是执行查询的或者录入的页面,简单设置如下
define('APP_ADMIN', '1'); if(!defined('APP_ADMIN')) exit;
应该做一个更为完善的权限系统,如基于角色的权限管理系统,最好细致到页面(元素)。
eval函数针对php安全有很大杀伤力,为了防止挂马,应禁用eval函数。
下载的页面必须设置下载目录,对要下载的文件进行检测或做目录权限控制,生成临时下载链接,不允许下载任意的文件。对于系统配置文件,日记文件等要做分离,建议像zend framework那样设置web目录,程序与web目录分开,避免泄漏源码等。不要将.svn文件直接放置在web目录,会导致源码泄露。
上传的页面必须进行类型检测,不要只截取扩展名,设置权限,不允许可执行文件。这里是网友写的一个文件类型检查类
/*通过文件名,获得文件类型* *@author chengmo QQ:8292669* *@copyright <a href="http://www.cnblogs.com/chengmo">http://www.cnblogs.com/chengmo</a> 2010-10-17 *@version 0.1 *$filename="d:/1.png";echo cFileTypeCheck::getFileType($filename); 打印:png */ class cFileTypeCheck { private static $_TypeList=array(); private static $CheckClass=null; private function __construct($filename) { self::$_TypeList=$this->getTypeList(); } /** *处理文件类型映射关系表* * * @param string $filename 文件类型 * @return string 文件类型,没有找到返回:other */ private function _getFileType($filename) { $filetype="other"; if(!file_exists($filename)) throw new Exception("no found file!"); $file = @fopen($filename,"rb"); if(!$file) throw new Exception("file refuse!"); $bin = fread($file, 15); //只读15字节 各个不同文件类型,头信息不一样。 fclose($file); $typelist=self::$_TypeList; foreach ($typelist as $v) { $blen=strlen(pack("H*",$v[0])); //得到文件头标记字节数 $tbin=substr($bin,0,intval($blen)); ///需要比较文件头长度 if(strtolower($v[0])==strtolower(array_shift(unpack("H*",$tbin)))) { return $v[1]; } } return $filetype; } /** *得到文件头与文件类型映射表* * * @return array array(array('key',value)...) */ public function getTypeList() { return array(array("FFD8FFE1","jpg"), array("89504E47","png"), array("47494638","gif"), array("49492A00","tif"), array("424D","bmp"), array("41433130","dwg"), array("38425053","psd"), array("7B5C727466","rtf"), array("3C3F786D6C","xml"), array("68746D6C3E","html"), array("44656C69766572792D646174","eml"), array("CFAD12FEC5FD746F","dbx"), array("2142444E","pst"), array("D0CF11E0","xls/doc"), array("5374616E64617264204A","mdb"), array("FF575043","wpd"), array("252150532D41646F6265","eps/ps"), array("255044462D312E","pdf"), array("E3828596","pwl"), array("504B0304","zip"), array("52617221","rar"), array("57415645","wav"), array("41564920","avi"), array("2E7261FD","ram"), array("2E524D46","rm"), array("000001BA","mpg"), array("000001B3","mpg"), array("6D6F6F76","mov"), array("3026B2758E66CF11","asf"), array("4D546864","mid")); } public static function getFileType($filename) { if(!self::$CheckClass) self::$CheckClass=new self($filename); $class=self::$CheckClass; return $class->_getFileType($filename); } } $filename="22.jpg"; echo $filename,"t",cFileTypeCheck::getFileType($filename),"rn"; $filename="11.doc"; echo $filename,"t",cFileTypeCheck::getFileType($filename),"rn";
对于暴力破解设置验证码,或者限制ip次数。
参考链接:
SQL Injection Attacks by Example
SQL Injection
Injection
PHP SQL Injection
PHP+MySQL环境下SQL Injection攻防总结
PDO防注入原理分析以及使用PDO的注意事项
SQL injection that gets around mysql_real_escape_string()
MySQL远程提权php版
XSS Filter Evasion Cheat Sheet
php通过文件头检测文件类型通用类(zip,rar…)
php 验证码【倾斜,正弦干扰线,黏贴,旋转