Permalink
Browse files

Updates Codeigniter to 2.2.6

  • Loading branch information...
1 parent ad9c943 commit 94d43c31ebe80cf3397a8d0b8e5b2fa87a3c3143 @Kovah Kovah committed Jun 7, 2016
Showing with 195 additions and 81 deletions.
  1. +1 −1 system/core/CodeIgniter.php
  2. +2 −4 system/core/Config.php
  3. +96 −69 system/core/Security.php
  4. +96 −7 system/helpers/captcha_helper.php
@@ -34,7 +34,7 @@
* @var string
*
*/
- define('CI_VERSION', '2.2.4');
+ define('CI_VERSION', '2.2.6');
/**
* CodeIgniter Branch (Core = TRUE, Reactor = FALSE)
@@ -67,12 +67,10 @@ function __construct()
// Set the base_url automatically if none was provided
if ($this->config['base_url'] == '')
{
- // The regular expression is only a basic validation for a valid "Host" header.
- // It's not exhaustive, only checks for valid characters.
- if (isset($_SERVER['HTTP_HOST']) && preg_match('/^((\[[0-9a-f:]+\])|(\d{1,3}(\.\d{1,3}){3})|[a-z0-9\-\.]+)(:\d+)?$/i', $_SERVER['HTTP_HOST']))
+ if (isset($_SERVER['SERVER_ADDR']))
{
$base_url = (empty($_SERVER['HTTPS']) OR strtolower($_SERVER['HTTPS']) === 'off') ? 'http' : 'https';
- $base_url .= '://'. $_SERVER['HTTP_HOST'];
+ $base_url .= '://'.$_SERVER['SERVER_ADDR'];
$base_url .= substr($_SERVER['SCRIPT_NAME'], 0, strpos($_SERVER['SCRIPT_NAME'], basename($_SERVER['SCRIPT_FILENAME'])));
}
@@ -355,7 +355,7 @@ public function xss_clean($str, $is_image = FALSE)
$words = array(
'javascript', 'expression', 'vbscript', 'jscript', 'wscript',
'vbs', 'script', 'base64', 'applet', 'alert', 'document',
- 'write', 'cookie', 'window', 'confirm', 'prompt'
+ 'write', 'cookie', 'window', 'confirm', 'prompt', 'eval'
);
foreach ($words as $word)
@@ -399,12 +399,8 @@ public function xss_clean($str, $is_image = FALSE)
}
}
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
*
@@ -414,8 +410,29 @@ public function xss_clean($str, $is_image = FALSE)
* So this: <blink>
* Becomes: &lt;blink&gt;
*/
- $naughty = 'alert|prompt|confirm|applet|audio|basefont|base|behavior|bgsound|blink|body|embed|expression|form|frameset|frame|head|html|ilayer|iframe|input|button|select|isindex|layer|link|meta|keygen|object|plaintext|style|script|textarea|title|math|video|svg|xml|xss';
- $str = preg_replace_callback('#<(/*\s*)('.$naughty.')([^><]*)([><]*)#is', array($this, '_sanitize_naughty_html'), $str);
+ $pattern = '#'
+ .'<((?<slash>/*\s*)(?<tagName>[a-z0-9]+)(?=[^a-z0-9]|$)' // tag start and name, followed by a non-tag character
+ .'[^\s\042\047a-z0-9>/=]*' // a valid attribute character immediately after the tag would count as a separator
+ // optional attributes
+ .'(?<attributes>(?:[\s\042\047/=]*' // non-attribute characters, excluding > (tag close) for obvious reasons
+ .'[^\s\042\047>/=]+' // attribute characters
+ // optional attribute-value
+ .'(?:\s*=' // attribute-value separator
+ .'(?:[^\s\042\047=><`]+|\s*\042[^\042]*\042|\s*\047[^\047]*\047|\s*(?U:[^\s\042\047=><`]*))' // single, double or non-quoted value
+ .')?' // end optional attribute-value group
+ .')*)' // end optional attributes group
+ .'[^>]*)(?<closeTag>\>)?#isS';
+
+ // Note: It would be nice to optimize this for speed, BUT
+ // only matching the naughty elements here results in
+ // false positives and in turn - vulnerabilities!
+ do
+ {
+ $old_str = $str;
+ $str = preg_replace_callback($pattern, array($this, '_sanitize_naughty_html'), $str);
+ }
+ while ($old_str !== $str);
+ unset($old_str);
/*
* Sanitize naughty scripting elements
@@ -626,82 +643,92 @@ protected function _compact_exploded_words($matches)
// --------------------------------------------------------------------
- /*
- * Remove Evil HTML Attributes (like evenhandlers and style)
+ /**
+ * Sanitize Naughty HTML
*
- * It removes the evil attribute and either:
- * - Everything up until a space
- * For example, everything between the pipes:
- * <a |style=document.write('hello');alert('world');| class=link>
- * - Everything inside the quotes
- * For example, everything between the pipes:
- * <a |style="document.write('hello'); alert('world');"| class="link">
+ * Callback function for xss_clean() to remove naughty HTML elements
*
- * @param string $str The string to check
- * @param boolean $is_image TRUE if this is an image
- * @return string The string with the evil attributes removed
+ * @param array
+ * @return string
*/
- protected function _remove_evil_attributes($str, $is_image)
+ protected function _sanitize_naughty_html($matches)
{
- // All javascript event handlers (e.g. onload, onclick, onmouseover), style, and xmlns
- $evil_attributes = array('on\w*', 'style', 'xmlns', 'formaction', 'form', 'xlink:href', 'FSCommand', 'seekSegmentTime');
+ static $naughty_tags = array(
+ 'alert', 'prompt', 'confirm', 'applet', 'audio', 'basefont', 'base', 'behavior', 'bgsound',
+ 'blink', 'body', 'embed', 'expression', 'form', 'frameset', 'frame', 'head', 'html', 'ilayer',
+ 'iframe', 'input', 'button', 'select', 'isindex', 'layer', 'link', 'meta', 'keygen', 'object',
+ 'plaintext', 'style', 'script', 'textarea', 'title', 'math', 'video', 'svg', 'xml', 'xss'
+ );
- if ($is_image === TRUE)
+ static $evil_attributes = array(
+ 'on\w+', 'style', 'xmlns', 'formaction', 'form', 'xlink:href', 'FSCommand', 'seekSegmentTime'
+ );
+
+ // First, escape unclosed tags
+ if (empty($matches['closeTag']))
+ {
+ return '&lt;'.$matches[1];
+ }
+ // Is the element that we caught naughty? If so, escape it
+ elseif (in_array(strtolower($matches['tagName']), $naughty_tags, 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)]);
+ return '&lt;'.$matches[1].'&gt;';
}
+ // For other tags, see if their attributes are "evil" and strip those
+ elseif (isset($matches['attributes']))
+ {
+ // We'll store the already fitlered attributes here
+ $attributes = array();
- do {
- $count = 0;
- $attribs = array();
+ // Attribute-catching pattern
+ $attributes_pattern = '#'
+ .'(?<name>[^\s\042\047>/=]+)' // attribute characters
+ // optional attribute-value
+ .'(?:\s*=(?<value>[^\s\042\047=><`]+|\s*\042[^\042]*\042|\s*\047[^\047]*\047|\s*(?U:[^\s\042\047=><`]*)))' // attribute-value separator
+ .'#i';
- // find occurrences of illegal attribute strings with quotes (042 and 047 are octal quotes)
- preg_match_all('/(?<!\w)('.implode('|', $evil_attributes).')\s*=\s*(\042|\047)([^\\2]*?)(\\2)/is', $str, $matches, PREG_SET_ORDER);
+ // Blacklist pattern for evil attribute names
+ $is_evil_pattern = '#^('.implode('|', $evil_attributes).')$#i';
- foreach ($matches as $attr)
+ // Each iteration filters a single attribute
+ do
{
- $attribs[] = preg_quote($attr[0], '/');
- }
+ // Strip any non-alpha characters that may preceed an attribute.
+ // Browsers often parse these incorrectly and that has been a
+ // of numerous XSS issues we've had.
+ $matches['attributes'] = preg_replace('#^[^a-z]+#i', '', $matches['attributes']);
- // find occurrences of illegal attribute strings without quotes
- preg_match_all('/(?<!\w)('.implode('|', $evil_attributes).')\s*=\s*([^\s>]*)/is', $str, $matches, PREG_SET_ORDER);
+ if ( ! preg_match($attributes_pattern, $matches['attributes'], $attribute, PREG_OFFSET_CAPTURE))
+ {
+ // No (valid) attribute found? Discard everything else inside the tag
+ break;
+ }
- foreach ($matches as $attr)
- {
- $attribs[] = preg_quote($attr[0], '/');
- }
+ if (
+ // Is it indeed an "evil" attribute?
+ preg_match($is_evil_pattern, $attribute['name'][0])
+ // Or does it have an equals sign, but no value and not quoted? Strip that too!
+ OR (trim($attribute['value'][0]) === '')
+ )
+ {
+ $attributes[] = 'xss=removed';
+ }
+ else
+ {
+ $attributes[] = $attribute[0][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);
+ $matches['attributes'] = substr($matches['attributes'], $attribute[0][1] + strlen($attribute[0][0]));
}
+ while ($matches['attributes'] !== '');
+ $attributes = empty($attributes)
+ ? ''
+ : ' '.implode(' ', $attributes);
+ return '<'.$matches['slash'].$matches['tagName'].$attributes.'>';
}
- while ($count);
-
- return $str;
- }
-
- // --------------------------------------------------------------------
- /**
- * Sanitize Naughty HTML
- *
- * Callback function for xss_clean() to remove naughty HTML elements
- *
- * @param array
- * @return string
- */
- protected function _sanitize_naughty_html($matches)
- {
- return '&lt;'.$matches[1].$matches[2].$matches[3] // encode opening brace
- // encode captured opening or closing brace to prevent recursive vectors:
- .str_replace(array('>', '<'), array('&gt;', '&lt;'), $matches[4]);
+ return $matches[0];
}
// --------------------------------------------------------------------
@@ -724,7 +751,7 @@ protected function _js_link_removal($match)
preg_replace(
'#href=.*?(?:(?:alert|prompt|confirm)(?:\(|&\#40;)|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|<script|<xss|data\s*:)#si',
'',
- $this->_filter_attributes(str_replace(array('<', '>'), '', $match[1]))
+ $this->_filter_attributes($match[1])
),
$match[0]
);
@@ -748,9 +775,9 @@ protected function _js_img_removal($match)
return str_replace(
$match[1],
preg_replace(
- '#src=.*?(?:(?:alert|prompt|confirm)(?:\(|&\#40;)|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)#si',
+ '#src=.*?(?:(?:alert|prompt|confirm|eval)(?:\(|&\#40;)|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)#si',
'',
- $this->_filter_attributes(str_replace(array('<', '>'), '', $match[1]))
+ $this->_filter_attributes($match[1])
),
$match[0]
);
@@ -872,4 +899,4 @@ protected function _csrf_set_hash()
}
/* End of file Security.php */
-/* Location: ./system/core/Security.php */
+/* Location: ./system/core/Security.php */
@@ -107,18 +107,93 @@ function create_captcha($data = '', $img_path = '', $img_url = '', $font_path =
// Do we have a "word" yet?
// -----------------------------------
- if ($word == '')
- {
+ // -----------------------------------
+ // Do we have a "word" yet?
+ // -----------------------------------
+
+ if (empty($word))
+ {
+ $word = '';
$pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ $pool_length = strlen($pool);
+ $rand_max = $pool_length - 1;
- $str = '';
- for ($i = 0; $i < 8; $i++)
+ // PHP7 or a suitable polyfill
+ if (function_exists('random_int'))
{
- $str .= substr($pool, mt_rand(0, strlen($pool) -1), 1);
+ try
+ {
+ for ($i = 0; $i < $word_length; $i++)
+ {
+ $word .= $pool[random_int(0, $rand_max)];
+ }
+ }
+ catch (Exception $e)
+ {
+ // This means fallback to the next possible
+ // alternative to random_int()
+ $word = '';
+ }
}
+ }
- $word = $str;
- }
+ if (empty($word))
+ {
+ // To avoid numerous get_random_bytes() calls, we'll
+ // just try fetching as much bytes as we need at once.
+ if (($bytes = _ci_captcha_get_random_bytes($pool_length)) !== FALSE)
+ {
+ $byte_index = $word_index = 0;
+ while ($word_index < $word_length)
+ {
+ if (($rand_index = unpack('C', $bytes[$byte_index++])) > $rand_max)
+ {
+ // Was this the last byte we have?
+ // If so, try to fetch more.
+ if ($byte_index === $pool_length)
+ {
+ // No failures should be possible if
+ // the first get_random_bytes() call
+ // didn't return FALSE, but still ...
+ for ($i = 0; $i < 5; $i++)
+ {
+ if (($bytes = _ci_captcha_get_random_bytes($pool_length)) === FALSE)
+ {
+ continue;
+ }
+
+ $byte_index = 0;
+ break;
+ }
+
+ if ($bytes === FALSE)
+ {
+ // Sadly, this means fallback to mt_rand()
+ $word = '';
+ break;
+ }
+ }
+
+ continue;
+ }
+
+ $word .= $pool[$rand_index];
+ $word_index++;
+ }
+ }
+ }
+
+ if (empty($word))
+ {
+ for ($i = 0; $i < $word_length; $i++)
+ {
+ $word .= $pool[mt_rand(0, $rand_max)];
+ }
+ }
+ elseif ( ! is_string($word))
+ {
+ $word = (string) $word;
+ }
// -----------------------------------
// Determine angle and position
@@ -239,6 +314,20 @@ function create_captcha($data = '', $img_path = '', $img_url = '', $font_path =
return array('word' => $word, 'time' => $now, 'image' => $img);
}
+
+ function _ci_captcha_get_random_bytes($length)
+ {
+ if (defined('MCRYPT_DEV_URANDOM'))
+ {
+ return mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
+ }
+ elseif (function_exists('openssl_random_pseudo_bytes'))
+ {
+ return openssl_random_pseudo_bytes($length);
+ }
+
+ return FALSE;
+ }
}
// ------------------------------------------------------------------------

0 comments on commit 94d43c3

Please sign in to comment.