diff options
author | Wes Baker <wes@wesbaker.com> | 2012-04-24 21:17:14 +0200 |
---|---|---|
committer | Wes Baker <wes@wesbaker.com> | 2012-06-29 22:51:01 +0200 |
commit | 9f9fe824a0f687f5a1e9ed2e6fbaba92f90480d1 (patch) | |
tree | 9b79f1d3f03cb4715889435f388c5081437acfa0 | |
parent | ea6cbe026b73be13dd853d710b9609e3faf50f44 (diff) |
Updating XSS cleaning to better handle base64 encoded attributes.
Conflicts:
system/core/Security.php
-rwxr-xr-x | system/core/Security.php | 151 |
1 files changed, 81 insertions, 70 deletions
diff --git a/system/core/Security.php b/system/core/Security.php index 6f5ac1ed8..7af240ded 100755 --- a/system/core/Security.php +++ b/system/core/Security.php @@ -1,4 +1,4 @@ -<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); +<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); /** * CodeIgniter * @@ -69,16 +69,16 @@ class CI_Security { * @access protected */ 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>' + 'document.cookie' => '[removed]', + 'document.write' => '[removed]', + '.parentNode' => '[removed]', + '.innerHTML' => '[removed]', + 'window.location' => '[removed]', + '-moz-binding' => '[removed]', + '<!--' => '<!--', + '-->' => '-->', + '<![CDATA[' => '<![CDATA[', + '<comment>' => '<comment>' ); /* never allowed, regex replacement */ @@ -89,10 +89,11 @@ class CI_Security { * @access protected */ protected $_never_allowed_regex = array( - "javascript\s*:" => '[removed]', - "expression\s*(\(|&\#40;)" => '[removed]', // CSS and IE - "vbscript\s*:" => '[removed]', // IE, surprise! - "Redirect\s+302" => '[removed]' + 'javascript\s*:', + 'expression\s*(\(|&\#40;)', // CSS and IE + 'vbscript\s*:', // IE, surprise! + 'Redirect\s+302', + "([\"'])?data\s*:[^\\1]*?base64[^\\1]*?,[^\\1]*?\\1?" ); /** @@ -364,9 +365,9 @@ class CI_Security { * These words are compacted back to their correct state. */ $words = array( - 'javascript', 'expression', 'vbscript', 'script', - 'applet', 'alert', 'document', 'write', 'cookie', 'window' - ); + 'javascript', 'expression', 'vbscript', 'script', 'base64', + 'applet', 'alert', 'document', 'write', 'cookie', 'window' + ); foreach ($words as $word) { @@ -524,38 +525,38 @@ class CI_Security { public function sanitize_filename($str, $relative_path = FALSE) { $bad = array( - "../", - "<!--", - "-->", - "<", - ">", - "'", - '"', - '&', - '$', - '#', - '{', - '}', - '[', - ']', - '=', - ';', - '?', - "%20", - "%22", - "%3c", // < - "%253c", // < - "%3e", // > - "%0e", // > - "%28", // ( - "%29", // ) - "%2528", // ( - "%26", // & - "%24", // $ - "%3f", // ? - "%3b", // ; - "%3d" // = - ); + "../", + "<!--", + "-->", + "<", + ">", + "'", + '"', + '&', + '$', + '#', + '{', + '}', + '[', + ']', + '=', + ';', + '?', + "%20", + "%22", + "%3c", // < + "%253c", // < + "%3e", // > + "%0e", // > + "%28", // ( + "%29", // ) + "%2528", // ( + "%26", // & + "%24", // $ + "%3f", // ? + "%3b", // ; + "%3d" // = + ); if ( ! $relative_path) { @@ -613,19 +614,20 @@ class CI_Security { */ unset($evil_attributes[array_search('xmlns', $evil_attributes)]); } - + do { $count = 0; $attribs = array(); - + // find occurrences of illegal attribute strings without quotes - preg_match_all("/(".implode('|', $evil_attributes).")\s*=\s*([^\s]*)/is", $str, $matches, PREG_SET_ORDER); - + preg_match_all('/('.implode('|', $evil_attributes).')\s*=\s*([^\s>]*)/is', $str, $matches, PREG_SET_ORDER); + foreach ($matches as $attr) { + $attribs[] = preg_quote($attr[0], '/'); } - + // find occurrences of illegal attribute strings with quotes (042 and 047 are octal quotes) preg_match_all("/(".implode('|', $evil_attributes).")\s*=\s*(\042|\047)([^\\2]*?)(\\2)/is", $str, $matches, PREG_SET_ORDER); @@ -637,11 +639,11 @@ class CI_Security { // 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$5', $str, -1, $count); + $str = preg_replace("/<(\/?[^><]+?)([^A-Za-z<>\-])(.*?)(".implode('|', $attribs).")(.*?)([\s><])([><]*)/i", '<$1 $3$5$6$7', $str, -1, $count); } - + } while ($count); - + return $str; } @@ -682,9 +684,15 @@ class CI_Security { */ protected function _js_link_removal($match) { - $attributes = $this->_filter_attributes(str_replace(array('<', '>'), '', $match[1])); - - return str_replace($match[1], preg_replace("#href=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)#si", "", $attributes), $match[0]); + 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] + ); } // -------------------------------------------------------------------- @@ -702,9 +710,15 @@ class CI_Security { */ protected function _js_img_removal($match) { - $attributes = $this->_filter_attributes(str_replace(array('<', '>'), '', $match[1])); - - return str_replace($match[1], preg_replace("#src=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)#si", "", $attributes), $match[0]); + 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] + ); } // -------------------------------------------------------------------- @@ -819,14 +833,11 @@ class CI_Security { */ protected function _do_never_allowed($str) { - foreach ($this->_never_allowed_str as $key => $val) - { - $str = str_replace($key, $val, $str); - } + $str = str_replace(array_keys($this->_never_allowed_str), $this->_never_allowed_str, $str); - foreach ($this->_never_allowed_regex as $key => $val) + foreach ($this->_never_allowed_regex as $regex) { - $str = preg_replace("#".$key."#i", $val, $str); + $str = preg_replace('#'.$regex.'#is', '[removed]', $str); } return $str; |