php 安全过滤函数代码,防止用户恶意输入内容。
//安全过滤输入[jb]
function check_str($string, $isurl = false)
{
$string =
preg_replace(‘/[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]/‘,‘‘,$string);
$string =
str_replace(array("\0","%00","\r"),‘‘,$string);
empty($isurl) &&
$string = preg_replace("/&(?!(#[0-9]+|[a-z]+);)/si",‘&‘,$string);
$string = str_replace(array("%3C",‘<‘),‘<‘,$string);
$string =
str_replace(array("%3E",‘>‘),‘>‘,$string);
$string =
str_replace(array(‘"‘,"‘","\t",‘ ‘),array(‘“‘,‘‘‘,‘ ‘,‘ ‘),$string);
return
trim($string);
}
/** * 安全过滤类-过滤javascript,css,iframes,object等不安全参数 过滤级别高 * Controller中使用方法:$this->controller->fliter_script($value) * @param string $value 需要过滤的值 * @return string */ function fliter_script($value) { $value = preg_replace("/(javascript:)?on(click|load|key|mouse|error|abort|move|unload|change|dblclick|move|reset|resize|submit)/i","&111n\\2",$value); $value = preg_replace("/(.*?)<\/script>/si","",$value); $value = preg_replace("/(.*?)<\/iframe>/si","",$value); $value = preg_replace ("//iesU", ‘‘, $value); return $value; } /** * 安全过滤类-过滤HTML标签 * Controller中使用方法:$this->controller->fliter_html($value) * @param string $value 需要过滤的值 * @return string */ function fliter_html($value) { if (function_exists(‘htmlspecialchars‘)) return htmlspecialchars($value); return str_replace(array("&", ‘"‘, "‘", "<", ">"), array("&", "\"", "‘", "<", ">"), $value); } /** * 安全过滤类-对进入的数据加下划线 防止SQL注入 * Controller中使用方法:$this->controller->fliter_sql($value) * @param string $value 需要过滤的值 * @return string */ function fliter_sql($value) { $sql = array("select", ‘insert‘, "update", "delete", "\‘", "\/\*", "\.\.\/", "\.\/", "union", "into", "load_file", "outfile"); $sql_re = array("","","","","","","","","","","",""); return str_replace($sql, $sql_re, $value); } /** * 安全过滤类-通用数据过滤 * Controller中使用方法:$this->controller->fliter_escape($value) * @param string $value 需要过滤的变量 * @return string|array */ function fliter_escape($value) { if (is_array($value)) { foreach ($value as $k => $v) { $value[$k] = self::fliter_str($v); } } else { $value = self::fliter_str($value); } return $value; } /** * 安全过滤类-字符串过滤 过滤特殊有危害字符 * Controller中使用方法:$this->controller->fliter_str($value) * @param string $value 需要过滤的值 * @return string */ function fliter_str($value) { $badstr = array("\0", "%00", "\r", ‘&‘, ‘ ‘, ‘"‘, "‘", "<", ">", " ", "%3C", "%3E"); $newstr = array(‘‘, ‘‘, ‘‘, ‘&‘, ‘ ‘, ‘"‘, ‘‘‘, "<", ">", " ", "<", ">"); $value = str_replace($badstr, $newstr, $value); $value = preg_replace(‘/&((#(\d{3,5}|x[a-fA-F0-9]{4}));)/‘, ‘&\\1‘, $value); return $value; } /** * 私有路劲安全转化 * Controller中使用方法:$this->controller->filter_dir($fileName) * @param string $fileName * @return string */ function filter_dir($fileName) { $tmpname = strtolower($fileName); $temp = array(‘:/‘,"\0", ".."); if (str_replace($temp, ‘‘, $tmpname) !== $tmpname) { return false; } return $fileName; } /** * 过滤目录 * Controller中使用方法:$this->controller->filter_path($path) * @param string $path * @return array */ public function filter_path($path) { $path = str_replace(array("‘",‘#‘,‘=‘,‘`‘,‘$‘,‘%‘,‘&‘,‘;‘), ‘‘, $path); return rtrim(preg_replace(‘/(\/){2,}|(\\\){1,}/‘, ‘/‘, $path), ‘/‘); } /** * 过滤PHP标签 * Controller中使用方法:$this->controller->filter_phptag($string) * @param string $string * @return string */ public function filter_phptag($string) { return str_replace(array(‘‘), array(‘<?‘, ‘?>‘), $string); } /** * 安全过滤类-返回函数 * Controller中使用方法:$this->controller->str_out($value) * @param string $value 需要过滤的值 * @return string */ public function str_out($value) { $badstr = array("<", ">", "%3C", "%3E"); $newstr = array("<", ">", "<", ">"); $value = str_replace($newstr, $badstr, $value); return stripslashes($value); //下划线 }
php使用正则过滤js脚本代码实例
<?php header("Content-type:text/html;charset=utf-8"); $str = ‘<script type="text/javascript" src="dd.js"></script> 测试php正则匹配掉js代码测试php正则匹配掉js代码测试php正则匹配掉js代码测试php正则匹配掉js代码测试php正则匹配掉js代码测试php正则匹配掉js代码 <script type="text/javascript" src="123.js"></script> <script type="text/javascript"> var aa = "sdsds"; alert(aa); </script> 测试php正则匹配掉js代码‘; $preg = "/<script[\s\S]*?<\/script>/i"; $newstr = preg_replace($preg,"",$str,3); //第四个参数中的3表示替换3次,默认是-1,替换全部 echo $newstr; ?>
一个完整成熟的站点,内容过滤,防注入,加密存储和传输,敏感词屏蔽都是必不可少功能,关于加密传输有cookie加密存储和解密,客户端js加密到php解密和php文件之间的加密传输,涉及的内容较多,如有可能将另开一贴,本贴主要分享一下内容过滤和敏感词屏蔽的经验
一,内容过滤
说起过滤,大概马上会想到addslashes函数,这个常用于防止sql注入,但其实光这些还远远不够,我们要做的常常是将所有不相关的sql关键词全部替换掉,保证发上来的东西放进sql语句里是无法改变sql行为的,所以一般来说,安全起见这两点要求都需要达到
addslashes函数为了不重复转义,一般会需要判断下服务器是否开启了自动转义,然后再进行转义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
function add_slashes( $string , $force = 0) { if (!get_magic_quotes_gpc() || $force ) { if ( is_array ( $string )) { foreach ( $string as $key => $val ) { $string [ $key ] = daddslashes( $val , $force ); } } else { $string = addslashes ( $string ); } } return $string ; } |
为了让所有的sql关键词都安全存储,我们需要将它们转义,显示的时候再翻译回来
function sql_encode($str)
{
if(empty($str)) return "";
$str=trim($str);
$str=str_replace("_","\_",$str);
$str=str_replace("%","\%",$str);
$str=str_replace(chr(39),"'",$str);
$str=str_replace("‘","‘‘",$str);
$str=str_replace("select","select",$str);
$str=str_replace("join","join",$str);
$str=str_replace("union","union",$str);
$str=str_replace("where","where",$str);
$str=str_replace("insert","insert",$str);
$str=str_replace("delete","delete",$str);
$str=str_replace("update","update",$str);
$str=str_replace("like","like",$str);
$str=str_replace("drop","drop",$str);
$str=str_replace("create","create",$str);
$str=str_replace("modify","modify",$str);
$str=str_replace("rename","rename",$str);
$str=str_replace("alter","alter",$str);
$str=str_replace("cast","cas",$str);
return $str;
}
function sql_decode($str)
{
if(empty($str)) return "";
$str=str_replace("'",chr(39),$str);
$str=str_replace("‘‘","‘",$str);
$str=str_replace("select","select",$str);
$str=str_replace("join","join",$str);
$str=str_replace("union","union",$str);
$str=str_replace("where","where",$str);
$str=str_replace("insert","insert",$str);
$str=str_replace("delete","delete",$str);
$str=str_replace("update","update",$str);
$str=str_replace("like","like",$str);
$str=str_replace("drop","drop",$str);
$str=str_replace("create","create",$str);
$str=str_replace("modify","modify",$str);
$str=str_replace("rename","rename",$str);
$str=str_replace("alter","alter",$str);
$str=str_replace("cas","cast",$str);
return $str;
}
有了以上的函数,对于sql语句涉及的内容就可以放心使用了,但这还不够,页面显示内容也需要过滤,页面内容过滤也分很多情况,比如标题是不允许html标签的,用户名是除了限制的数字字母,下划线外,其他都不允许使用的,而内容是可以有限制性的使用一些html标签,这就需要我们针对不同情况做不同的过滤处理
1,标题过滤
function filters_title($text)
{
$text = trim($text);
$text = str_replace("‘","",$text);
$text = strip_tags($text);
$text = stripslashes($text);
return $text;
}
2,用户名过滤
function filters_username($string)
{
$length=strlen($string);
if($length<2 || $length>18){return false;}
for($n=0; $n<$length; $n++)
{
$t = ord($string[$n]);
if( (47<$t && $t<58) || (64<$t && $t<91) || (96<$t && $t<123) || $t==45 || $t==95 || $t>126){}
else{return false;}
}
return true;
}
3,内容过滤
function filters_outcontent($str)
{
$str = stripslashes($str);
$str = preg_replace("/<div[^>]*?>/is","",$str);
$str = str_replace("aaaaa","\r\n",$str);
$str = str_replace("bbbbb","\n",$str);
$str = str_replace("ccccc","\r",$str);
$str = str_replace(‘\"‘,‘"‘,$str);
$str = str_replace(array(‘<HTML‘, ‘<BODY‘, ‘<INPUT‘, ‘<SCRIPT‘, ‘<FORM‘, ‘<IFRAME‘), array(‘<html‘, ‘<body‘, ‘<input‘, ‘<script‘, ‘<form‘, ‘<iframe‘), $str);
$str = str_replace(array(‘<html‘, ‘<body‘, ‘<input‘, ‘<script‘, ‘<form‘, ‘<iframe‘, ‘<textarea‘,‘</textarea>‘), array(‘<html‘, ‘<body‘, ‘<input‘, ‘<script‘, ‘<form‘, ‘<iframe‘, ‘<textarea‘, ‘</textarea>‘), $str);
return $str;
}
可以根据以上思路添加自己的过滤场景,做到符合项目要求
二,敏感词屏蔽
敏感词屏蔽是现在国内站点必须使用的功能之一了,国情如此,辛辛苦苦做的站,因为这个被封,实在很不划算。根据应用场景,敏感词过滤可以分为替换和禁止两种,替换就是不提示,直接替换敏感词为**等,禁止一半而言需要提示用户,有敏感词,需要修改,例如在用户注册时候,替换一般用在文章中。
敏感词需要数据库支持,将敏感词存入数据库时候,可以按照分类,那些是要替换的,那些是要禁止的,按照固定格式存储,例如 abc=**,这是替换。cba={banned},这是禁止,或者在函数中制定第二个参数,replace或者banned来制定函数执行替换或者精致操作都可以,我采用的是第一种,如果有需要,大家可以根据原函数修改为第二种,最核心的其实很简单就是个正则查找的过程
function censor($string) {
global $dblink, $tablepre;
$censoraray = $banned = $banwords = array();
$query = $dblink->query("SELECT * FROM censor WHERE var=‘censor‘ ");
if($value = $dblink->fetch_array($query)) {
$censorstr = is_array($value)?$value[‘datavalue‘]:$value;
} else {
$censorstr = ‘‘;
}
if (strlen(trim($censorstr)) > 0) { //有值就屏蔽
$censorarr = explode("\n", $censorstr);//按输入时候的回车分割为数组
foreach($censorarr as $censor) {
$censor = trim($censor);
if(empty($censor)) continue;
list($find, $replace) = explode(‘=‘, $censor);
$findword = $find;
$find = preg_replace("/\\\{(\d+)\\\}/", ".{0,\\1}", preg_quote($find, ‘/‘));//匹配屏蔽语法中的"a{1}s{2}s"
switch($replace) {
case ‘{BANNED}‘:
$banwords[] = preg_replace("/\\\{(\d+)\\\}/", "*", preg_quote($findword, ‘/‘));
$banned[] = $find;
break;
default:
$censoraray[‘filter‘][‘find‘][] = ‘/‘.$find.‘/i‘;
$censoraray[‘filter‘][‘replace‘][] = $replace;
break;
}
}
if($banned) {
$censoraray[‘banned‘] = ‘/(‘.implode(‘|‘, $banned).‘)/i‘;
$censoraray[‘banword‘] = implode(‘, ‘, $banwords);
}
if($censoraray[‘banned‘] && preg_match($censoraray[‘banned‘], $string)) {
return $censoraray[‘banned‘];//有敏感词汇不予显示
} else {
$string = empty($censoraray[‘filter‘]) ? $string :
@preg_replace($censoraray[‘filter‘][‘find‘], $censoraray[‘filter‘][‘replace‘], $string);
}
}
return $string;
}
因为我采用了第一种,所以需要将敏感词从数据库中取出后,做替换或者禁止的判断,其实核心代码很少,算是给大家一些思路吧
三,cookie加密
cookie加密存储是站点安全的很重要选择步骤,一些敏感的数据存储在cookie里方便了页面之间的传输,或者用于身份认定,来源判断,这些数据如果不加密,对有经验的人来说,很容易就会分析出一些站点的内部机制,或者将cookie用于跨站攻击。
设置cookie的语法很简单
setcookie(name,value,expire,path,domain,secure)
name 必需。规定 cookie 的名称。
value 必需。规定 cookie 的值。
expire 可选。规定 cookie 的有效期。
path 可选。规定 cookie 的服务器路径。
domain 可选。规定 cookie 的域名。
secure 可选。规定是否通过安全的 HTTPS 连接来传输 cookie。
设置cookie的函数可以这样写
function set_cookie($var, $value, $life = 0, $prefix = 1)
{
global $cookiepre, $cookiedomain, $cookiepath, $timestamp, $_SERVER;
setcookie(($prefix ? $cookiepre : ‘‘).$var, $value, $life ? $timestamp + $life : 0, $cookiepath,$cookiedomain, $_SERVER[‘SERVER_PORT‘] == 443 ? 1 : 0);
}
解释一下
1,$prefix = 1 所以当($prefix ? $cookiepre : ‘‘).$var时候就会加上前缀,也可以选择不加 2,$lift=0 所以当$life ? $timestamp + $life : 0时候,如果传值了且不是0,就会设置有效期
这里最重要的是加密函数,我这里用的是下面这个
function authcode($string, $operation = ‘DECODE‘, $key = ‘‘, $expiry = 0)
{
$ckey_length = 4;
//note 随机密钥长度 取值 0-32;
//note 加入随机密钥,可以令密文无任何规律,即便是原文和密钥完全相同,加密结果也会每次不同,增大破解难度。
//note 取值越大,密文变动规律越大,密文变化 = 16 的 $ckey_length 次方
//note 当此值为 0 时,则不产生随机密钥
$key = md5($key ? $key : UC_KEY);
$keya = md5(substr($key, 0, 16));
$keyb = md5(substr($key, 16, 16));
$keyc = $ckey_length ? ($operation == ‘DECODE‘ ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : ‘‘;
$cryptkey = $keya.md5($keya.$keyc);
$key_length = strlen($cryptkey);
$string = $operation == ‘DECODE‘ ? base64_decode(substr($string, $ckey_length)) : sprintf(‘%010d‘, $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
$string_length = strlen($string);
$result = ‘‘;
$box = range(0, 255);
$rndkey = array();
for($i = 0; $i <= 255; $i++)
{
$rndkey[$i] = ord($cryptkey[$i % $key_length]);
}
for($j = $i = 0; $i < 256; $i++)
{
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
$tmp = $box[$i];
$box[$i] = $box[$j];
$box[$j] = $tmp;
}
for($a = $j = $i = 0; $i < $string_length; $i++)
{
$a = ($a + 1) % 256;
$j = ($j + $box[$a]) % 256;
$tmp = $box[$a];
$box[$a] = $box[$j];
$box[$j] = $tmp;
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
}
if($operation == ‘DECODE‘)
{
if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16))
{
return substr($result, 26);
}
else
{
return ‘‘;
}
}
else
{
return $keyc.str_replace(‘=‘, ‘‘, base64_encode($result));
}
}
第二个参数可设置为加密encode,或者解密decode。
这样一来,加密的设置cookie就可以这样写
set_cookie(‘compound‘, authcode("$uid\t$uname\t$pw", ‘ENCODE‘, $key));
读取的时候,将cookie值读入变量,然后 $cookiearray=explode("\t", authcod($cookieval, ‘DECODE‘, $key))就可以读入到数组里了。
部分来源:http://www.cnblogs.com/phpinfo/p/3592873.html
php过滤所有恶意字符:
//php 批量过滤post,get敏感数据 if (get_magic_quotes_gpc()) { $_GET = stripslashes_array($_GET); $_POST = stripslashes_array($_POST); } function stripslashes_array(&$array) { while(list($key,$var) = each($array)) { if ($key != ‘argc‘ && $key != ‘argv‘ && (strtoupper($key) != $key || ‘‘.intval($key) == "$key")) { if (is_string($var)) { $array[$key] = stripslashes($var); } if (is_array($var)) { $array[$key] = stripslashes_array($var); } } } return $array; } //过滤 function htmlencode($str){ if(empty($str)) return; if($str=="") return $str; $str=trim($str); $str=str_replace("&","&",$str); $str=str_replace(">",">",$str); $str=str_replace("<","<",$str); $str=str_replace(chr(32)," ",$str); $str=str_replace(chr(9)," ",$str); $str=str_replace(chr(9)," ",$str); $str=str_replace(chr(34),"&",$str); $str=str_replace(chr(39),"‘",$str); $str=str_replace(chr(13)," ",$str); $str=str_replace("‘","‘‘",$str); $str=str_replace("select","select",$str); $str=str_replace("SCRIPT","SCRIPT",$str); $str=str_replace("script","script",$str); $str=str_replace("join","join",$str); $str=str_replace("union","union",$str); $str=str_replace("where","where",$str); $str=str_replace("insert","insert",$str); $str=str_replace("delete","delete",$str); $str=str_replace("update","update",$str); $str=str_replace("like","like",$str); $str=str_replace("drop","drop",$str); $str=str_replace("create","create",$str); $str=str_replace("modify","modify",$str); $str=str_replace("rename","rename",$str); $str=str_replace("alter","alter",$str); $str=str_replace("cast","cas",$str); return $str; } //解码 function htmldecode($str){ if(empty($str)) return; if($str=="") return $str; $str=str_replace("select","select",$str); $str=str_replace("join","join",$str); $str=str_replace("union","union",$str); $str=str_replace("where","where",$str); $str=str_replace("insert","insert",$str); $str=str_replace("delete","delete",$str); $str=str_replace("update","update",$str); $str=str_replace("like","like",$str); $str=str_replace("drop","drop",$str); $str=str_replace("create","create",$str); $str=str_replace("modify","modify",$str); $str=str_replace("rename","rename",$str); $str=str_replace("alter","alter",$str); $str=str_replace("cas","cast",$str); $str=str_replace("&","&",$str); $str=str_replace(">",">",$str); $str=str_replace("<","<",$str); $str=str_replace(" ",chr(32),$str); $str=str_replace(" ",chr(9),$str); $str=str_replace(" ",chr(9),$str); $str=str_replace("&",chr(34),$str); $str=str_replace("‘",chr(39),$str); $str=str_replace(" ",chr(13),$str); $str=str_replace("‘‘","‘",$str); return $str; } // 函数:string_filter($string, $match_type=1) // 功能:过滤非法内容 // 参数: // $string 需要检查的字符串 // $match_type 匹配类型,1为精确匹配, 2为模糊匹配,默认为1 // // 返回:有非法内容返回True,无非法内容返回False // 其他:非法关键字列表保存在txt文件里, 分为普通非法关键字和严重非法关键字两个列表 // 作者:heiyeluren // 时间:2006-1-18 // //====================================================================== function lib_lawless_string_filter($string, $match_type=1) { //字符串空直接返回为非法 $string = trim($string); if (empty($string)) { return false; } //获取重要关键字列表和普通关键字列表 $common_file = "common_list.txt"; //通用过滤关键字列表 $signify_file = "signify_list.txt"; //重要过滤关键字列表 //如果任何列表文件不存在直接返回false,否则把两个文件列表读取到两个数组里 if (!file_exists($common_file) || !file_exists($signify_file)) { return false; } $common_list = file($common_file); $signify_list = file($signify_file); //精确匹配 if ($match_type == 1) { $is_lawless = exact_match($string, $common_list); } //模糊匹配 if ($match_type == 2) { $is_lawless = blur_match($string, $common_list, $signify_list); } //判断检索结果数组中是否有数据,如果有,证明是非法的 if (is_array($is_lawless) && !empty($is_lawless)) { return true; } else { return false; } } //--------------------- // 精确匹配,为过滤服务 //--------------------- function exact_match($string, $common_list) { $string = trim($string); $string = lib_replace_end_tag($string); //检索普通过滤关键字列表 foreach($common_list as $block) { $block = trim($block); if (preg_match("/^$string$/i", $block)) { $blist[] = $block; } } //判断有没有过滤内容在数组里 if (!empty($blist)) { return array_unique($blist); } return false; } //---------------------- // 模糊匹配,为过滤服务 //---------------------- function blur_match($string, $common_list, $signify_list) { $string = trim($string); $s_len = strlen($string); $string = lib_replace_end_tag($string); //检索普通过滤关键字列表 foreach($common_list as $block) { $block = trim($block); if (preg_match("/^$string$/i", $block)) { $blist[] = $block; } } //检索严重过滤关键字列表 foreach($signify_list as $block) { $block = trim($block); if ($s_len>=strlen($block) && preg_match("/$block/i", $string)) { $blist[] = $block; } } //判断有没有过滤内容在数组里 if (!empty($blist)) { return array_unique($blist); } return false; } //-------------------------- // 替换HTML尾标签,为过滤服务 //-------------------------- function lib_replace_end_tag($str) { if (empty($str)) return false; $str = htmlspecialchars($str); $str = str_replace( ‘/‘, "", $str); $str = str_replace("\\", "", $str); $str = str_replace(">", "", $str); $str = str_replace("<", "", $str); $str = str_replace("", "", $str); $str = str_replace("", "", $str); $str=str_replace("select","select",$str); $str=str_replace("join","join",$str); $str=str_replace("union","union",$str); $str=str_replace("where","where",$str); $str=str_replace("insert","insert",$str); $str=str_replace("delete","delete",$str); $str=str_replace("update","update",$str); $str=str_replace("like","like",$str); $str=str_replace("drop","drop",$str); $str=str_replace("create","create",$str); $str=str_replace("modify","modify",$str); $str=str_replace("rename","rename",$str); $str=str_replace("alter","alter",$str); $str=str_replace("cas","cast",$str); $str=str_replace("&","&",$str); $str=str_replace(">",">",$str); $str=str_replace("<","<",$str); $str=str_replace(" ",chr(32),$str); $str=str_replace(" ",chr(9),$str); $str=str_replace(" ",chr(9),$str); $str=str_replace("&",chr(34),$str); $str=str_replace("‘",chr(39),$str); $str=str_replace(" ",chr(13),$str); $str=str_replace("‘‘","‘",$str); $str=str_replace("css","‘",$str); $str=str_replace("CSS","‘",$str); return $str; //HTML标签,可以作为扩展过滤 /* $tags = array("/html", "/head", "/body", "/div", "/span", "/DOCTYPE", "/title", "/link", "/meta", "/style", "/p", "/h1,", "/h2,", "/h3,", "/h4,", "/h5,", "/h6", "/strong", "/em", "/abbr", "/acronym", "/address", "/bdo", "/blockquote", "/cite", "/q", "/code", "/ins", "/del", "/dfn", "/kbd", "/pre", "/samp", "/var", "/br", "/a", "/img", "/area", "/map", "/object", "/param", "/ul", "/ol", "/li", "/dl", "/dt", "/dd", "/table", "/tr", "/td", "/th", "/tbody", "/thead", "/tfoot", "/col", "/colgroup", "/caption", "/form", "/input", "/textarea", "/select", "/option", "/optgroup", "/button", "/label", "/fieldset", "/legend", "/script", "/noscript", "/b", "/i", "/tt", "/sub", "/sup", "/big", "/small", "/hr" ); */ }
代码:
引用是直接这样:
$xxx = htmlspecialchars($_POST[‘xxx‘]);
或者
$xxx = htmlspecialchars($_GET[‘xxx‘]);
作为PHP程序员,特别是新手,对于互联网的险恶总是知道的太少,对于外部的入侵有很多时候是素手无策的,他们根本不知道黑客是如何入侵的、提交入侵、上传漏洞、sql 注入、跨脚本攻击等等。
作为最基本的防范你需要注意你的外部提交,做好第一面安全机制处理防火墙。
规则 1:绝不要信任外部数据或输入
关于Web应用程序安全性,必须认识到的第一件事是不应该信任外部数据。外部数据(outside data) 包括不是由程序员在PHP代码中直接输入的任何数据。在采取措施确保安全之前,来自任何其他来源(比如 GET 变量、表单 POST、数据库、配置文件、会话变量或 cookie)的任何数据都是不可信任的。 例如,下面的数据元素可以被认为是安全的,因为它们是在PHP中设置的。
但是,下面的数据元素都是有瑕疵的。
清单 2. 不安全、有瑕疵的代码
为 什么第一个变量 $myUsername 是有瑕疵的?因为它直接来自表单 POST。用户可以在这个输入域中输入任何字符串,包括用来清除文件或运行以前上传的文件的恶意命令。您可能会问,“难道不能使用只接受字母 A-Z 的客户端(Javascrīpt)表单检验脚本来避免这种危险吗?”是的,这总是一个有好处的步骤,但是正如在后面会看到的,任何人都可以将任何表单下载 到自己的机器上,修改它,然后重新提交他们需要的任何内容。 解决方案很简单:必须对 $_POST[‘username‘] 运行清理代码。如果不这么做,那么在使用 $myUsername 的任何其他时候(比如在数组或常量中),就可能污染这些对象。
对用户输入进行清理的一个简单方法是,使用正则表达式来处理它。在这个示例中,只希望接受字母。将字符串限制为特定数量的字符,或者要求所有字母都是小写的,这可能也是个好主意。
清单 3. 使用户输入变得安全
规则 2:禁用那些使安全性难以实施的 PHP 设置 已经知道了不能信任用户输入,还应该知道不应该信任机器上配置 PHP 的方式。例如,要确保禁用 register_globals。如果启用了 register_globals,就可能做一些粗心的事情,比如使用 $variable 替换同名的 GET 或 POST 字符串。通过禁用这个设置,PHP 强迫您在正确的名称空间中引用正确的变量。要使用来自表单 POST 的变量,应该引用 $_POST[‘variable‘]。这样就不会将这个特定变量误会成 cookie、会话或 GET 变量。
规则 3:如果不能理解它,就不能保护它
一些开发人员使用奇怪的语法,或者将语句组织得很紧凑,形成简短但是含义模糊的代码。这种方式可能效率高,但是如果您不理解代码正在做什么,那么就无法决定如何保护它。
例如,您喜欢下面两段代码中的哪一段?
清单 4. 使代码容易得到保护
在第二个比较清晰的代码段中,很容易看出 $input 是有瑕疵的,需要进行清理,然后才能安全地处理。 规则 4:“纵深防御” 是新的法宝 本教程将用示例来说明如何保护在线表单,同时在处理表单的 PHP 代码中采用必要的措施。同样,即使使用 PHP regex 来确保 GET 变量完全是数字的,仍然可以采取措施确保 SQL 查询使用转义的用户输入。
纵深防御不只是一种好思想,它可以确保您不会陷入严重的麻烦。 既然已经讨论了基本规则,现在就来研究第一种威胁:SQL 注入攻击。 防止 SQL 注入攻击 在 SQL 注入攻击 中,用户通过操纵表单或 GET 查询字符串,将信息添加到数据库查询中。例如,假设有一个简单的登录数据库。这个数据库中的每个记录都有一个用户名字段和一个密码字段。构建一个登录表单,让用户能够登录。
清单 5. 简单的登录表单
这个表单接受用户输入的用户名和密码,并将用户输入提交给名为 verify.php 的文件。在这个文件中,PHP 处理来自登录表单的数据,如下所示:
清单 6. 不安全的 PHP 表单处理代码
这 段代码看起来没问题,对吗?世界各地成百(甚至成千)的 PHP/MySQL 站点都在使用这样的代码。它错在哪里?好,记住 “不能信任用户输入”。这里没有对来自用户的任何信息进行转义,因此使应用程序容易受到攻击。具体来说,可能会出现任何类型的 SQL 注入攻击。
例如,如果用户输入 foo 作为用户名,输入 ‘ or ‘1′=‘1 作为密码,那么实际上会将以下字符串传递给 PHP,然后将查询传递给 MySQL:
这个查询总是返回计数值 1,因此 PHP 会允许进行访问。通过在密码字符串的末尾注入某些恶意 SQL,黑客就能装扮成合法的用户。
解 决这个问题的办法是,将 PHP 的内置 mysql_real_escape_string() 函数用作任何用户输入的包装器。这个函数对字符串中的字符进行转义,使字符串不可能传递撇号等特殊字符并让 MySQL 根据特殊字符进行操作。清单 7 展示了带转义处理的代码。
清单 7. 安全的 PHP 表单处理代码
使用 mysql_real_escape_string() 作为用户输入的包装器,就可以避免用户输入中的任何恶意 SQL 注入。如果用户尝试通过 SQL 注入传递畸形的密码,那么会将以下查询传递给数据库: select count(*) as ctr from users where username=‘foo‘ and password=‘\‘ or \‘1\‘=\‘1′ limit 1″
数据库中没有任何东西与这样的密码匹配。仅仅采用一个简单的步骤,就堵住了 Web 应用程序中的一个大漏洞。这里得出的经验是,总是应该对 SQL 查询的用户输入进行转义。 但是,还有几个安全漏洞需要堵住。下一项是操纵 GET 变量。 防止用户操纵 GET 变量
在前一节中,防止了用户使用畸形的密码进行登录。如果您很聪明,应该应用您学到的方法,确保对 SQL 语句的所有用户输入进行转义。 但 是,用户现在已经安全地登录了。用户拥有有效的密码,并不意味着他将按照规则行事 —— 他有很多机会能够造成损害。例如,应用程序可能允许用户查看特殊的内容。所有链接指向 template.php?pid=33 或 template.php?pid=321 这样的位置。URL 中问号后面的部分称为查询字符串。因为查询字符串直接放在 URL 中,所以也称为 GET 查询字符串。 在 PHP 中,如果禁用了 register_globals,那么可以用 $_GET[‘pid‘] 访问这个字符串。在 template.php 页面中,可能会执行与清单 8 相似的操作。 清单 8. 示例 template.php
代码如下:
这 里有什么错吗?首先,这里隐含地相信来自浏览器的 GET 变量 pid 是安全的。这会怎么样呢?大多数用户没那么聪明,无法构造出语义攻击。但是,如果他们注意到浏览器的 URL 位置域中的 pid=33,就可能开始捣乱。如果他们输入另一个数字,那么可能没问题;但是如果输入别的东西,比如输入 SQL 命令或某个文件的名称(比如 /etc/passwd),或者搞别的恶作剧,比如输入长达 3,000 个字符的数值,那么会发生什么呢?
在这种情况下,要记住基本规则,不要信任用户输入。应用程序开发人员知道 template.php 接受的个人标识符(PID)应该是数字,所以可以使用 PHP 的 is_numeric() 函数确保不接受非数字的 PID,如下所示:
清单 9. 使用 is_numeric() 来限制 GET 变量
这个方法似乎是有效的,但是以下这些输入都能够轻松地通过 is_numeric() 的检查: 100 (有效)
100.1 (不应该有小数位) +0123.45e6 (科学计数法 —— 不好) 0xff33669f (十六进制 —— 危险!危险!)
那么,有安全意识的 PHP 开发人员应该怎么做呢?多年的经验表明,最好的做法是使用正则表达式来确保整个 GET 变量由数字组成,如下所示:
清单 10. 使用正则表达式限制 GET 变量
需 要做的只是使用 strlen() 检查变量的长度是否非零;如果是,就使用一个全数字正则表达式来确保数据元素是有效的。如果 PID 包含字母、斜线、点号或任何与十六进制相似的内容,那么这个例程捕获它并将页面从用户活动中屏蔽。如果看一下 Page 类幕后的情况,就会看到有安全意识的 PHP 开发人员已经对用户输入 $pid 进行了转义,从而保护了 fetchPage() 方法,如下所示:
清单 11. 对 fetchPage() 方法进行转义
您可能会问,“既然已经确保 PID 是数字,那么为什么还要进行转义?” 因为不知道在多少不同的上下文和情况中会使用 fetchPage() 方法。必须在调用这个方法的所有地方进行保护,而方法中的转义体现了纵深防御的意义。 如 果用户尝试输入非常长的数值,比如长达 1000 个字符,试图发起缓冲区溢出攻击,那么会发生什么呢?下一节更详细地讨论这个问题,但是目前可以添加另一个检查,确保输入的 PID 具有正确的长度。您知道数据库的 pid 字段的最大长度是 5 位,所以可以添加下面的检查。
清单 12. 使用正则表达式和长度检查来限制 GET 变量
现在,任何人都无法在数据库应用程序中塞进一个 5,000 位的数值 —— 至少在涉及 GET 字符串的地方不会有这种情况。想像一下黑客在试图突破您的应用程序而遭到挫折时咬牙切齿的样子吧!而且因为关闭了错误报告,黑客更难进行侦察。 缓冲区溢出攻击
缓冲区溢出攻击 试图使 PHP 应用程序中(或者更精确地说,在 Apache 或底层操作系统中)的内存分配缓冲区发生溢出。请记住,您可能是使用 PHP 这样的高级语言来编写 Web 应用程序,但是最终还是要调用 C(在 Apache 的情况下)。与大多数低级语言一样,C 对于内存分配有严格的规则。
缓冲区溢出攻击向缓冲区发送大量数据,使部分数据溢出到相邻的内存缓冲区,从而破坏缓冲区或者重写逻辑。这样就能够造成拒绝服务、破坏数据或者在远程服务器上执行恶意代码。
防止缓冲区溢出攻击的惟一方法是检查所有用户输入的长度。例如,如果有一个表单元素要求输入用户的名字,那么在这个域上添加值为 40 的 maxlength 属性,并在后端使用 substr() 进行检查。清单 13 给出表单和 PHP 代码的简短示例。
清单 13. 检查用户输入的长度
为 什么既提供 maxlength 属性,又在后端进行 substr() 检查?因为纵深防御总是好的。浏览器防止用户输入 PHP 或 MySQL 不能安全地处理的超长字符串(想像一下有人试图输入长达 1,000 个字符的名称),而后端 PHP 检查会确保没有人远程地或者在浏览器中操纵表单数据。 正如您看到的,这种方式与前一节中使用 strlen() 检查 GET 变量 pid 的长度相似。在这个示例中,忽略长度超过 5 位的任何输入值,但是也可以很容易地将值截短到适当的长度,如下所示:
清单 14. 改变输入的 GET 变量的长度
注 意,缓冲区溢出攻击并不限于长的数字串或字母串。也可能会看到长的十六进制字符串(往往看起来像 \xA3 或 \xFF)。记住,任何缓冲区溢出攻击的目的都是淹没特定的缓冲区,并将恶意代码或指令放到下一个缓冲区中,从而破坏数据或执行恶意代码。对付十六进制缓 冲区溢出最简单的方法也是不允许输入超过特定的长度。
如果您处理的是允许在数据库中输入较长条目的表单文本区,那么无法在客户端轻松地限制数据的长度。在数据到达 PHP 之后,可以使用正则表达式清除任何像十六进制的字符串。
清单 15. 防止十六进制字符串
您 可能会发现这一系列操作有点儿太严格了。毕竟,十六进制串有合法的用途,比如输出外语中的字符。如何部署十六进制 regex 由您自己决定。比较好的策略是,只有在一行中包含过多十六进制串时,或者字符串的字符超过特定数量(比如 128 或 255)时,才删除十六进制串。
跨站点脚本攻击 在跨站点脚本(XSS)攻击中,往往有一个恶意用户在表单中(或通过其他用户输入方式)输入信息,这些输入将恶 意的客户端标记插入过程或数据库中。例如,假设站点上有一个简单的来客登记簿程序,让访问者能够留下姓名、电子邮件地址和简短的消息。恶意用户可以利用这 个机会插入简短消息之外的东西,比如对于其他用户不合适的图片或将用户重定向到另一个站点的 Javascrīpt,或者窃取 cookie 信息。
幸运的是,PHP 提供了 strip_tags() 函数,这个函数可以清除任何包围在 HTML 标记中的内容。strip_tags() 函数还允许提供允许标记的列表,比如 <b> 或 <i>。 浏览器内的数据操纵
有一类浏览器插件允许用户篡改页面上的头部元素和表单元素。使用 Tamper Data(一个 Mozilla 插件),可以很容易地操纵包含许多隐藏文本字段的简单表单,从而向 PHP 和 MySQL 发送指令。 用户在点击表单上的 Submit 之前,他可以启动 Tamper Data。在提交表单时,他会看到表单数据字段的列表。Tamper Data 允许用户篡改这些数据,然后浏览器完成表单提交。
让我们回到前面建立的示例。已经检查了字符串长度、清除了 HTML 标记并删除了十六进制字符。但是,添加了一些隐藏的文本字段,如下所示:
清单 17. 隐藏变量
注意,隐藏变量之一暴露了表名:users。还会看到一个值为 create 的 action 字段。只要有基本的 SQL 经验,就能够看出这些命令可能控制着中间件中的一个 SQL 引擎。想搞大破坏的人只需改变表名或提供另一个选项,比如 delete。
现在还剩下什么问题呢?远程表单提交。 远程表单提交 Web 的好处是可以分享信息和服务。坏处也是可以分享信息和服务,因为有些人做事毫无顾忌。 以 表单为例。任何人都能够访问一个 Web 站点,并使用浏览器上的 File > Save As 建立表单的本地副本。然后,他可以修改 action 参数来指向一个完全限定的 URL(不指向 formHandler.php,而是指向 http://www.yoursite.com/formHandler.php,因为表单在这个站点上),做他希望的任何修改,点击 Submit,服务器会把这个表单数据作为合法通信流接收。 首先可能考虑检查 $_SERVER[‘HTTP_REFERER‘],从而判断请求是否来自自己的服务器,这种方法可以挡住大多数恶意用户,但是挡不住最高明的黑客。这些人足够聪明,能够篡改头部中的引用者信息,使表单的远程副本看起来像是从您的服务器提交的。
处理远程表单提交更好的方式是,根据一个惟一的字符串或时间戳生成一个令牌,并将这个令牌放在会话变量和表单中。提交表单之后,检查两个令牌是否匹配。如果不匹配,就知道有人试图从表单的远程副本发送数据。
要创建随机的令牌,可以使用 PHP 内置的 md5()、uniqid() 和 rand() 函数,如下所示:
清单 18. 防御远程表单提交;可限制IP;
这种技术是有效的,这是因为在 PHP 中会话数据无法在服务器之间迁移。即使有人获得了您的 PHP 源代码,将它转移到自己的服务器上,并向您的服务器提交信息,您的服务器接收的也只是空的或畸形的会话令牌和原来提供的表单令牌。它们不匹配,远程表单提交就失败了。
原文:http://www.cnblogs.com/lbs8/p/4360895.html