Guest
Public paste!

Master

By: a guest | Jul 9th, 2009 | Syntax: PHP | Size: 7.26 KB | Hits: 211 | Expires: Never
This paste has a previous version, view the difference. Copy text to clipboard
  1.         function xss_clean($str) {
  2.                 /*
  3.                  * Remove Null Characters
  4.                  *
  5.                  * This prevents sandwiching null characters
  6.                  * between ascii characters, like Java\0script.
  7.                  */
  8.                 $str = preg_replace('/\0+/', '', $str);
  9.                 $str = preg_replace('/(\\\\0)+/', '', $str);
  10.                
  11.                 /*
  12.                  * Validate standard character entities
  13.                  *
  14.                  * Add a semicolon if missing.  We do this to enable
  15.                  * the conversion of entities to ASCII later.
  16.                  */
  17.                 $str = preg_replace('#(&\#?[0-9a-z]+)[\x00-\x20]*;?#i', "\\1;", $str);
  18.                
  19.                 /*
  20.                  * Validate UTF16 two byte encoding (x00)
  21.                  *
  22.                  * Just as above, adds a semicolon if missing.
  23.                  */
  24.                 $str = preg_replace('#(&\#x?)([0-9A-F]+);?#i', "\\1\\2;", $str);
  25.                
  26.                 /*
  27.                  * URL Decode
  28.                  *
  29.                  * Just in case stuff like this is submitted:
  30.                  * <a href="http://%77%77%77%2E%67%6F%6F%67%6C%65%2E%63%6F%6D">Google</a>
  31.                  * Note: Use rawurldecode() so it does not remove plus signs
  32.                  */
  33.                 $str = rawurldecode($str);
  34.                
  35.                 /*
  36.                  * Convert character entities to ASCII
  37.                  *
  38.                  * This permits our tests below to work reliably.
  39.                  * We only convert entities that are within tags since
  40.                  * these are the ones that will pose security problems.
  41.                  */
  42.                
  43.                 $str = preg_replace_callback("/[a-z]+=([\'\"]).*?\\1/si", array($this, '_attribute_conversion'), $str);
  44.                
  45.                 $str = preg_replace_callback("/<([\w]+)[^>]*>/si", array($this, '_html_entity_decode_callback'), $str);
  46.                
  47.                 /*
  48.                
  49.                 Old Code that when modified to use preg_replace()'s above became more efficient memory-wise
  50.                
  51.                 if (preg_match_all("/[a-z]+=([\'\"]).*?\\1/si", $str, $matches))                {        
  52.                         for ($i = 0; $i < count($matches[0]); $i++)                     {
  53.                                 if (stristr($matches[0][$i], '>'))                              {
  54.                                         $str = str_replace(     $matches['0'][$i],      str_replace('>', '&lt;', $matches[0][$i]),              $str);
  55.                                 }
  56.                         }
  57.                 }
  58.                  
  59.     if (preg_match_all("/<([\w]+)[^>]*>/si", $str, $matches))    {        
  60.                         for ($i = 0; $i < count($matches[0]); $i++)                     {
  61.                                 $str = str_replace($matches[0][$i], $this->_html_entity_decode($matches[0][$i], $charset),      $str);
  62.                         }
  63.                 }
  64.                 */
  65.                
  66.                 /*
  67.                  * Convert all tabs to spaces
  68.                  *
  69.                  * This prevents strings like this: ja  vascript
  70.                  * NOTE: we deal with spaces between characters later.
  71.                  * NOTE: preg_replace was found to be amazingly slow here on large blocks of data,
  72.                  * so we use str_replace.
  73.                  */
  74.                
  75.                 $str = str_replace("\t", " ", $str);
  76.                
  77.                 /*
  78.                  * Not Allowed Under Any Conditions
  79.                  */
  80.                 $bad = array(
  81.                         'document.cookie' => '[removed]',
  82.                         'document.write' => '[removed]',
  83.                         '.parentNode' => '[removed]',
  84.                         '.innerHTML' => '[removed]',
  85.                         'window.location' => '[removed]',
  86.                         '-moz-binding' => '[removed]',
  87.                         '<!--' => '&lt;!--',
  88.                         '-->' => '--&gt;',
  89.                         '<!CDATA[' => '&lt;![CDATA['
  90.                 );
  91.                 foreach ($bad as $key=>$val) {
  92.                         $str = str_replace($key, $val, $str);
  93.                 }
  94.                
  95.                 $bad = array(
  96.                         "javascript\s*:" => '[removed]',
  97.                         "expression\s*\(" => '[removed]', // CSS and IE
  98.                         "Redirect\s+302" => '[removed]'
  99.                 );
  100.                 foreach ($bad as $key=>$val) {
  101.                         $str = preg_replace("#" . $key . "#i", $val, $str);
  102.                 }
  103.                
  104.                 /*
  105.                  * Makes PHP tags safe
  106.                  *
  107.                  *  Note: XML tags are inadvertently replaced too:
  108.                  *      <?xml
  109.                  * But it doesn't seem to pose a problem.
  110.                  */
  111.                 $str = str_replace(array('<?php', '<?PHP', '<?', '?' . '>'), array('&lt;?php', '&lt;?PHP', '&lt;?', '?&gt;'), $str);
  112.                
  113.                 /*
  114.                  * Compact any exploded words
  115.                  *
  116.                  * This corrects words like:  j a v a s c r i p t
  117.                  * These words are compacted back to their correct state.
  118.                  */
  119.                 $words = array('javascript', 'expression', 'vbscript', 'script', 'applet', 'alert', 'document', 'write', 'cookie', 'window');
  120.                 foreach ($words as $word) {
  121.                         $temp = '';
  122.                         for($i = 0; $i < strlen($word); $i++) {
  123.                                 $temp .= substr($word, $i, 1) . "\s*";
  124.                         }
  125.                        
  126.                         // We only want to do this when it is followed by a non-word character
  127.                         // That way valid stuff like "dealer to" does not become "dealerto"
  128.                         $str = preg_replace('#(' . substr($temp, 0, -3) . ')(\W)#ise', "preg_replace('/\s+/s', '', '\\1').'\\2'", $str);
  129.                 }
  130.                
  131.                 /*
  132.                  * Remove disallowed Javascript in links or img tags
  133.                  */
  134.                 do {
  135.                         $original = $str;
  136.                         if ((version_compare(PHP_VERSION, '5.0', '>=') === TRUE && stripos($str, '</a>') !== FALSE) or preg_match("/<\/a>/i", $str)) {
  137.                                 $str = preg_replace_callback("#<a.*?</a>#si", array($this, '_js_link_removal'), $str);
  138.                         }
  139.                         if ((version_compare(PHP_VERSION, '5.0', '>=') === TRUE && stripos($str, '<img') !== FALSE) or preg_match("/img/i", $str)) {
  140.                                 $str = preg_replace_callback("#<img.*?" . ">#si", array($this, '_js_img_removal'), $str);
  141.                         }
  142.                         if ((version_compare(PHP_VERSION, '5.0', '>=') === TRUE && (stripos($str, 'script') !== FALSE or stripos($str, 'xss') !== FALSE)) or preg_match("/(script|xss)/i", $str)) {
  143.                                 $str = preg_replace("#</*(script|xss).*?\>#si", "", $str);
  144.                         }
  145.                 } while ($original != $str);
  146.                
  147.                 unset($original);
  148.                
  149.                 /*
  150.                  * Remove JavaScript Event Handlers
  151.                  *
  152.                  * Note: This code is a little blunt.  It removes the event handler and anything up to the closing >,
  153.                  * but it's unlikely to be a problem.
  154.                  */
  155.                 $event_handlers = array('onblur', 'onchange', 'onclick', 'onfocus', 'onload', 'onmouseover', 'onmouseup', 'onmousedown', 'onselect', 'onsubmit', 'onunload', 'onkeypress', 'onkeydown', 'onkeyup', 'onresize', 'xmlns');
  156.                 $str = preg_replace("#<([^>]+)(" . implode('|', $event_handlers) . ")([^>]*)>#iU", "&lt;\\1\\2\\3&gt;", $str);
  157.                
  158.                 /*
  159.                  * Sanitize naughty HTML elements
  160.                  *
  161.                  * If a tag containing any of the words in the list
  162.                  * below is found, the tag gets converted to entities.
  163.                  *
  164.                  * So this: <blink>
  165.                  * Becomes: &lt;blink&gt;
  166.                  */
  167.                 $str = preg_replace('#<(/*\s*)(alert|applet|basefont|base|behavior|bgsound|blink|body|embed|expression|form|frameset|frame|head|html|ilayer|iframe|input|layer|link|meta|object|plaintext|style|script|textarea|title|xml|xss)([^>]*)>#is', "&lt;\\1\\2\\3&gt;", $str);
  168.                
  169.                 /*
  170.                  * Sanitize naughty scripting elements
  171.                  *
  172.                  * Similar to above, only instead of looking for tags it looks for PHP and JavaScript commands
  173.                  * that are disallowed.  Rather than removing the code, it simply converts the parenthesis to entities
  174.                  * rendering the code un-executable.
  175.                  *
  176.                  * For example: eval('some code')
  177.                  * Becomes:             eval&#40;'some code'&#41;
  178.                  */
  179.                 $str = preg_replace('#(alert|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si', "\\1\\2&#40;\\3&#41;", $str);
  180.                
  181.                 /*
  182.                  * Final clean up
  183.                  *
  184.                  * This adds a bit of extra precaution in case something got through the above filters
  185.                  */
  186.                 $bad = array(
  187.                         'document.cookie' => '[removed]',
  188.                         'document.write' => '[removed]',
  189.                         '.parentNode' => '[removed]',
  190.                         '.innerHTML' => '[removed]',
  191.                         'window.location' => '[removed]',
  192.                         '-moz-binding' => '[removed]',
  193.                         '<!--' => '&lt;!--', '-->' => '--&gt;',
  194.                         '<!CDATA[' => '&lt;![CDATA['
  195.                 );
  196.                 foreach ($bad as $key=>$val) {
  197.                         $str = str_replace($key, $val, $str);
  198.                 }
  199.                
  200.                 $bad = array(
  201.                         "javascript\s*:" => '[removed]',
  202.                         "expression\s*\(" => '[removed]', // CSS and IE
  203.                         "Redirect\s+302" => '[removed]'
  204.                 );
  205.                 foreach ($bad as $key=>$val) {
  206.                         $str = preg_replace("#" . $key . "#i", $val, $str);
  207.                 }
  208.                
  209.                 log_message('debug', "XSS Filtering completed");
  210.                 return $str;
  211.         }