当前位置:首页 > CMS教程 > 其它CMS > 列表

CI框架安全类Security.php源码分析

发布:smiling 来源: PHP粉丝网  添加日期:2021-04-23 10:13:05 浏览: 评论:0 

之前我们分析了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脚本标签强制转成实体对象

代码如下:

  1. <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 
  2. /** 
  3.  * 安全类 
  4.  */ 
  5. class CI_Security { 
  6.  //url的随机hash值 
  7.  protected $_xss_hash   = ''
  8.  //防csrf攻击的cookie标记的哈希值  
  9.  protected $_csrf_hash   = ''
  10.  //防csrf cookie过期时间 
  11.  protected $_csrf_expire   = 7200; 
  12.  //防csrf的cookie名称 
  13.  protected $_csrf_token_name  = 'ci_csrf_token'
  14.  //防csrf的token名称 
  15.  protected $_csrf_cookie_name = 'ci_csrf_token'
  16.  //不允许出现的字符串数组 
  17.  protected $_never_allowed_str = array
  18.   'document.cookie' => '[removed]'
  19.   'document.write' => '[removed]'
  20.   '.parentNode'  => '[removed]'
  21.   '.innerHTML'  => '[removed]'
  22.   'window.location' => '[removed]'
  23.   '-moz-binding'  => '[removed]'
  24.   '<!--'    => '<!--'
  25.   '-->'    => '-->'
  26.   '<![CDATA['   => '<![CDATA['
  27.   '<comment>'   => '<comment>' 
  28.  ); 
  29.  //不允许出现的正则表达式数组 
  30.  protected $_never_allowed_regex = array
  31.   'javascript\s*:'
  32.   'expression\s*(\(|&\#40;)'// CSS and IE 
  33.   'vbscript\s*:'// IE, surprise! 
  34.   'Redirect\s+302'
  35.   "([\"'])?data\s*:[^\\1]*?base64[^\\1]*?,[^\\1]*?\\1?" 
  36.  ); 
  37.  //构造函数 
  38.  public function __construct() 
  39.  { 
  40.   // CSRF保护是否开启 
  41.   if (config_item('csrf_protection') === TRUE) 
  42.   { 
  43.    // CSRF配置 
  44.    foreach (array('csrf_expire''csrf_token_name''csrf_cookie_name'as $key
  45.    { 
  46.     if (FALSE !== ($val = config_item($key))) 
  47.     { 
  48.      $this->{'_'.$key} = $val
  49.     } 
  50.    } 
  51.    // _csrf_cookie_name加上cookie前缀 
  52.    if (config_item('cookie_prefix')) 
  53.    { 
  54.     $this->_csrf_cookie_name = config_item('cookie_prefix').$this->_csrf_cookie_name; 
  55.    } 
  56.    // 设置csrf的hash值 
  57.    $this->_csrf_set_hash(); 
  58.   } 
  59.   log_message('debug'"Security Class Initialized"); 
  60.  } 
  61.  // -------------------------------------------------------------------- 
  62.  /** 
  63.   * Verify Cross Site Request Forgery Protection 
  64.   * 
  65.   * @return object 
  66.   */ 
  67.  public function csrf_verify() 
  68.  { 
  69.   // 如果不是post请求,则设置csrf的cookie值 
  70.   if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST'
  71.   { 
  72.    return $this->csrf_set_cookie(); 
  73.   } 
  74.   // Do the tokens exist in both the _POST and _COOKIE arrays? 
  75.   if ( ! isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name])) 
  76.   { 
  77.    $this->csrf_show_error(); 
  78.   } 
  79.   // token匹配吗 
  80.   if ($_POST[$this->_csrf_token_name] != $_COOKIE[$this->_csrf_cookie_name]) 
  81.   { 
  82.    $this->csrf_show_error(); 
  83.   } 
  84.   // We kill this since we're done and we don't want to 
  85.   // polute the _POST array 
  86.   unset($_POST[$this->_csrf_token_name]); 
  87.   // Nothing should last forever 
  88.   unset($_COOKIE[$this->_csrf_cookie_name]); 
  89.   $this->_csrf_set_hash(); 
  90.   $this->csrf_set_cookie(); 
  91.   log_message('debug''CSRF token verified'); 
  92.   return $this
  93.  } 
  94.  // -------------------------------------------------------------------- 
  95.  /** 
  96.   * 设置csrf的cookie值 
  97.   */ 
  98.  public function csrf_set_cookie() 
  99.  { 
  100.   $expire = time() + $this->_csrf_expire; 
  101.   $secure_cookie = (config_item('cookie_secure') === TRUE) ? 1 : 0; 
  102.   if ($secure_cookie && (emptyempty($_SERVER['HTTPS']) OR strtolower($_SERVER['HTTPS']) === 'off')) 
  103.   { 
  104.    return FALSE; 
  105.   } 
  106.   setcookie($this->_csrf_cookie_name, $this->_csrf_hash, $expire, config_item('cookie_path'), config_item('cookie_domain'), $secure_cookie); 
  107.   log_message('debug'"CRSF cookie Set"); 
  108.   return $this
  109.  } 
  110.  //csrf保存 
  111.  public function csrf_show_error() 
  112.  { 
  113.   show_error('The action you have requested is not allowed.'); 
  114.  } 
  115.  //获取csrf的hash值 
  116.  public function get_csrf_hash() 
  117.  { 
  118.   return $this->_csrf_hash; 
  119.  } 
  120.  //获取csrf的token值 
  121.  public function get_csrf_token_name() 
  122.  { 
  123.   return $this->_csrf_token_name; 
  124.  } 
  125.  /** 
  126.   * XSS 过滤 
  127.   */ 
  128.  public function xss_clean($str$is_image = FALSE) 
  129.  { 
  130.   //是否是数组 
  131.   if (is_array($str)) 
  132.   { 
  133.    while (list($key) = each($str)) 
  134.    { 
  135.     $str[$key] = $this->xss_clean($str[$key]); 
  136.    } 
  137.    return $str
  138.   } 
  139.   //去掉可见字符串 
  140.   $str = remove_invisible_characters($str); 
  141.   // 验证实体url 
  142.   $str = $this->_validate_entities($str); 
  143.   /* 
  144.    * URL 解码 
  145.    * 
  146.    * Just in case stuff like this is submitted: 
  147.    * 
  148.    * <a href="http://%77%77%77%2E%67%6F%6F%67%6C%65%2E%63%6F%6D">Google</a> 
  149.    * 
  150.    * Note: Use rawurldecode() so it does not remove plus signs 
  151.    * 
  152.    */ 
  153.   $str = rawurldecode($str); 
  154.   /* 
  155.    * Convert character entities to ASCII 
  156.    * 
  157.    * This permits our tests below to work reliably. 
  158.    * We only convert entities that are within tags since 
  159.    * these are the ones that will pose security problems. 
  160.    * 
  161.    */ 
  162.   $str = preg_replace_callback("/[a-z]+=([\'\"]).*?\\1/si"array($this, '_convert_attribute'), $str); 
  163.   $str = preg_replace_callback("/<\w+.*?(?=>|<|$)/si"array($this'_decode_entity'), $str); 
  164.   /* 
  165.    * Remove Invisible Characters Again! 
  166.    */ 
  167.   $str = remove_invisible_characters($str); 
  168.   /* 
  169.    * Convert all tabs to spaces 
  170.    * 
  171.    * This prevents strings like this: ja vascript 
  172.    * NOTE: we deal with spaces between characters later. 
  173.    * NOTE: preg_replace was found to be amazingly slow here on 
  174.    * large blocks of data, so we use str_replace. 
  175.    */ 
  176.   if (strpos($str"\t") !== FALSE) 
  177.   { 
  178.    $str = str_replace("\t"' '$str); 
  179.   } 
  180.   /* 
  181.    * Capture converted string for later comparison 
  182.    */ 
  183.   $converted_string = $str
  184.   // Remove Strings that are never allowed 
  185.   $str = $this->_do_never_allowed($str); 
  186.   /* 
  187.    * Makes PHP tags safe 
  188.    * 
  189.    * Note: XML tags are inadvertently replaced too: 
  190.    * 
  191.    * <?xml 
  192.    * 
  193.    * But it doesn't seem to pose a problem. 
  194.    */ 
  195.   if ($is_image === TRUE) 
  196.   { 
  197.    // Images have a tendency to have the PHP short opening and 
  198.    // closing tags every so often so we skip those and only 
  199.    // do the long opening tags. 
  200.    $str = preg_replace('/<\?(php)/i'"<?\\1"$str); 
  201.   } 
  202.   else 
  203.   { 
  204.    $str = str_replace(array('<?''?'.'>'),  array('<?''?>'), $str); 
  205.   } 
  206.   /* 
  207.    * Compact any exploded words 
  208.    * 
  209.    * This corrects words like:  j a v a s c r i p t 
  210.    * These words are compacted back to their correct state. 
  211.    */ 
  212.   $words = array
  213.    'javascript''expression''vbscript''script''base64'
  214.    'applet''alert''document''write''cookie''window' 
  215.   ); 
  216.   foreach ($words as $word
  217.   { 
  218.    $temp = ''
  219.    for ($i = 0, $wordlen = strlen($word); $i < $wordlen$i++) 
  220.    { 
  221.     $temp .= substr($word$i, 1)."\s*"
  222.    } 
  223.    // We only want to do this when it is followed by a non-word character 
  224.    // That way valid stuff like "dealer to" does not become "dealerto" 
  225.    $str = preg_replace_callback('#('.substr($temp, 0, -3).')(\W)#is'array($this'_compact_exploded_words'), $str); 
  226.   } 
  227.   /* 
  228.    * Remove disallowed Javascript in links or img tags 
  229.    * We used to do some version comparisons and use of stripos for PHP5, 
  230.    * but it is dog slow compared to these simplified non-capturing 
  231.    * preg_match(), especially if the pattern exists in the string 
  232.    */ 
  233.   do 
  234.   { 
  235.    $original = $str
  236.    if (preg_match("/<a/i"$str)) 
  237.    { 
  238.     $str = preg_replace_callback("#<a\s+([^>]*?)(>|$)#si"array($this'_js_link_removal'), $str); 
  239.    } 
  240.    if (preg_match("/<img/i"$str)) 
  241.    { 
  242.     $str = preg_replace_callback("#<img\s+([^>]*?)(\s?/?>|$)#si"array($this'_js_img_removal'), $str); 
  243.    } 
  244.    if (preg_match("/script/i"$str) OR preg_match("/xss/i"$str)) 
  245.    { 
  246.     $str = preg_replace("#<(/*)(script|xss)(.*?)\>#si"'[removed]'$str); 
  247.    } 
  248.   } 
  249.   while($original != $str); 
  250.   unset($original); 
  251.   // Remove evil attributes such as style, onclick and xmlns 
  252.   $str = $this->_remove_evil_attributes($str$is_image); 
  253.   /* 
  254.    * Sanitize naughty HTML elements 
  255.    * 
  256.    * If a tag containing any of the words in the list 
  257.    * below is found, the tag gets converted to entities. 
  258.    * 
  259.    * So this: <blink> 
  260.    * Becomes: <blink> 
  261.    */ 
  262.   $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'
  263.   $str = preg_replace_callback('#<(/*\s*)('.$naughty.')([^><]*)([><]*)#is'array($this'_sanitize_naughty_html'), $str); 
  264.   /* 
  265.    * Sanitize naughty scripting elements 
  266.    * 
  267.    * Similar to above, only instead of looking for 
  268.    * tags it looks for PHP and JavaScript commands 
  269.    * that are disallowed.  Rather than removing the 
  270.    * code, it simply converts the parenthesis to entities 
  271.    * rendering the code un-executable. 
  272.    * 
  273.    * For example: eval('some code'
  274.    * Becomes:  eval('some code'
  275.    */ 
  276.   $str = preg_replace('#(alert|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si'"\\1\\2(\\3)"$str); 
  277.   // Final clean up 
  278.   // This adds a bit of extra precaution in case 
  279.   // something got through the above filters 
  280.   $str = $this->_do_never_allowed($str); 
  281.   /* 
  282.    * Images are Handled in a Special Way 
  283.    * - Essentially, we want to know that after all of the character 
  284.    * conversion is done whether any unwanted, likely XSS, code was found. 
  285.    * If not, we return TRUE, as the image is clean. 
  286.    * However, if the string post-conversion does not matched the 
  287.    * string post-removal of XSS, then it fails, as there was unwanted XSS 
  288.    * code found and removed/changed during processing. 
  289.    */ 
  290.   if ($is_image === TRUE) 
  291.   { 
  292.    return ($str == $converted_string) ? TRUE: FALSE; 
  293.   } 
  294.   log_message('debug'"XSS Filtering completed"); 
  295.   return $str
  296.  } 
  297.  // -------------------------------------------------------------------- 
  298.  //保护url的随机hash值 
  299.  public function xss_hash() 
  300.  { 
  301.   if ($this->_xss_hash == ''
  302.   { 
  303.    mt_srand(); 
  304.    $this->_xss_hash = md5(time() + mt_rand(0, 1999999999)); 
  305.   } 
  306.   return $this->_xss_hash; 
  307.  } 
  308.  // -------------------------------------------------------------------- 
  309.  /** 
  310.   * html实体转码 
  311.   */ 
  312.  public function entity_decode($str$charset='UTF-8'
  313.  { 
  314.   if (stristr($str'&') === FALSE) 
  315.   { 
  316.    return $str
  317.   } 
  318.   $str = html_entity_decode($str, ENT_COMPAT, $charset); 
  319.   $str = preg_replace('~&#x(0*[0-9a-f]{2,5})~ei''chr(hexdec("\\1"))'$str); 
  320.   return preg_replace('~&#([0-9]{2,4})~e''chr(\\1)'$str); 
  321.  } 
  322.  // -------------------------------------------------------------------- 
  323.  //过滤文件名,保证文件名安全 
  324.  public function sanitize_filename($str$relative_path = FALSE) 
  325.  { 
  326.   $bad = array
  327.    "../"
  328.    "<!--"
  329.    "-->"
  330.    "<"
  331.    ">"
  332.    "'"
  333.    '"'
  334.    '&'
  335.    '$'
  336.    '#'
  337.    '{'
  338.    '}'
  339.    '['
  340.    ']'
  341.    '='
  342.    ';'
  343.    '?'
  344.    "%20"
  345.    "%22"
  346.    "%3c",  // < 
  347.    "%253c"// < 
  348.    "%3e",  // > 
  349.    "%0e",  // > 
  350.    "%28",  // ( 
  351.    "%29",  // ) 
  352.    "%2528"// ( 
  353.    "%26",  // & 
  354.    "%24",  // $ 
  355.    "%3f",  // ? 
  356.    "%3b",  // ; 
  357.    "%3d"  // = 
  358.   ); 
  359.   if ( ! $relative_path
  360.   { 
  361.    $bad[] = './'
  362.    $bad[] = '/'
  363.   } 
  364.   $str = remove_invisible_characters($str, FALSE); 
  365.   return stripslashes(str_replace($bad''$str)); 
  366.  } 
  367.  //压缩单词如j a v a s c r i p t成javascript 
  368.  protected function _compact_exploded_words($matches
  369.  { 
  370.   return preg_replace('/\s+/s'''$matches[1]).$matches[2]; 
  371.  } 
  372.  // -------------------------------------------------------------------- 
  373.  /* 
  374.   * 去掉一些危害的html属性 
  375.   */ 
  376.  protected function _remove_evil_attributes($str$is_image
  377.  { 
  378.   // All javascript event handlers (e.g. onload, onclick, onmouseover), style, and xmlns 
  379.   $evil_attributes = array('on\w*''style''xmlns''formaction'); 
  380.   if ($is_image === TRUE) 
  381.   { 
  382.    /* 
  383.     * Adobe Photoshop puts XML metadata into JFIF images, 
  384.     * including namespacing, so we have to allow this for images. 
  385.     */ 
  386.    unset($evil_attributes[array_search('xmlns'$evil_attributes)]); 
  387.   } 
  388.   do { 
  389.    $count = 0; 
  390.    $attribs = array(); 
  391.    // find occurrences of illegal attribute strings with quotes (042 and 047 are octal quotes) 
  392.    preg_match_all('/('.implode('|'$evil_attributes).')\s*=\s*(\042|\047)([^\\2]*?)(\\2)/is'$str$matches, PREG_SET_ORDER); 
  393.    foreach ($matches as $attr
  394.    { 
  395.     $attribs[] = preg_quote($attr[0], '/'); 
  396.    } 
  397.    // find occurrences of illegal attribute strings without quotes 
  398.    preg_match_all('/('.implode('|'$evil_attributes).')\s*=\s*([^\s>]*)/is'$str$matches, PREG_SET_ORDER); 
  399.    foreach ($matches as $attr
  400.    { 
  401.     $attribs[] = preg_quote($attr[0], '/'); 
  402.    } 
  403.    // replace illegal attribute strings that are inside an html tag 
  404.    if (count($attribs) > 0) 
  405.    { 
  406.     $str = preg_replace('/(<?)(\/?[^><]+?)([^A-Za-z<>\-])(.*?)('.implode('|'$attribs).')(.*?)([\s><]?)([><]*)/i''$1$2 $4$6$7$8'$str, -1, $count); 
  407.    } 
  408.   } while ($count); 
  409.   return $str
  410.  } 
  411.  // -------------------------------------------------------------------- 
  412.  /** 
  413.   * 净化html,补齐未关闭的标签 
  414.   */ 
  415.  protected function _sanitize_naughty_html($matches
  416.  { 
  417.   // encode opening brace 
  418.   $str = '<'.$matches[1].$matches[2].$matches[3]; 
  419.   // encode captured opening or closing brace to prevent recursive vectors 
  420.   $str .= str_replace(array('>''<'), array('>''<'), 
  421.        $matches[4]); 
  422.   return $str
  423.  } 
  424.  // -------------------------------------------------------------------- 
  425.  /** 
  426.   * 过滤超链接中js 
  427.   */ 
  428.  protected function _js_link_removal($match
  429.  { 
  430.   return str_replace
  431.    $match[1], 
  432.    preg_replace( 
  433.     '#href=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|<script|<xss|data\s*:)#si'
  434.     ''
  435.     $this->_filter_attributes(str_replace(array('<''>'), ''$match[1])) 
  436.    ), 
  437.    $match[0] 
  438.   ); 
  439.  } 
  440.  // -------------------------------------------------------------------- 
  441.  /** 
  442.   * 过滤图片链接中的js 
  443.   */ 
  444.  protected function _js_img_removal($match
  445.  { 
  446.   return str_replace
  447.    $match[1], 
  448.    preg_replace( 
  449.     '#src=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)#si'
  450.     ''
  451.     $this->_filter_attributes(str_replace(array('<''>'), ''$match[1])) 
  452.    ), 
  453.    $match[0] 
  454.   ); 
  455.  } 
  456.  // -------------------------------------------------------------------- 
  457.  /** 
  458.   * 转换属性,将一些字符转换成实体 
  459.   */ 
  460.  protected function _convert_attribute($match
  461.  { 
  462.   return str_replace(array('>''<''\\'), array('>', '<', '\\\\'), $match[0]); 
  463.  } 
  464.  // -------------------------------------------------------------------- 
  465.  //过滤html标签属性 
  466.  protected function _filter_attributes($str
  467.  { 
  468.   $out = ''
  469.   if (preg_match_all('#\s*[a-z\-]+\s*=\s*(\042|\047)([^\\1]*?)\\1#is'$str$matches)) 
  470.   { 
  471.    foreach ($matches[0] as $match
  472.    { 
  473.     $out .= preg_replace("#/\*.*?\*/#s"''$match); 
  474.    } 
  475.   } 
  476.   return $out
  477.  } 
  478.  // -------------------------------------------------------------------- 
  479.  //html实体转码 
  480.  protected function _decode_entity($match
  481.  { 
  482.   return $this->entity_decode($match[0], strtoupper(config_item('charset'))); 
  483.  } 
  484.  // -------------------------------------------------------------------- 
  485.  /** 
  486.   * 验证url实体 
  487.   */ 
  488.  protected function _validate_entities($str
  489.  { 
  490.   /* 
  491.    * Protect GET variables in URLs 
  492.    */ 
  493.    // 901119URL5918AMP18930PROTECT8198 
  494.   $str = preg_replace('|\&([a-z\_0-9\-]+)\=([a-z\_0-9\-]+)|i'$this->xss_hash()."\\1=\\2"$str); 
  495.   /* 
  496.    * Validate standard character entities 
  497.    * 
  498.    * Add a semicolon if missing.  We do this to enable 
  499.    * the conversion of entities to ASCII later. 
  500.    * 
  501.    */ 
  502.   $str = preg_replace('#(&\#?[0-9a-z]{2,})([\x00-\x20])*;?#i'"\\1;\\2"$str); 
  503.   /* 
  504.    * Validate UTF16 two byte encoding (x00) 
  505.    * 
  506.    * Just as above, adds a semicolon if missing. 
  507.    * 
  508.    */ 
  509.   $str = preg_replace('#(&\#x?)([0-9A-F]+);?#i',"\\1\\2;",$str); 
  510.   /* 
  511.    * Un-Protect GET variables in URLs 
  512.    */ 
  513.   $str = str_replace($this->xss_hash(), '&'$str); 
  514.   return $str
  515.  } 
  516.  // ---------------------------------------------------------------------- 
  517.  //过滤不允许出现的字符串 
  518.  protected function _do_never_allowed($str
  519.  { 
  520.   $str = str_replace(array_keys($this->_never_allowed_str), $this->_never_allowed_str, $str); 
  521.   foreach ($this->_never_allowed_regex as $regex
  522.   { 
  523.    $str = preg_replace('#'.$regex.'#is''[removed]'$str); 
  524.   } 
  525.   return $str
  526.  } 
  527.  // -------------------------------------------------------------------- 
  528.  //设置csrf的hash值 
  529.  protected function _csrf_set_hash() 
  530.  { 
  531.   if ($this->_csrf_hash == ''
  532.   { 
  533.    // 如果_csrf_cookie_name存在,直接作为csrf hash值 
  534.    if (isset($_COOKIE[$this->_csrf_cookie_name]) && 
  535.     preg_match('#^[0-9a-f]{32}$#iS'$_COOKIE[$this->_csrf_cookie_name]) === 1) 
  536.    { 
  537.     return $this->_csrf_hash = $_COOKIE[$this->_csrf_cookie_name]; 
  538.    }//www.phpfensi.com 
  539.                         //否则随机一个md5字符串 
  540.    return $this->_csrf_hash = md5(uniqid(rand(), TRUE)); 
  541.   } 
  542.   return $this->_csrf_hash; 
  543.  } 

Tags: CI框架安全类 Security php

分享到: