在乌云漏洞平台上发现很多开源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 验证码【倾斜,正弦干扰线,黏贴,旋转