CI框架安全类Security.php源码分析
网络编程 2021-07-05 09:50www.168986.cn编程入门
之前我们分析了CI框架的session类session.php,本文我们继续分析CI框架的安全类security.php文件,方便我们更详细的了解CI框架,从而更熟练的应用CI框架
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 aess 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[',
'<ment>' => '<ment>'
);
//不允许出现的正则表达式数组
protected $_never_allowed_regex = array(
'javascript\s:',
'expression\s(\(|&\#40;)', // CSS and IE
'vbscript\s:', // IE, surprise!
'Redirect\s+302',
"([\"'])?data\s:[^\\1]?base64[^\\1]?,[^\\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 Fery Protection
@return object
/
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=">
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]+=([\'\"]).?\\1/si", 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: ja vascript
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 parison
/
$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('/<\?(php)/i', "<?\\1", $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 pacted 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 bee "dealerto"
$str = preg_replace_callback('#('.substr($temp, 0, -3).')(\W)#is', array($this, '_pact_exploded_words'), $str);
}
/
Remove disallowed Javascript in links or img tags
We used to do some version parisons and use of stripos for PHP5,
but it is dog slow pared 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("#<a\s+([^>]?)(>|$)#si", array($this, '_js_link_removal'), $str);
}
if (preg_match("/<img/i", $str))
{
$str = preg_replace_callback("#<img\s+([^>]?)(\s?/?>|$)#si", array($this, '_js_img_removal'), $str);
}
if (preg_match("/script/i", $str) OR preg_match("/xss/i", $str))
{
$str = preg_replace("#<(/)(script|xss)(.?)\>#si", '[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>
Bees: <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 mands
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')
Bees: eval('some code')
/
$str = preg_replace('#(alert|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s)\((.?)\)#si', ")", $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 pleted");
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(", $str);
return preg_replace('~&#([0-9]{2,4})~e', 'chr(, $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 _pact_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('on\w', '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 ourrences of illegal attribute strings with quotes (042 and 047 are octal quotes)
preg_match_all('/('.implode('|', $evil_attributes).')\s=\s(\042|\047)([^\\2]?)(, $str, $matches, PREG_SET_ORDER);
foreach ($matches as $attr)
{
$attribs[] = preg_quote($attr[0], '/');
}
// find ourrences 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&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|<script|<xss|data\s:)#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&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|<script|<xss|base64\s,)#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(\042|\047)([^\\1]?)\\1#is', $str, $matches))
{
foreach ($matches[0] as $match)
{
$out .= preg_replace("#/\.?\/#s", '', $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()."", $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]);?#i', "", $str);
/
Validate UTF16 two byte encoding (x00)
Just as above, adds a semicolon if missing.
/
$str = preg_replace('#(&\#x?)([0-9A-F]+);?#i',");
/
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;
}
}
编程语言
- 如何快速学会编程 如何快速学会ug编程
- 免费学编程的app 推荐12个免费学编程的好网站
- 电脑怎么编程:电脑怎么编程网咯游戏菜单图标
- 如何写代码新手教学 如何写代码新手教学手机
- 基础编程入门教程视频 基础编程入门教程视频华
- 编程演示:编程演示浦丰投针过程
- 乐高编程加盟 乐高积木编程加盟
- 跟我学plc编程 plc编程自学入门视频教程
- ug编程成航林总 ug编程实战视频
- 孩子学编程的好处和坏处
- 初学者学编程该从哪里开始 新手学编程从哪里入
- 慢走丝编程 慢走丝编程难学吗
- 国内十强少儿编程机构 中国少儿编程机构十强有
- 成人计算机速成培训班 成人计算机速成培训班办
- 孩子学编程网上课程哪家好 儿童学编程比较好的
- 代码编程教学入门软件 代码编程教程