|
2楼
楼主 |
发表于 2011-3-20 21:38:41
|
只看该作者
PHP编程要注意的安全问题,防注入问题。(应该反复学习,很有用!)
内容总结来源于互联网博客,PHP、MYSQL手册等。恕不一一注明来源。
//几个有用的php字符串处理函数
1 <?php
2
3 nl2br(); // \n to <br/>
4 addslashes(); stripslashes(); //对数据库操作时,转义特殊字符
5
6 chop(); //除去字符串右边空格
7 trim(); //除去字符串中所有空格
8 ltrim(); //除去字符串左边空格
9
10 htmlspecialchars(); //转换'$','"','<','>'为相应的html实体
11 htmlentities(); //转换所有html标记为相应的html实体
12 //两个函数在格式化带有英文字符的html代码的时候基本没啥问题,但是htmlentities对中文字符也不放过,这样得出来的结果是中文字符部分变为一堆乱码。
13 array explode(string separator, string str); //分割字符串
14 string implode(string separator, array arr); //连接字符串
15
16 strtoupper(); strtolower(); //转换大小写
17 ucfirst(); //只转换第一个字符为大写
18 ucwords(); //转换每个words的第一个字母为大写
19 mysql_real_escape_string()//本函数将 unescaped_string 中的特殊字符转义,并计及连接的当前字符集,因此可以安全用于 mysql_query()。 注: mysql_real_escape_string() 并不转义 % 和 _。
20 ?>
//防止sql注入
在PHP编码的时候,一些比较基本的安全问题
1. 注意初始化你的变量
2.防止SQL Injection (sql注射)
我们知道Web上提交数据有两种方式,一种是get、一种是post,那么很多常见的sql注射就是从get方式入手的,而且注射的语句里面一定是包含一些sql语句的,因为没有sql语句,那么如何进行,sql语句有四大句:select 、update、delete、insert,那么我们如果在我们提交的数据中进行过滤是不是能够避免这些问题呢?
于是我们使用正则就构建如下函数:
(1)把 select,insert,update,delete, union, into, load_file, outfile /*, ./ , ../ , ' 等等危险的参数字符串全部过滤掉,处理掉?id=1' select * from ***的情况
<?php
/*
函数名称:inject_check()
函数作用:检测提交的值是不是含有SQL注射的字符,防止注射,保护服务器安全
参 数:$sql_str: 提交的变量
返 回 值:返回检测结果,ture or false
函数作者:heiyeluren
*/
function inject_check($sql_str) {
return eregi('select|insert|update|delete|'|/*|*|../|./|union|into|load_file|outfile', $sql_str); // 进行过滤
}
?>
(2)处理掉a.php?id=1asdfasdfasdf
<?php
/*
函数名称:verify_id()
函数作用:校验提交的ID类值是否合法
参 数:$id: 提交的ID值
返 回 值:返回处理后的ID
函数作者:heiyeluren
*/
function verify_id($id=null) {
if (!$id) { exit('没有提交参数!'); } // 是否为空判断
elseif (inject_check($id)) { exit('提交的参数非法!'); } // 注射判断
elseif (!is_numeric($id)) { exit('提交的参数非法!'); } // 数字判断
$id = intval($id); // 整型化
return $id;
}
?>
(3)去除' _ ', ' % ',这些字符特殊意义字符
<?php
/*
函数名称:str_check()
函数作用:对提交的字符串进行过滤
参 数:$var: 要处理的字符串
返 回 值:返回过滤后的字符串
函数作者:heiyeluren
*/
function str_check( $str ) {
if (!get_magic_quotes_gpc()) // 判断magic_quotes_gpc是否打开
{
$str = addslashes($str); // 进行过滤
}
$str = str_replace("_", "\_", $str); // 把 '_'过滤掉
$str = str_replace("%", "\%", $str); // 把' % '过滤掉
return $str;
}
?>
(4)对编辑内容进行过滤和转换
<?php
/*
函数名称:post_check()
函数作用:对提交的编辑内容进行处理
参 数:$post: 要提交的内容
返 回 值:$post: 返回过滤后的内容
函数作者:heiyeluren
*/
function post_check($post) {
if (!get_magic_quotes_gpc()) // 判断magic_quotes_gpc是否为打开
{
$post = addslashes($post); // 进行magic_quotes_gpc没有打开的情况对提交数据的过滤
}
$post = str_replace("_", "\_", $post); // 把 '_'过滤掉
$post = str_replace("%", "\%", $post); // 把' % '过滤掉
$post = nl2br($post); // 回车转换
$post= htmlspecialchars($post); // html标记转换
return $post;
}
?>
综合来说即2个,1. 初始化你的变量 2. 一定记得要过滤你的变量
//服务器端用 mysql_real_escape_string 清洁客户端数据
在服务器端清洁客户端数据是每个程序员经常要做的工作,虽然我们通常会在客户端添加 Javascript 验证,但是,恶意用户很容易自己构造 FORM 提交数据以绕过客户端验证,另外,在客户端禁用 Javascript 时验证同样不能起到作用。因此,服务器端清洁数据必不可少,本文介绍的是用 mysql_real_escape_string 清洁数据的方法,经过清洁的数据可以直接插入到数据库中。
由于 mysql_real_escape_string 需要 MySQL 数据库连接,因此,在调用 mysql_real_escape_string 之前,必须连接上 MySQL 数据库。
PHP:
1 <?php
2 // 说明:用 array_map() 调用 mysql_real_escape_string 清理数组
3 // 整理:http://www.codebit.cn
4 function mysqlClean($data)
5 {
6 return (is_array($data))?array_map('mysqlClean', $data):mysql_real_escape_string($data);
7 }
8 ?>
调用方法
PHP:
1 <?php
2 $conn = mysql_connect('localhost', 'user', 'pass');
3
4
5 $_POST = mysqlClean($_POST);
6 ?>
经过清洁的数据可以直接插入数据库。
注意!mysql_real_escape_string 必须在(PHP 4 >= 4.3.0, PHP 5)的情况下才能使用。否则只能用 mysql_escape_string ,两者的区别是:
mysql_real_escape_string 考虑到连接的当前字符集,而mysql_escape_string 不考虑。
由于 mysql_real_escape_string 需要 MySQL 数据库连接,因此,在调用 mysql_real_escape_string 之前,必须连接上 MySQL 数据库。
在知道数据类型为字符串时,我们可以在清洁数据的同时限制字符串长度。此方法来自 David Lane, Hugh E. Williams《Web Database Application with PHP and MySQL 》(O'Reilly, May 2004)
PHP:
1 <?php
2 // 说明:用 mysql_real_escape_string 清洁并限制字符长度
3 // 整理:http://www.codebit.cn
4 function mysqlClean($array, $index, $maxlength)
5 {
6 if (isset($array[$index]))
7 {
8 $input = substr($array["{$index}"], 0, $maxlength);
9 $input = mysql_real_escape_string($input);
10 return ($input);
11 }
12 return NULL;
13 }
14 ?>
调用方法:
PHP:
1 <?php
2 $conn = mysql_connect('localhost', 'user', 'pass');
3
4 if(isset($_POST['username']))
5 {
6 $_POST['username'] = mysqlClean($_POST, 'username', 20);
7 echo $_POST['username'];
8 }
9 ?>
将 $_POST 数组中的 'username' 清洁并截取前20位字符。
//关于sql注入
SQL 注入攻击是黑客攻击网站最常用的手段。如果你的站点没有使用严格的用户输入检验,那么常容易遭到SQL注入攻击。SQL注入攻击通常通过给站点数据库提交不良的数据或查询语句来实现,很可能使数据库中的纪录遭到暴露,更改或被删除。下面来谈谈SQL注入攻击是如何实现的,又如何防范。
看这个例子:
// supposed input
$name = “ilia’; Delete FROM users;”;
mysql_query(“Select * FROM users Where name=’{$name}’”);
很明显最后数据库执行的命令是:
Select * FROM users Where name=ilia; Delete FROM users
这就给数据库带来了灾难性的后果--所有记录都被删除了。不过如果你使用的数据库是MySQL,那么还好,mysql_query()函数不允许直接执行这样的操作(不能单行进行多个语句操作),所以你可以放心。如果你使用的数据库是SQLite或者PostgreSQL,支持这样的语句,那么就将面临灭顶之灾了。
上面提到,SQL注入主要是提交不安全的数据给数据库来达到攻击目的。为了防止SQL注入攻击,PHP自带一个功能可以对输入的字符串进行处理,可以在较底层对输入进行安全上的初步处理,也即Magic Quotes。(php.ini magic_quotes_gpc)。如果magic_quotes_gpc选项启用,那么输入的字符串中的单引号,双引号和其它一些字符前将会被自动加上反斜杠\。
但Magic Quotes并不是一个很通用的解决方案,没能屏蔽所有有潜在危险的字符,并且在许多服务器上Magic Quotes并没有被启用。所以,我们还需要使用其它多种方法来防止SQL注入。许多数据库本身就提供这种输入数据处理功能。例如PHP的MySQL操作函数中有一个叫mysql_real_escape_string()的函数,可将特殊字符和可能引起数据库操作出错的字符转义。
看这段代码:
//如果Magic Quotes功用启用
if (get_magic_quotes_gpc()) {
$name = stripslashes($name);
}else{
$name = mysql_real_escape_string($name);
}
mysql_query(“Select * FROM users Where name=’{$name}’”);
注意,在我们使用数据库所带的功能之前要判断一下Magic Quotes是否打开,就像上例中那样,否则两次重复处理就会出错。如果MQ已启用,我们要把加上的\去掉才得到真实数据。除了对以上字符串形式的数据进行预处理之外,储存Binary数据到数据库中时,也要注意进行预处理。否则数据可能与数据库自身的存储格式相冲突,引起数据库崩溃,数据记录丢失,甚至丢失整个库的数据。有些数据库如PostgreSQL,提供一个专门用来编码二进制数据的函数pg_escape_bytea(),它可以对数据进行类似于Base64那样的编码。
如:
// for plain-text data use:
pg_escape_string($regular_strings);
// for binary data use:
pg_escape_bytea($binary_data);
另一种情况下,我们也要采用这样的机制。那就是数据库系统本身不支持的多字节语言如中文,日语等。其中有些的ASCII范围和二进制数据的范围重叠。不过对数据进行编码将有可能导致像LIKE abc% 这样的查询语句失效。
以下是一段copy来的代码:
PHP代码
$_POST = sql_injection($_POST);
$_GET = sql_injection($_GET);
function sql_injection($content)
{
if (!get_magic_quotes_gpc()) {
if (is_array($content)) {
foreach ($content as $key=>$value) {
$content[$key] = addslashes($value);
}
} else {
addslashes($content);
}
}
return $content;
}
//用户发布的html,过滤危险代码
function uh($str)
{
$farr = array(
"/\s+/", //过滤多余的空白
"/<(\/?)(scripti?framestylehtmlbodytitlelinkmeta\?\%)([^>]*?)>/isU", //过滤 <script 等可能引入恶意内容或恶意改变显示布局的代码,如果不需要插入flash等,还可以加入<object的过滤
"/(<[^>]*)on[a-zA-Z]+\s*=([^>]*>)/isU", //过滤javascript的on事件
);
$tarr = array(
" ",
"<\\1\\2\\3>", //如果要直接清除不安全的标签,这里可以留空
"\\1\\2",
);
$str = preg_replace( $farr,$tarr,$str);
return $str;
}
//一个php操作mysql的脚本,带过滤XXS攻击的函数
<?php
class SFun{var $Fun="now()";function SFun($Date=""){if($Date!="")$this->Fun=$Date;}}
class MySqlDB
{
var $dbConn;
var $dbHost;
var $dbUID;
var $dbPWD;
var $dbName;
var $dbEncode;
var $IsOp;
function MySqlDB($Host='localhost',$UID='root',$Pwd='',$Name='',$Encode='utf8')
{
$this->dbHost = $Host;
$this->dbUID = $UID;
$this->dbPWD = $Pwd;
$this->dbName = $Name;
$this->dbEncode = $Encode;
$this->IsOp=false;
}
//打开数据库
function Open()
{
if (!$this->dbConn)
{
@$this->dbConn = mysql_connect($this->dbHost, $this->dbUID, $this->dbPWD) or die("数据库连接错误!...");
}
mysql_query("SET NAMES '" . $this->dbEncode . "'");
mysql_select_db($this->dbName);
$this->IsOp=true;
}
//关闭数据库
function Close()
{
if ($this->dbConn&&$this->IsOp)
{
mysql_close($this->dbConn);
$this->IsOp=false;
}
}
function htmlrsp($str)
{
$str=str_replace("<","<",$str);
$str=str_replace(">",">",$str);
return $str;
}
//过滤XSS危险脚本
function RemoveXSS($val) {
if(strpos($val,"<")===false)return $val;
$val = preg_replace('/([\x00-\x08][\x0b-\x0c][\x0e-\x20])/', '', $val);
$search = 'abcdefghijklmnopqrstuvwxyz';
$search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$search .= '1234567890!@#$%^&*()';
$search .= '~`";+/={}[]-_|\'\\';
for ($i = 0; $i < strlen($search); $i++) {
$val = preg_replace('/(&#[x|X]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val); // with a ;
$val = preg_replace('/(�{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ;
}
$ra1 = array('javascript', 'vbscript', 'expression', 'applet');
$ra2 = array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload');
$ra = array_merge($ra1, $ra2);
$val=preg_replace("'<script[^>]*?>.*?</script>'si","",$val);
$val=preg_replace("'<style[^>]*?>.*?</style>'si","",$val);
$val=preg_replace("/<(script|iframe|expression|applet|meta|xml|blink|style|frame|frameset|ilayer|layer|bgsound|title|base|link)(|[^>]+)>/i","",$val);
$val=preg_replace("/<\\/(script|iframe|expression|applet|meta|xml|blink|style|frame|frameset|ilayer|layer|bgsound|title|base)>/i","",$val);
$newval="";
$idx=0;
$edx=0;
do
{
$idx=strpos($val,"<",$edx);
if($idx!==false)
{
if($idx>$edx){
$str=substr($val,$edx,$idx-$edx);
$newval.=$this->htmlrsp($str);
}
$edx=strpos($val,">",$idx);
if($edx!==false&&$edx>$idx)
{
$edx++;
$tag=substr($val,$idx,$edx-$idx);
$ridx=strrpos($tag,"<",1);
if($ridx!==false){
$str=substr($val,$idx,$ridx);
$newval.=$this->htmlrsp($str);
$idx=$idx+$ridx;
$tag=substr($val,$idx,$edx-$idx);
}
if(stripos($tag,"on")===false)
{$newval.=$tag;}
else
{
for($j=0;$j<count($ra1);$j++){if(stripos($tag,$ra1[$j])!==false){$tag=str_ireplace($ra1[$j],"",$tag);}}
for($j=0;$j<count($ra2);$j++){
if(stripos($tag,$ra2[$j])!==false){
$tag=preg_replace('/'.$ra2[$j].'="[^"]+"/i',"",$tag);
$tag=preg_replace("/".$ra2[$j]."='[^']+'/i","",$tag);
$tag=preg_replace("/".$ra2[$j]."=[^ ]+>/i",">",$tag);
$tag=preg_replace("/".$ra2[$j]."=[^ ]+ /i","",$tag);
$tag=str_ireplace($ra2[$j],"",$tag);
}}
$newval.=$tag;
}
}
else
{
$str=substr($val,$idx);
$newval.=$this->htmlrsp($str);
}
}
else
{
$str=substr($val,$edx);
$newval.=$this->htmlrsp($str);
}
}while($idx!==false&&$edx!==false);
return $newval;
}
//执行SQL语句
function ExeSql($sql)
{
mysql_query($sql, $this->dbConn) or die("执行SQL语句错误...".$sql);
}
function HGexecute($SqlArr)
{
mysql_query("SET AUTOCOMMIT=0");//设置为不自动提交,因为MYSQL默认立即执行
mysql_query("BEGIN");//开始事务定义
for($i=0;$i<count($SqlArr);$i++)
{
$sql=$SqlArr[$i];
if(!mysql_query($sql, $this->dbConn))
{
mysql_query("ROLLBACK");//判断执行失败回滚
mysql_query("SET AUTOCOMMIT=1");
return false;
}
}
mysql_query("SET AUTOCOMMIT=1");
mysql_query("COMMIT");//执行事务
return true;
}
//取SQL数据
function GetData($sql)
{
$result = mysql_query($sql, $this->dbConn) or die("查询SQL语句错误...".$sql);
$records=array();
//while($record = mysql_fetch_array($result))
while($record = mysql_fetch_object($result))
{
$records[] = $record;
}
return $records;
}
function _T($str){
$str=$this->RemoveXSS($str);
$str=str_replace('\\','\\\\',$str);
$str=str_replace('\'','\\\'',$str);
//$str=str_replace('"','\\"',$str);
return $str;}
function GetOne($sql)
{
$records=$this->GetData($sql);
return $records[0];
}
function Like($str)
{
$str=$this->_T($str);
$str=str_replace('%','\\%',$str);
return $str;
}
//插入记录
function Add($Tb,$A,$IsId=false)
{
$SqlKey=array();
$SqlArr=array();
foreach ($A as $key=>$value)
{
$SqlKey[]="`".$key."`";
if($value===NULL)
$SqlArr[]="NULL";
else if(gettype($value)=="integer"||gettype($value)=="boolean"||gettype($value)=="double"||gettype($value)=="float")
$SqlArr[]=$value;
else if(gettype($value)=="object")
$SqlArr[]=$value->Fun;
else
$SqlArr[]="'".$this->_T($value)."'";
}
$Sql="INSERT INTO ".$Tb."(".join(",",$SqlKey).") VALUES (".join(",",$SqlArr).")";
mysql_query($Sql, $this->dbConn) or die("执行SQL语句错误...".$Sql);
if($IsId)
return mysql_insert_id($this->dbConn);
else
return 0;
}
function Update($Tb,$A,$Id)
{
$Id=intval($Id);
$SqlArr=array();
foreach ($A as $key=>$value)
{
if($value===NULL)
$SqlArr[]="`".$key."`=NULL";
else if(gettype($value)=="integer"||gettype($value)=="boolean"||gettype($value)=="double"||gettype($value)=="float")
$SqlArr[]="`".$key."`=".$value;
else if(gettype($value)=="object")
$SqlArr[]="`".$key."`=".$value->Fun;
else
$SqlArr[]="`".$key."`='".$this->_T($value)."'";
}
$Sql="update ".$Tb." set ".join(",",$SqlArr)." where id=".$Id;
mysql_query($Sql, $this->dbConn) or die("执行SQL语句错误...".$Sql);
}
function Del($Tb,$Id)
{
$Id=intval($Id);
$Sql="delete from ".$Tb." where id=".$Id;
mysql_query($Sql, $this->dbConn) or die("执行SQL语句错误...".$Sql);
}
}
/*
$DB=new MySqlDB('127.0.0.1','root','123','test');
$DB->Open();
$A["key1"]=$_POST["FCKeditor4"];
$A["key2"]=NULL;
$A["key3"]=new SFun("now()");
$A["key4"]=NULL;
$id=$DB->Add("test",$A,1);
echo $id;
$Row=$DB->GetOne("select * from test where id=".$id);
print_r($Row);
$DB->Close();*/
?> |
|