手机
当前位置:查字典教程网 >编程开发 >php教程 >CI框架安全类Security.php源码分析
CI框架安全类Security.php源码分析
摘要:CI安全类提供了全局防御CSRF攻击和XSS攻击策略,只需要在配置文件开启即可:复制代码代码如下:$config['csrf_protect...

CI安全类提供了全局防御CSRF攻击和XSS攻击策略,只需要在配置文件开启即可:

复制代码 代码如下:

$config['csrf_protection'] = TRUE;

$config['global_xss_filtering'] = TRUE;

并提供了实用方法:

复制代码 代码如下:

$this->security->xss_clean($data);//第二个参数为TRUE,验证图片安全

$this->security->sanitize_filename()//过滤文件名

CI也提供了安全函数:

xss_clean()//xss过滤

sanitize_filename()//净化文件名

do_hash()//md5或sha加密

strip_image_tags() //删除图片标签的不必要字符

encode_php_tags()//把PHP脚本标签强制转成实体对象

复制代码 代码如下:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

/**

* 安全类

*/

class CI_Security {

//url的随机hash值

protected $_xss_hash= '';

//防csrf攻击的cookie标记的哈希值

protected $_csrf_hash= '';

//防csrf cookie过期时间

protected $_csrf_expire= 7200;

//防csrf的cookie名称

protected $_csrf_token_name= 'ci_csrf_token';

//防csrf的token名称

protected $_csrf_cookie_name= 'ci_csrf_token';

//不允许出现的字符串数组

protected $_never_allowed_str = array(

'document.cookie'=> '[removed]',

'document.write'=> '[removed]',

'.parentNode'=> '[removed]',

'.innerHTML'=> '[removed]',

'window.location'=> '[removed]',

'-moz-binding'=> '[removed]',

'<',

'-->'=> '-->',

'<![CDATA['=> '<![CDATA[',

'<comment>'=> '<comment>'

);

//不允许出现的正则表达式数组

protected $_never_allowed_regex = array(

'javascripts*:',

'expressions*((|()', // CSS and IE

'vbscripts*:', // IE, surprise!

'Redirects+302',

"(["'])?datas*:[^1]*?base64[^1]*?,[^1]*"

);

//构造函数

public function __construct()

{

// CSRF保护是否开启

if (config_item('csrf_protection') === TRUE)

{

// CSRF配置

foreach (array('csrf_expire', 'csrf_token_name', 'csrf_cookie_name') as $key)

{

if (FALSE !== ($val = config_item($key)))

{

$this->{'_'.$key} = $val;

}

}

// _csrf_cookie_name加上cookie前缀

if (config_item('cookie_prefix'))

{

$this->_csrf_cookie_name = config_item('cookie_prefix').$this->_csrf_cookie_name;

}

// 设置csrf的hash值

$this->_csrf_set_hash();

}

log_message('debug', "Security Class Initialized");

}

// --------------------------------------------------------------------

/**

* Verify Cross Site Request Forgery Protection

*

* @returnobject

*/

public function csrf_verify()

{

// 如果不是post请求,则设置csrf的cookie值

if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST')

{

return $this->csrf_set_cookie();

}

// Do the tokens exist in both the _POST and _COOKIE arrays?

if ( ! isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name]))

{

$this->csrf_show_error();

}

// token匹配吗

if ($_POST[$this->_csrf_token_name] != $_COOKIE[$this->_csrf_cookie_name])

{

$this->csrf_show_error();

}

// We kill this since we're done and we don't want to

// polute the _POST array

unset($_POST[$this->_csrf_token_name]);

// Nothing should last forever

unset($_COOKIE[$this->_csrf_cookie_name]);

$this->_csrf_set_hash();

$this->csrf_set_cookie();

log_message('debug', 'CSRF token verified');

return $this;

}

// --------------------------------------------------------------------

/**

* 设置csrf的cookie值

*/

public function csrf_set_cookie()

{

$expire = time() + $this->_csrf_expire;

$secure_cookie = (config_item('cookie_secure') === TRUE) ? 1 : 0;

if ($secure_cookie && (empty($_SERVER['HTTPS']) OR strtolower($_SERVER['HTTPS']) === 'off'))

{

return FALSE;

}

setcookie($this->_csrf_cookie_name, $this->_csrf_hash, $expire, config_item('cookie_path'), config_item('cookie_domain'), $secure_cookie);

log_message('debug', "CRSF cookie Set");

return $this;

}

//csrf保存

public function csrf_show_error()

{

show_error('The action you have requested is not allowed.');

}

//获取csrf的hash值

public function get_csrf_hash()

{

return $this->_csrf_hash;

}

//获取csrf的token值

public function get_csrf_token_name()

{

return $this->_csrf_token_name;

}

/**

* XSS 过滤

*/

public function xss_clean($str, $is_image = FALSE)

{

//是否是数组

if (is_array($str))

{

while (list($key) = each($str))

{

$str[$key] = $this->xss_clean($str[$key]);

}

return $str;

}

//去掉可见字符串

$str = remove_invisible_characters($str);

// 验证实体url

$str = $this->_validate_entities($str);

/*

* URL 解码

*

* Just in case stuff like this is submitted:

*

* <a href="http://%77%77%77%2E%67%6F%6F%67%6C%65%2E%63%6F%6D">Google</a>

*

* Note: Use rawurldecode() so it does not remove plus signs

*

*/

$str = rawurldecode($str);

/*

* Convert character entities to ASCII

*

* This permits our tests below to work reliably.

* We only convert entities that are within tags since

* these are the ones that will pose security problems.

*

*/

$str = preg_replace_callback("/[a-z]+=(['"]).*", array($this, '_convert_attribute'), $str);

$str = preg_replace_callback("/<w+.*?(?=>|<|$)/si", array($this, '_decode_entity'), $str);

/*

* Remove Invisible Characters Again!

*/

$str = remove_invisible_characters($str);

/*

* Convert all tabs to spaces

*

* This prevents strings like this: javascript

* NOTE: we deal with spaces between characters later.

* NOTE: preg_replace was found to be amazingly slow here on

* large blocks of data, so we use str_replace.

*/

if (strpos($str, "t") !== FALSE)

{

$str = str_replace("t", ' ', $str);

}

/*

* Capture converted string for later comparison

*/

$converted_string = $str;

// Remove Strings that are never allowed

$str = $this->_do_never_allowed($str);

/*

* Makes PHP tags safe

*

* Note: XML tags are inadvertently replaced too:

*

* <?xml

*

* But it doesn't seem to pose a problem.

*/

if ($is_image === TRUE)

{

// Images have a tendency to have the PHP short opening and

// closing tags every so often so we skip those and only

// do the long opening tags.

$str = preg_replace('/<"<", $str);

}

else

{

$str = str_replace(array('<?', '?'.'>'), array('<?', '?>'), $str);

}

/*

* Compact any exploded words

*

* This corrects words like: j a v a s c r i p t

* These words are compacted back to their correct state.

*/

$words = array(

'javascript', 'expression', 'vbscript', 'script', 'base64',

'applet', 'alert', 'document', 'write', 'cookie', 'window'

);

foreach ($words as $word)

{

$temp = '';

for ($i = 0, $wordlen = strlen($word); $i < $wordlen; $i++)

{

$temp .= substr($word, $i, 1)."s*";

}

// We only want to do this when it is followed by a non-word character

// That way valid stuff like "dealer to" does not become "dealerto"

$str = preg_replace_callback('#('.substr($temp, 0, -3).')(W)#is', array($this, '_compact_exploded_words'), $str);

}

/*

* Remove disallowed Javascript in links or img tags

* We used to do some version comparisons and use of stripos for PHP5,

* but it is dog slow compared to these simplified non-capturing

* preg_match(), especially if the pattern exists in the string

*/

do

{

$original = $str;

if (preg_match("/<a/i", $str))

{

$str = preg_replace_callback("#<as+([^>]*", array($this, '_js_link_removal'), $str);

}

if (preg_match("/<img/i", $str))

{

$str = preg_replace_callback("#<imgs+([^>]*?)(s?/", array($this, '_js_img_removal'), $str);

}

if (preg_match("/script/i", $str) OR preg_match("/xss/i", $str))

{

$str = preg_replace("#<(/*)(script|xss)(.*", '[removed]', $str);

}

}

while($original != $str);

unset($original);

// Remove evil attributes such as style, onclick and xmlns

$str = $this->_remove_evil_attributes($str, $is_image);

/*

* Sanitize naughty HTML elements

*

* If a tag containing any of the words in the list

* below is found, the tag gets converted to entities.

*

* So this: <blink>

* Becomes: <blink>

*/

$naughty = 'alert|applet|audio|basefont|base|behavior|bgsound|blink|body|embed|expression|form|frameset|frame|head|html|ilayer|iframe|input|isindex|layer|link|meta|object|plaintext|style|script|textarea|title|video|xml|xss';

$str = preg_replace_callback('#<(/*s*)('.$naughty.')([^><]*)([><]*)#is', array($this, '_sanitize_naughty_html'), $str);

/*

* Sanitize naughty scripting elements

*

* Similar to above, only instead of looking for

* tags it looks for PHP and JavaScript commands

* that are disallowed. Rather than removing the

* code, it simply converts the parenthesis to entities

* rendering the code un-executable.

*

* For example:eval('some code')

* Becomes:eval('some code')

*/

$str = preg_replace('#(alert|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(s*)((.*"12(3)", $str);

// Final clean up

// This adds a bit of extra precaution in case

// something got through the above filters

$str = $this->_do_never_allowed($str);

/*

* Images are Handled in a Special Way

* - Essentially, we want to know that after all of the character

* conversion is done whether any unwanted, likely XSS, code was found.

* If not, we return TRUE, as the image is clean.

* However, if the string post-conversion does not matched the

* string post-removal of XSS, then it fails, as there was unwanted XSS

* code found and removed/changed during processing.

*/

if ($is_image === TRUE)

{

return ($str == $converted_string) ? TRUE: FALSE;

}

log_message('debug', "XSS Filtering completed");

return $str;

}

// --------------------------------------------------------------------

//保护url的随机hash值

public function xss_hash()

{

if ($this->_xss_hash == '')

{

mt_srand();

$this->_xss_hash = md5(time() + mt_rand(0, 1999999999));

}

return $this->_xss_hash;

}

// --------------------------------------------------------------------

/**

* html实体转码

*/

public function entity_decode($str, $charset='UTF-8')

{

if (stristr($str, '&') === FALSE)

{

return $str;

}

$str = html_entity_decode($str, ENT_COMPAT, $charset);

$str = preg_replace('~&#x(0*[0-9a-f]{2,5})~ei', 'chr(hexdec("1"))', $str);

return preg_replace('~&#([0-9]{2,4})~e', 'chr(1)', $str);

}

// --------------------------------------------------------------------

//过滤文件名,保证文件名安全

public function sanitize_filename($str, $relative_path = FALSE)

{

$bad = array(

"../",

"<!--",

"-->",

"<",

">",

"'",

'"',

'&',

'$',

'#',

'{',

'}',

'[',

']',

'=',

';',

'?',

"%20",

"%22",

"%3c",// <

"%253c",// <

"%3e",// >

"%0e",// >

"%28",// (

"%29",// )

"%2528",// (

"%26",// &

"%24",// $

"%3f",// ?

"%3b",// ;

"%3d"// =

);

if ( ! $relative_path)

{

$bad[] = './';

$bad[] = '/';

}

$str = remove_invisible_characters($str, FALSE);

return stripslashes(str_replace($bad, '', $str));

}

//压缩单词如j a v a s c r i p t成javascript

protected function _compact_exploded_words($matches)

{

return preg_replace('/s+/s', '', $matches[1]).$matches[2];

}

// --------------------------------------------------------------------

/*

* 去掉一些危害的html属性

*/

protected function _remove_evil_attributes($str, $is_image)

{

// All javascript event handlers (e.g. onload, onclick, onmouseover), style, and xmlns

$evil_attributes = array('onw*', 'style', 'xmlns', 'formaction');

if ($is_image === TRUE)

{

/*

* Adobe Photoshop puts XML metadata into JFIF images,

* including namespacing, so we have to allow this for images.

*/

unset($evil_attributes[array_search('xmlns', $evil_attributes)]);

}

do {

$count = 0;

$attribs = array();

// find occurrences of illegal attribute strings with quotes (042 and 047 are octal quotes)

preg_match_all('/('.implode('|', $evil_attributes).')s*=s*(42|47)([^2]*"file://2)/is'">2)/is', $str, $matches, PREG_SET_ORDER);

foreach ($matches as $attr)

{

$attribs[] = preg_quote($attr[0], '/');

}

// find occurrences of illegal attribute strings without quotes

preg_match_all('/('.implode('|', $evil_attributes).')s*=s*([^s>]*)/is', $str, $matches, PREG_SET_ORDER);

foreach ($matches as $attr)

{

$attribs[] = preg_quote($attr[0], '/');

}

// replace illegal attribute strings that are inside an html tag

if (count($attribs) > 0)

{

$str = preg_replace('/(<?)(/?[^><]+?)([^A-Za-z<>-])(.*?)('.implode('|', $attribs).')(.*?)([s><]?)([><]*)/i', '$1$2 $4$6$7$8', $str, -1, $count);

}

} while ($count);

return $str;

}

// --------------------------------------------------------------------

/**

* 净化html,补齐未关闭的标签

*/

protected function _sanitize_naughty_html($matches)

{

// encode opening brace

$str = '<'.$matches[1].$matches[2].$matches[3];

// encode captured opening or closing brace to prevent recursive vectors

$str .= str_replace(array('>', '<'), array('>', '<'),

$matches[4]);

return $str;

}

// --------------------------------------------------------------------

/**

* 过滤超链接中js

*/

protected function _js_link_removal($match)

{

return str_replace(

$match[1],

preg_replace(

'#href=.*?(alert(|alert(|javascript:|livescript:|mocha:|charset=|window.|document.|.cookie|<script|<xss|datas*:)#si',

'',

$this->_filter_attributes(str_replace(array('<', '>'), '', $match[1]))

),

$match[0]

);

}

// --------------------------------------------------------------------

/**

* 过滤图片链接中的js

*/

protected function _js_img_removal($match)

{

return str_replace(

$match[1],

preg_replace(

'#src=.*?(alert(|alert(|javascript:|livescript:|mocha:|charset=|window.|document.|.cookie|<script|<xss|base64s*,)#si',

'',

$this->_filter_attributes(str_replace(array('<', '>'), '', $match[1]))

),

$match[0]

);

}

// --------------------------------------------------------------------

/**

* 转换属性,将一些字符转换成实体

*/

protected function _convert_attribute($match)

{

return str_replace(array('>', '<', ''), array('>', '<', ''), $match[0]);

}

// --------------------------------------------------------------------

//过滤html标签属性

protected function _filter_attributes($str)

{

$out = '';

if (preg_match_all('#s*[a-z-]+s*=s*(42|47)([^1]*?)1#is', $str, $matches))

{

foreach ($matches[0] as $match)

{

$out .= preg_replace("#/*.*", '', $match);

}

}

return $out;

}

// --------------------------------------------------------------------

//html实体转码

protected function _decode_entity($match)

{

return $this->entity_decode($match[0], strtoupper(config_item('charset')));

}

// --------------------------------------------------------------------

/**

* 验证url实体

*/

protected function _validate_entities($str)

{

/*

* Protect GET variables in URLs

*/

// 901119URL5918AMP18930PROTECT8198

$str = preg_replace('|&([a-z_0-9-]+)=([a-z_0-9-]+)|i', $this->xss_hash()."1=2", $str);

/*

* Validate standard character entities

*

* Add a semicolon if missing. We do this to enable

* the conversion of entities to ASCII later.

*

*/

$str = preg_replace('#(&#?[0-9a-z]{2,})([x00-x20])*;"file://1;2">1;2", $str);

/*

* Validate UTF16 two byte encoding (x00)

*

* Just as above, adds a semicolon if missing.

*

*/

$str = preg_replace('#(&#x?)([0-9A-F]+);"file://12;",$str">12;",$str);

/*

* Un-Protect GET variables in URLs

*/

$str = str_replace($this->xss_hash(), '&', $str);

return $str;

}

// ----------------------------------------------------------------------

//过滤不允许出现的字符串

protected function _do_never_allowed($str)

{

$str = str_replace(array_keys($this->_never_allowed_str), $this->_never_allowed_str, $str);

foreach ($this->_never_allowed_regex as $regex)

{

$str = preg_replace('#'.$regex.'#is', '[removed]', $str);

}

return $str;

}

// --------------------------------------------------------------------

//设置csrf的hash值

protected function _csrf_set_hash()

{

if ($this->_csrf_hash == '')

{

// 如果_csrf_cookie_name存在,直接作为csrf hash值

if (isset($_COOKIE[$this->_csrf_cookie_name]) &&

preg_match('#^[0-9a-f]{32}$#iS', $_COOKIE[$this->_csrf_cookie_name]) === 1)

{

return $this->_csrf_hash = $_COOKIE[$this->_csrf_cookie_name];

}

//否则随机一个md5字符串

return $this->_csrf_hash = md5(uniqid(rand(), TRUE));

}

return $this->_csrf_hash;

}

}

【CI框架安全类Security.php源码分析】相关文章:

php实现的mongodb操作类实例

汉字转化为拼音(php版)

提问的智慧(2)

在PHP中使用XML

BBS(php & mysql)完整版(一)

PHP session文件独占锁引起阻塞问题解决方法

php实现修改新闻时删除图片的方法

递归实现php数组转xml的代码分享

PHP的MVC框架 深入解析

提升PHP执行速度全攻略

精品推荐
分类导航