[ Index ]
 

Code source de Joomla 1.0.13

Accédez au Source d'autres logiciels libres

title

Body

[fermer]

/includes/phpInputFilter/ -> class.inputfilter.php (source)

   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  ?>


Généré le : Wed Nov 21 14:43:32 2007 par Balluche grâce à PHPXref 0.7
  Clicky Web Analytics