| [ Index ] |
|
Code source de Joomla 1.0.13 |
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 9 * Tobin and Andrew Eddie. 10 * 11 * Modification by Louis Landry 12 * 13 * @copyright: Daniel Morris 14 * @email: dan@rootcube.com 15 * @license: GNU General Public License (GPL) 16 */ 17 class InputFilter 18 { 19 var $tagsArray; // default = empty array 20 var $attrArray; // default = empty array 21 22 var $tagsMethod; // default = 0 23 var $attrMethod; // default = 0 24 25 var $xssAuto; // default = 1 26 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'); 27 var $attrBlacklist = array ('action', 'background', 'codebase', 'dynsrc', 'lowsrc'); // also will strip ALL event handlers 28 29 /** 30 * Constructor for inputFilter class. Only first parameter is required. 31 * 32 * @access protected 33 * @param array $tagsArray list of user-defined tags 34 * @param array $attrArray list of user-defined attributes 35 * @param int $tagsMethod WhiteList method = 0, BlackList method = 1 36 * @param int $attrMethod WhiteList method = 0, BlackList method = 1 37 * @param int $xssAuto Only auto clean essentials = 0, Allow clean 38 * blacklisted tags/attr = 1 39 */ 40 function inputFilter($tagsArray = array (), $attrArray = array (), $tagsMethod = 0, $attrMethod = 0, $xssAuto = 1) 41 { 42 /* 43 * Make sure user defined arrays are in lowercase 44 */ 45 $tagsArray = array_map('strtolower', (array) $tagsArray); 46 $attrArray = array_map('strtolower', (array) $attrArray); 47 48 /* 49 * Assign member variables 50 */ 51 $this->tagsArray = $tagsArray; 52 $this->attrArray = $attrArray; 53 $this->tagsMethod = $tagsMethod; 54 $this->attrMethod = $attrMethod; 55 $this->xssAuto = $xssAuto; 56 } 57 58 /** 59 * Method to be called by another php script. Processes for XSS and 60 * specified bad code. 61 * 62 * @access public 63 * @param mixed $source Input string/array-of-string to be 'cleaned' 64 * @return mixed $source 'cleaned' version of input parameter 65 */ 66 function process($source) 67 { 68 /* 69 * Are we dealing with an array? 70 */ 71 if (is_array($source)) 72 { 73 foreach ($source as $key => $value) 74 { 75 // filter element for XSS and other 'bad' code etc. 76 if (is_string($value)) 77 { 78 $source[$key] = $this->remove($this->decode($value)); 79 } 80 } 81 return $source; 82 } else 83 /* 84 * Or a string? 85 */ 86 if (is_string($source) && !empty ($source)) 87 { 88 // filter source for XSS and other 'bad' code etc. 89 return $this->remove($this->decode($source)); 90 } else 91 { 92 /* 93 * Not an array or string.. return the passed parameter 94 */ 95 return $source; 96 } 97 } 98 99 /** 100 * Internal method to iteratively remove all unwanted tags and attributes 101 * 102 * @access protected 103 * @param string $source Input string to be 'cleaned' 104 * @return string $source 'cleaned' version of input parameter 105 */ 106 function remove($source) 107 { 108 $loopCounter = 0; 109 /* 110 * Iteration provides nested tag protection 111 */ 112 while ($source != $this->filterTags($source)) 113 { 114 $source = $this->filterTags($source); 115 $loopCounter ++; 116 } 117 return $source; 118 } 119 120 /** 121 * Internal method to strip a string of certain tags 122 * 123 * @access protected 124 * @param string $source Input string to be 'cleaned' 125 * @return string $source 'cleaned' version of input parameter 126 */ 127 function filterTags($source) 128 { 129 /* 130 * In the beginning we don't really have a tag, so everything is 131 * postTag 132 */ 133 $preTag = null; 134 $postTag = $source; 135 136 /* 137 * Is there a tag? If so it will certainly start with a '<' 138 */ 139 $tagOpen_start = strpos($source, '<'); 140 141 while ($tagOpen_start !== false) 142 { 143 144 /* 145 * Get some information about the tag we are processing 146 */ 147 $preTag .= substr($postTag, 0, $tagOpen_start); 148 $postTag = substr($postTag, $tagOpen_start); 149 $fromTagOpen = substr($postTag, 1); 150 $tagOpen_end = strpos($fromTagOpen, '>'); 151 152 /* 153 * Let's catch any non-terminated tags and skip over them 154 */ 155 if ($tagOpen_end === false) 156 { 157 $postTag = substr($postTag, $tagOpen_start +1); 158 $tagOpen_start = strpos($postTag, '<'); 159 continue; 160 } 161 162 /* 163 * Do we have a nested tag? 164 */ 165 $tagOpen_nested = strpos($fromTagOpen, '<'); 166 $tagOpen_nested_end = strpos(substr($postTag, $tagOpen_end), '>'); 167 if (($tagOpen_nested !== false) && ($tagOpen_nested < $tagOpen_end)) 168 { 169 $preTag .= substr($postTag, 0, ($tagOpen_nested +1)); 170 $postTag = substr($postTag, ($tagOpen_nested +1)); 171 $tagOpen_start = strpos($postTag, '<'); 172 continue; 173 } 174 175 176 /* 177 * Lets get some information about our tag and setup attribute pairs 178 */ 179 $tagOpen_nested = (strpos($fromTagOpen, '<') + $tagOpen_start +1); 180 $currentTag = substr($fromTagOpen, 0, $tagOpen_end); 181 $tagLength = strlen($currentTag); 182 $tagLeft = $currentTag; 183 $attrSet = array (); 184 $currentSpace = strpos($tagLeft, ' '); 185 186 /* 187 * Are we an open tag or a close tag? 188 */ 189 if (substr($currentTag, 0, 1) == "/") 190 { 191 // Close Tag 192 $isCloseTag = true; 193 list ($tagName) = explode(' ', $currentTag); 194 $tagName = substr($tagName, 1); 195 } else 196 { 197 // Open Tag 198 $isCloseTag = false; 199 list ($tagName) = explode(' ', $currentTag); 200 } 201 202 /* 203 * Exclude all "non-regular" tagnames 204 * OR no tagname 205 * OR remove if xssauto is on and tag is blacklisted 206 */ 207 if ((!preg_match("/^[a-z][a-z0-9]*$/i", $tagName)) || (!$tagName) || ((in_array(strtolower($tagName), $this->tagBlacklist)) && ($this->xssAuto))) 208 { 209 $postTag = substr($postTag, ($tagLength +2)); 210 $tagOpen_start = strpos($postTag, '<'); 211 // Strip tag 212 continue; 213 } 214 215 /* 216 * Time to grab any attributes from the tag... need this section in 217 * case attributes have spaces in the values. 218 */ 219 while ($currentSpace !== false) 220 { 221 $fromSpace = substr($tagLeft, ($currentSpace +1)); 222 $nextSpace = strpos($fromSpace, ' '); 223 $openQuotes = strpos($fromSpace, '"'); 224 $closeQuotes = strpos(substr($fromSpace, ($openQuotes +1)), '"') + $openQuotes +1; 225 226 /* 227 * Do we have an attribute to process? [check for equal sign] 228 */ 229 if (strpos($fromSpace, '=') !== false) 230 { 231 /* 232 * If the attribute value is wrapped in quotes we need to 233 * grab the substring from the closing quote, otherwise grab 234 * till the next space 235 */ 236 if (($openQuotes !== false) && (strpos(substr($fromSpace, ($openQuotes +1)), '"') !== false)) 237 { 238 $attr = substr($fromSpace, 0, ($closeQuotes +1)); 239 } else 240 { 241 $attr = substr($fromSpace, 0, $nextSpace); 242 } 243 } else 244 { 245 /* 246 * No more equal signs so add any extra text in the tag into 247 * the attribute array [eg. checked] 248 */ 249 $attr = substr($fromSpace, 0, $nextSpace); 250 } 251 252 // Last Attribute Pair 253 if (!$attr) 254 { 255 $attr = $fromSpace; 256 } 257 258 /* 259 * Add attribute pair to the attribute array 260 */ 261 $attrSet[] = $attr; 262 263 /* 264 * Move search point and continue iteration 265 */ 266 $tagLeft = substr($fromSpace, strlen($attr)); 267 $currentSpace = strpos($tagLeft, ' '); 268 } 269 270 /* 271 * Is our tag in the user input array? 272 */ 273 $tagFound = in_array(strtolower($tagName), $this->tagsArray); 274 275 /* 276 * If the tag is allowed lets append it to the output string 277 */ 278 if ((!$tagFound && $this->tagsMethod) || ($tagFound && !$this->tagsMethod)) 279 { 280 /* 281 * Reconstruct tag with allowed attributes 282 */ 283 if (!$isCloseTag) 284 { 285 // Open or Single tag 286 $attrSet = $this->filterAttr($attrSet); 287 $preTag .= '<'.$tagName; 288 for ($i = 0; $i < count($attrSet); $i ++) 289 { 290 $preTag .= ' '.$attrSet[$i]; 291 } 292 293 /* 294 * Reformat single tags to XHTML 295 */ 296 if (strpos($fromTagOpen, "</".$tagName)) 297 { 298 $preTag .= '>'; 299 } else 300 { 301 $preTag .= ' />'; 302 } 303 } else 304 { 305 // Closing Tag 306 $preTag .= '</'.$tagName.'>'; 307 } 308 } 309 310 /* 311 * Find next tag's start and continue iteration 312 */ 313 $postTag = substr($postTag, ($tagLength +2)); 314 $tagOpen_start = strpos($postTag, '<'); 315 //print "T: $preTag\n"; 316 } 317 318 /* 319 * Append any code after the end of tags and return 320 */ 321 if ($postTag != '<') 322 { 323 $preTag .= $postTag; 324 } 325 return $preTag; 326 } 327 328 /** 329 * Internal method to strip a tag of certain attributes 330 * 331 * @access protected 332 * @param array $attrSet Array of attribute pairs to filter 333 * @return array $newSet Filtered array of attribute pairs 334 */ 335 function filterAttr($attrSet) 336 { 337 /* 338 * Initialize variables 339 */ 340 $newSet = array (); 341 342 /* 343 * Iterate through attribute pairs 344 */ 345 for ($i = 0; $i < count($attrSet); $i ++) 346 { 347 /* 348 * Skip blank spaces 349 */ 350 if (!$attrSet[$i]) 351 { 352 continue; 353 } 354 355 /* 356 * Split into name/value pairs 357 */ 358 $attrSubSet = explode('=', trim($attrSet[$i]), 2); 359 list ($attrSubSet[0]) = explode(' ', $attrSubSet[0]); 360 361 /* 362 * Remove all "non-regular" attribute names 363 * AND blacklisted attributes 364 */ 365 if ((!eregi("^[a-z]*$", $attrSubSet[0])) || (($this->xssAuto) && ((in_array(strtolower($attrSubSet[0]), $this->attrBlacklist)) || (substr($attrSubSet[0], 0, 2) == 'on')))) 366 { 367 continue; 368 } 369 370 /* 371 * XSS attribute value filtering 372 */ 373 if ($attrSubSet[1]) 374 { 375 // strips unicode, hex, etc 376 $attrSubSet[1] = str_replace('&#', '', $attrSubSet[1]); 377 // strip normal newline within attr value 378 $attrSubSet[1] = preg_replace('/\s+/', '', $attrSubSet[1]); 379 // strip double quotes 380 $attrSubSet[1] = str_replace('"', '', $attrSubSet[1]); 381 // [requested feature] convert single quotes from either side to doubles (Single quotes shouldn't be used to pad attr value) 382 if ((substr($attrSubSet[1], 0, 1) == "'") && (substr($attrSubSet[1], (strlen($attrSubSet[1]) - 1), 1) == "'")) 383 { 384 $attrSubSet[1] = substr($attrSubSet[1], 1, (strlen($attrSubSet[1]) - 2)); 385 } 386 // strip slashes 387 $attrSubSet[1] = stripslashes($attrSubSet[1]); 388 } 389 390 /* 391 * Autostrip script tags 392 */ 393 if (InputFilter :: badAttributeValue($attrSubSet)) 394 { 395 continue; 396 } 397 398 /* 399 * Is our attribute in the user input array? 400 */ 401 $attrFound = in_array(strtolower($attrSubSet[0]), $this->attrArray); 402 403 /* 404 * If the tag is allowed lets keep it 405 */ 406 if ((!$attrFound && $this->attrMethod) || ($attrFound && !$this->attrMethod)) 407 { 408 /* 409 * Does the attribute have a value? 410 */ 411 if ($attrSubSet[1]) 412 { 413 $newSet[] = $attrSubSet[0].'="'.$attrSubSet[1].'"'; 414 } 415 elseif ($attrSubSet[1] == "0") 416 { 417 /* 418 * Special Case 419 * Is the value 0? 420 */ 421 $newSet[] = $attrSubSet[0].'="0"'; 422 } else 423 { 424 $newSet[] = $attrSubSet[0].'="'.$attrSubSet[0].'"'; 425 } 426 } 427 } 428 return $newSet; 429 } 430 431 /** 432 * Function to determine if contents of an attribute is safe 433 * 434 * @access protected 435 * @param array $attrSubSet A 2 element array for attributes name,value 436 * @return boolean True if bad code is detected 437 */ 438 function badAttributeValue($attrSubSet) 439 { 440 $attrSubSet[0] = strtolower($attrSubSet[0]); 441 $attrSubSet[1] = strtolower($attrSubSet[1]); 442 return (((strpos($attrSubSet[1], 'expression') !== false) && ($attrSubSet[0]) == 'style') || (strpos($attrSubSet[1], 'javascript:') !== false) || (strpos($attrSubSet[1], 'behaviour:') !== false) || (strpos($attrSubSet[1], 'vbscript:') !== false) || (strpos($attrSubSet[1], 'mocha:') !== false) || (strpos($attrSubSet[1], 'livescript:') !== false)); 443 } 444 445 /** 446 * Try to convert to plaintext 447 * 448 * @access protected 449 * @param string $source 450 * @return string Plaintext string 451 */ 452 function decode($source) 453 { 454 // url decode 455 $source = html_entity_decode($source, ENT_QUOTES, "ISO-8859-1"); 456 // convert decimal 457 $source = preg_replace('/&#(\d+);/me', "chr(\\1)", $source); // decimal notation 458 // convert hex 459 $source = preg_replace('/&#x([a-f0-9]+);/mei', "chr(0x\\1)", $source); // hex notation 460 return $source; 461 } 462 463 /** 464 * Method to be called by another php script. Processes for SQL injection 465 * 466 * @access public 467 * @param mixed $source input string/array-of-string to be 'cleaned' 468 * @param resource $connection - An open MySQL connection 469 * @return string 'cleaned' version of input parameter 470 */ 471 function safeSQL($source, & $connection) 472 { 473 // clean all elements in this array 474 if (is_array($source)) 475 { 476 foreach ($source as $key => $value) 477 { 478 // filter element for SQL injection 479 if (is_string($value)) 480 { 481 $source[$key] = $this->quoteSmart($this->decode($value), $connection); 482 } 483 } 484 return $source; 485 // clean this string 486 } else 487 if (is_string($source)) 488 { 489 // filter source for SQL injection 490 if (is_string($source)) 491 { 492 return $this->quoteSmart($this->decode($source), $connection); 493 } 494 // return parameter as given 495 } else 496 { 497 return $source; 498 } 499 } 500 501 /** 502 * Method to escape a string 503 * 504 * @author Chris Tobin 505 * @author Daniel Morris 506 * 507 * @access protected 508 * @param string $source 509 * @param resource $connection An open MySQL connection 510 * @return string Escaped string 511 */ 512 function quoteSmart($source, & $connection) 513 { 514 /* 515 * Strip escaping slashes if necessary 516 */ 517 if (get_magic_quotes_gpc()) 518 { 519 $source = stripslashes($source); 520 } 521 522 /* 523 * Escape numeric and text values 524 */ 525 $source = $this->escapeString($source, $connection); 526 return $source; 527 } 528 529 /** 530 * @author Chris Tobin 531 * @author Daniel Morris 532 * 533 * @access protected 534 * @param string $source 535 * @param resource $connection An open MySQL connection 536 * @return string Escaped string 537 */ 538 function escapeString($string, & $connection) { 539 /* 540 * Use the appropriate escape string depending upon which version of php 541 * you are running 542 */ 543 if (version_compare(phpversion(), '4.3.0', '<')) { 544 $string = mysql_escape_string($string); 545 } else { 546 $string = mysql_real_escape_string($string); 547 } 548 549 return $string; 550 } 551 } 552 ?>
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
| Généré le : Wed Nov 21 14:43:32 2007 | par Balluche grâce à PHPXref 0.7 |
|