| [ Index ] | PHP Cross Reference of Mambo 4.6.5 |
|
| [ Variables ] [ Functions ] [ Classes ] [ Constants ] [ Statistics ] | ||
[Summary view] [Print] [Text view]
1 <?php 2 3 /** @class: InputFilter (PHP4 & PHP5, with comments) 4 * @project: PHP Input Filter 5 * @date: 10-05-2005 6 * @version: 1.2.2_php4/php5 7 * @author: Daniel Morris 8 * @contributors: Gianpaolo Racca, Ghislain Picard, Marco Wandschneider, Chris Tobin and Andrew Eddie. 9 * @copyright: Daniel Morris 10 * @email: dan@rootcube.com 11 * @license: GNU General Public License (GPL) 12 */ 13 class InputFilter { 14 var $tagsArray; // default = empty array 15 var $attrArray; // default = empty array 16 17 var $tagsMethod; // default = 0 18 var $attrMethod; // default = 0 19 20 var $xssAuto; // default = 1 21 var $tagBlacklist = array('applet', 'body', 'bgsound', 'base', 'basefont', 'embed', 'frame', 'frameset', 'head', 'html', 'id', 'iframe', 'ilayer', 'layer', 'link', 'meta', 'name', 'object', 'script', 'style', 'title', 'xml'); 22 var $attrBlacklist = array('action', 'background', 'codebase', 'dynsrc', 'lowsrc'); // also will strip ALL event handlers 23 24 /** 25 * Constructor for inputFilter class. Only first parameter is required. 26 * @access constructor 27 * @param Array $tagsArray - list of user-defined tags 28 * @param Array $attrArray - list of user-defined attributes 29 * @param int $tagsMethod - 0= allow just user-defined, 1= allow all but user-defined 30 * @param int $attrMethod - 0= allow just user-defined, 1= allow all but user-defined 31 * @param int $xssAuto - 0= only auto clean essentials, 1= allow clean blacklisted tags/attr 32 */ 33 function inputFilter($tagsArray = array(), $attrArray = array(), $tagsMethod = 0, $attrMethod = 0, $xssAuto = 1) { 34 // make sure user defined arrays are in lowercase 35 for ($i = 0; $i < count($tagsArray); $i++) $tagsArray[$i] = strtolower($tagsArray[$i]); 36 for ($i = 0; $i < count($attrArray); $i++) $attrArray[$i] = strtolower($attrArray[$i]); 37 // assign to member vars 38 $this->tagsArray = (array) $tagsArray; 39 $this->attrArray = (array) $attrArray; 40 $this->tagsMethod = $tagsMethod; 41 $this->attrMethod = $attrMethod; 42 $this->xssAuto = $xssAuto; 43 } 44 45 /** 46 * Method to be called by another php script. Processes for XSS and specified bad code. 47 * @access public 48 * @param Mixed $source - input string/array-of-string to be 'cleaned' 49 * @return String $source - 'cleaned' version of input parameter 50 */ 51 function process($source) { 52 // clean all elements in this array 53 if (is_array($source)) { 54 foreach($source as $key => $value) 55 // filter element for XSS and other 'bad' code etc. 56 if (is_string($value)) $source[$key] = $this->remove($this->decode($value)); 57 return $source; 58 // clean this string 59 } else if (is_string($source)) { 60 // filter source for XSS and other 'bad' code etc. 61 return $this->remove($this->decode($source)); 62 // return parameter as given 63 } else return $source; 64 } 65 66 /** 67 * Internal method to iteratively remove all unwanted tags and attributes 68 * @access protected 69 * @param String $source - input string to be 'cleaned' 70 * @return String $source - 'cleaned' version of input parameter 71 */ 72 function remove($source) { 73 $loopCounter=0; 74 // provides nested-tag protection 75 while($source != $this->filterTags($source)) { 76 $source = $this->filterTags($source); 77 $loopCounter++; 78 } 79 return $source; 80 } 81 82 /** 83 * Internal method to strip a string of certain tags 84 * @access protected 85 * @param String $source - input string to be 'cleaned' 86 * @return String $source - 'cleaned' version of input parameter 87 */ 88 function filterTags($source) { 89 // filter pass setup 90 $preTag = NULL; 91 $postTag = $source; 92 // find initial tag's position 93 $tagOpen_start = strpos($source, '<'); 94 // interate through string until no tags left 95 while($tagOpen_start !== FALSE) { 96 // process tag interatively 97 $preTag .= substr($postTag, 0, $tagOpen_start); 98 $postTag = substr($postTag, $tagOpen_start); 99 $fromTagOpen = substr($postTag, 1); 100 // end of tag 101 $tagOpen_end = strpos($fromTagOpen, '>'); 102 if ($tagOpen_end === false) break; 103 // next start of tag (for nested tag assessment) 104 $tagOpen_nested = strpos($fromTagOpen, '<'); 105 if (($tagOpen_nested !== false) && ($tagOpen_nested < $tagOpen_end)) { 106 $preTag .= substr($postTag, 0, ($tagOpen_nested+1)); 107 $postTag = substr($postTag, ($tagOpen_nested+1)); 108 $tagOpen_start = strpos($postTag, '<'); 109 continue; 110 } 111 $tagOpen_nested = (strpos($fromTagOpen, '<') + $tagOpen_start + 1); 112 $currentTag = substr($fromTagOpen, 0, $tagOpen_end); 113 $tagLength = strlen($currentTag); 114 if (!$tagOpen_end) { 115 $preTag .= $postTag; 116 $tagOpen_start = strpos($postTag, '<'); 117 } 118 // iterate through tag finding attribute pairs - setup 119 $tagLeft = $currentTag; 120 $attrSet = array(); 121 $currentSpace = strpos($tagLeft, ' '); 122 // is end tag 123 if (substr($currentTag, 0, 1) == "/") { 124 $isCloseTag = TRUE; 125 list($tagName) = explode(' ', $currentTag); 126 $tagName = substr($tagName, 1); 127 // is start tag 128 } else { 129 $isCloseTag = FALSE; 130 list($tagName) = explode(' ', $currentTag); 131 } 132 // excludes all "non-regular" tagnames OR no tagname OR remove if xssauto is on and tag is blacklisted 133 if ((!preg_match("/^[a-z][a-z0-9]*$/i",$tagName)) || (!$tagName) || ((in_array(strtolower($tagName), $this->tagBlacklist)) && ($this->xssAuto))) { 134 $postTag = substr($postTag, ($tagLength + 2)); 135 $tagOpen_start = strpos($postTag, '<'); 136 // don't append this tag 137 continue; 138 } 139 // this while is needed to support attribute values with spaces in! 140 while ($currentSpace !== FALSE) { 141 $fromSpace = substr($tagLeft, ($currentSpace+1)); 142 $nextSpace = strpos($fromSpace, ' '); 143 $openQuotes = strpos($fromSpace, '"'); 144 $closeQuotes = strpos(substr($fromSpace, ($openQuotes+1)), '"') + $openQuotes + 1; 145 // another equals exists 146 if (strpos($fromSpace, '=') !== FALSE) { 147 // opening and closing quotes exists 148 if (($openQuotes !== FALSE) && (strpos(substr($fromSpace, ($openQuotes+1)), '"') !== FALSE)) 149 $attr = substr($fromSpace, 0, ($closeQuotes+1)); 150 // one or neither exist 151 else $attr = substr($fromSpace, 0, $nextSpace); 152 // no more equals exist 153 } else $attr = substr($fromSpace, 0, $nextSpace); 154 // last attr pair 155 if (!$attr) $attr = $fromSpace; 156 // add to attribute pairs array 157 $attrSet[] = $attr; 158 // next inc 159 $tagLeft = substr($fromSpace, strlen($attr)); 160 $currentSpace = strpos($tagLeft, ' '); 161 } 162 // appears in array specified by user 163 $tagFound = in_array(strtolower($tagName), $this->tagsArray); 164 // remove this tag on condition 165 if ((!$tagFound && $this->tagsMethod) || ($tagFound && !$this->tagsMethod)) { 166 // reconstruct tag with allowed attributes 167 if (!$isCloseTag) { 168 $attrSet = $this->filterAttr($attrSet); 169 $preTag .= '<' . $tagName; 170 for ($i = 0; $i < count($attrSet); $i++) 171 $preTag .= ' ' . $attrSet[$i]; 172 // reformat single tags to XHTML 173 if (strpos($fromTagOpen, "</" . $tagName)) $preTag .= '>'; 174 else $preTag .= ' />'; 175 // just the tagname 176 } else $preTag .= '</' . $tagName . '>'; 177 } 178 // find next tag's start 179 $postTag = substr($postTag, ($tagLength + 2)); 180 $tagOpen_start = strpos($postTag, '<'); 181 } 182 // append any code after end of tags 183 $preTag .= $postTag; 184 return $preTag; 185 } 186 187 /** 188 * Internal method to strip a tag of certain attributes 189 * @access protected 190 * @param Array $attrSet 191 * @return Array $newSet 192 */ 193 function filterAttr($attrSet) { 194 $newSet = array(); 195 // process attributes 196 for ($i = 0; $i <count($attrSet); $i++) { 197 // skip blank spaces in tag 198 if (!$attrSet[$i]) continue; 199 // split into attr name and value 200 $attrSubSet = explode('=', trim($attrSet[$i]),2); 201 list($attrSubSet[0]) = explode(' ', $attrSubSet[0]); 202 // removes all "non-regular" attr names AND also attr blacklisted 203 if ((!eregi("^[a-z]*$",$attrSubSet[0])) || (($this->xssAuto) && ((in_array(strtolower($attrSubSet[0]), $this->attrBlacklist)) || (substr($attrSubSet[0], 0, 2) == 'on')))) 204 continue; 205 // xss attr value filtering 206 if ($attrSubSet[1]) { 207 // strips unicode, hex, etc 208 $attrSubSet[1] = str_replace('&#', '', $attrSubSet[1]); 209 // strip normal newline within attr value 210 $attrSubSet[1] = preg_replace('/\s+/', '', $attrSubSet[1]); 211 // strip double quotes 212 $attrSubSet[1] = str_replace('"', '', $attrSubSet[1]); 213 // [requested feature] convert single quotes from either side to doubles (Single quotes shouldn't be used to pad attr value) 214 if ((substr($attrSubSet[1], 0, 1) == "'") && (substr($attrSubSet[1], (strlen($attrSubSet[1]) - 1), 1) == "'")) 215 $attrSubSet[1] = substr($attrSubSet[1], 1, (strlen($attrSubSet[1]) - 2)); 216 // strip slashes 217 $attrSubSet[1] = stripslashes($attrSubSet[1]); 218 } 219 // auto strip attr's with "javascript: 220 if (InputFilter::badAttributeValue( $attrSubSet )) 221 continue; 222 223 // if matches user defined array 224 $attrFound = in_array(strtolower($attrSubSet[0]), $this->attrArray); 225 // keep this attr on condition 226 if ((!$attrFound && $this->attrMethod) || ($attrFound && !$this->attrMethod)) { 227 // attr has value 228 if ($attrSubSet[1]) $newSet[] = $attrSubSet[0] . '="' . $attrSubSet[1] . '"'; 229 // attr has decimal zero as value 230 else if ($attrSubSet[1] == "0") $newSet[] = $attrSubSet[0] . '="0"'; 231 // reformat single attributes to XHTML 232 else $newSet[] = $attrSubSet[0] . '="' . $attrSubSet[0] . '"'; 233 } 234 } 235 return $newSet; 236 } 237 238 /** 239 * Function to determine if contents of an attribute is safe 240 * @param Array A 2 element array for attribute [name] and [value] 241 * @return Boolean True if bad code is detected 242 */ 243 function badAttributeValue( $attrSubSet ) { 244 $attrSubSet[0] = strtolower( $attrSubSet[0] ); 245 $attrSubSet[1] = strtolower( $attrSubSet[1] ); 246 return ( 247 ((strpos($attrSubSet[1], 'expression') !== false) && ($attrSubSet[0]) == 'style') || 248 (strpos($attrSubSet[1], 'javascript:') !== false) || 249 (strpos($attrSubSet[1], 'behaviour:') !== false) || 250 (strpos($attrSubSet[1], 'vbscript:') !== false) || 251 (strpos($attrSubSet[1], 'mocha:') !== false) || 252 (strpos($attrSubSet[1], 'livescript:') !== false) 253 ); 254 } 255 256 /** 257 * Try to convert to plaintext 258 * @access protected 259 * @param String $source 260 * @return String $source 261 */ 262 function decode($source) { 263 // url decode 264 $source = html_entity_decode($source, ENT_QUOTES, "ISO-8859-1"); 265 // convert decimal 266 $source = preg_replace('/&#(\d+);/me',"chr(\\1)", $source); // decimal notation 267 // convert hex 268 $source = preg_replace('/&#x([a-f0-9]+);/mei',"chr(0x\\1)", $source); // hex notation 269 return $source; 270 } 271 272 /** 273 * Method to be called by another php script. Processes for SQL injection 274 * @access public 275 * @param Mixed $source - input string/array-of-string to be 'cleaned' 276 * @param Buffer $connection - An open MySQL connection 277 * @return String $source - 'cleaned' version of input parameter 278 */ 279 function safeSQL($source, &$connection) { 280 // clean all elements in this array 281 if (is_array($source)) { 282 foreach($source as $key => $value) 283 // filter element for SQL injection 284 if (is_string($value)) $source[$key] = $this->quoteSmart($this->decode($value), $connection); 285 return $source; 286 // clean this string 287 } else if (is_string($source)) { 288 // filter source for SQL injection 289 if (is_string($source)) return $this->quoteSmart($this->decode($source), $connection); 290 // return parameter as given 291 } else return $source; 292 } 293 294 /** 295 * @author Chris Tobin 296 * @author Daniel Morris 297 * @access protected 298 * @param String $source 299 * @param Resource $connection - An open MySQL connection 300 * @return String $source 301 */ 302 function quoteSmart($source, &$connection) { 303 // strip slashes 304 if (get_magic_quotes_gpc()) $source = stripslashes($source); 305 // quote both numeric and text 306 $source = $this->escapeString($source, $connection); 307 return $source; 308 } 309 310 /** 311 * @author Chris Tobin 312 * @author Daniel Morris 313 * @access protected 314 * @param String $source 315 * @param Resource $connection - An open MySQL connection 316 * @return String $source 317 */ 318 function escapeString($string, &$connection) { 319 // depreciated function 320 if (version_compare(phpversion(),"4.3.0", "<")) mysql_escape_string($string); 321 // current function 322 else mysql_real_escape_string($string); 323 return $string; 324 } 325 } 326 327 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Wed Feb 8 00:05:01 2012 | Cross-referenced by PHPXref 0.7 |
| Mambo API: Mambo is Free software released under the GNU/General Public License, Version 2 |