Support Joomla!

Joomla! 1.5 Documentation

Packages

Package: Joomla-Framework

License

Content on this site is copyright © 2005 - 2008 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution- NonCommercial- ShareAlike 2.5. Some parts of this website may be subject to other licenses.
Source code for file /joomla/filter/filterinput.php

Documentation is available at filterinput.php

  1. <?php
  2. /**
  3.  * @version        $Id: filterinput.php 9764 2007-12-30 07:48:11Z ircmaxell $
  4.  * @package        Joomla.Framework
  5.  * @subpackage    Filter
  6.  * @copyright    Copyright (C) 2005 - 2008 Open Source Matters. All rights reserved.
  7.  * @license        GNU/GPL, see LICENSE.php
  8.  *  Joomla! is free software. This version may have been modified pursuant to the
  9.  *  GNU General Public License, and as distributed it includes or is derivative
  10.  *  of works licensed under the GNU General Public License or other free or open
  11.  *  source software licenses. See COPYRIGHT.php for copyright notices and
  12.  *  details.
  13.  */
  14.  
  15. // Check to ensure this file is within the rest of the framework
  16. defined('JPATH_BASE'or die();
  17.  
  18. /**
  19.  * JFilterInput is a class for filtering input from any data source
  20.  *
  21.  * Forked from the php input filter library by: Daniel Morris <dan@rootcube.com>
  22.  * Original Contributors: Gianpaolo Racca, Ghislain Picard, Marco Wandschneider, Chris Tobin and Andrew Eddie.
  23.  *
  24.  * @author        Louis Landry <louis.landry@joomla.org>
  25.  * @package     Joomla.Framework
  26.  * @subpackage        Filter
  27.  * @since        1.5
  28.  */
  29. class JFilterInput extends JObject
  30. {
  31.     var $tagsArray// default = empty array
  32.     
  33.     var $attrArray// default = empty array
  34.  
  35.     
  36.  
  37.     var $tagsMethod// default = 0
  38.     
  39.     var $attrMethod// default = 0
  40.  
  41.     
  42.  
  43.     var $xssAuto// default = 1
  44.     
  45.     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');
  46.     var $attrBlacklist = array ('action''background''codebase''dynsrc''lowsrc')// also will strip ALL event handlers
  47.  
  48.     
  49.  
  50.     /**
  51.      * Constructor for inputFilter class. Only first parameter is required.
  52.      *
  53.      * @access    protected
  54.      * @param    array    $tagsArray    list of user-defined tags
  55.      * @param    array    $attrArray    list of user-defined attributes
  56.      * @param    int        $tagsMethod    WhiteList method = 0, BlackList method = 1
  57.      * @param    int        $attrMethod    WhiteList method = 0, BlackList method = 1
  58.      * @param    int        $xssAuto    Only auto clean essentials = 0, Allow clean blacklisted tags/attr = 1
  59.      * @since    1.5
  60.      */
  61.     function __construct($tagsArray array()$attrArray array()$tagsMethod 0$attrMethod 0$xssAuto 1)
  62.     {
  63.         // Make sure user defined arrays are in lowercase
  64.         $tagsArray array_map('strtolower'(array) $tagsArray);
  65.         $attrArray array_map('strtolower'(array) $attrArray);
  66.  
  67.         // Assign member variables
  68.         $this->tagsArray    = $tagsArray;
  69.         $this->attrArray    = $attrArray;
  70.         $this->tagsMethod    = $tagsMethod;
  71.         $this->attrMethod    = $attrMethod;
  72.         $this->xssAuto        = $xssAuto;
  73.     }
  74.  
  75.     /**
  76.      * Returns a reference to an input filter object, only creating it if it doesn't already exist.
  77.      *
  78.      * This method must be invoked as:
  79.      *         <pre>  $filter = & JFilterInput::getInstance();</pre>
  80.      *
  81.      * @static
  82.      * @param    array    $tagsArray    list of user-defined tags
  83.      * @param    array    $attrArray    list of user-defined attributes
  84.      * @param    int        $tagsMethod    WhiteList method = 0, BlackList method = 1
  85.      * @param    int        $attrMethod    WhiteList method = 0, BlackList method = 1
  86.      * @param    int        $xssAuto    Only auto clean essentials = 0, Allow clean blacklisted tags/attr = 1
  87.      * @return    object    The JFilterInput object.
  88.      * @since    1.5
  89.      */
  90.     function getInstance($tagsArray array()$attrArray array()$tagsMethod 0$attrMethod 0$xssAuto 1)
  91.     {
  92.         static $instances;
  93.  
  94.         $sig md5(serialize(array($tagsArray,$attrArray,$tagsMethod,$attrMethod,$xssAuto)));
  95.  
  96.         if (!isset ($instances)) {
  97.             $instances array();
  98.         }
  99.  
  100.         if (empty ($instances[$sig])) {
  101.             $instances[$signew JFilterInput($tagsArray$attrArray$tagsMethod$attrMethod$xssAuto);
  102.         }
  103.  
  104.         return $instances[$sig];
  105.     }
  106.  
  107.     /**
  108.      * Method to be called by another php script. Processes for XSS and
  109.      * specified bad code.
  110.      *
  111.      * @access    public
  112.      * @param    mixed    $source    Input string/array-of-string to be 'cleaned'
  113.      * @param    string    $type    Return type for the variable (INT, FLOAT, BOOLEAN, WORD, ALNUM, CMD, BASE64, STRING, ARRAY, PATH, NONE)
  114.      * @return    mixed    'Cleaned' version of input parameter
  115.      * @since    1.5
  116.      * @static
  117.      */
  118.     function clean($source$type='string')
  119.     {
  120.         // Handle the type constraint
  121.         switch (strtoupper($type))
  122.         {
  123.             case 'INT' :
  124.             case 'INTEGER' :
  125.                 // Only use the first integer value
  126.                 preg_match('/-?[0-9]+/'(string) $source$matches);
  127.                 $result (int) $matches[0];
  128.                 break;
  129.  
  130.             case 'FLOAT' :
  131.             case 'DOUBLE' :
  132.                 // Only use the first floating point value
  133.                 preg_match('/-?[0-9]+(\.[0-9]+)?/'(string) $source$matches);
  134.                 $result (float) $matches[0];
  135.                 break;
  136.  
  137.             case 'BOOL' :
  138.             case 'BOOLEAN' :
  139.                 $result = (bool) $source;
  140.                 break;
  141.  
  142.             case 'WORD' :
  143.                 $result = (string) preg_replace'/[^A-Z_]/i'''$source );
  144.                 break;
  145.  
  146.             case 'ALNUM' :
  147.                 $result = (string) preg_replace'/[^A-Z0-9]/i'''$source );
  148.                 break;
  149.  
  150.             case 'CMD' :
  151.                 $result = (string) preg_replace'/[^A-Z0-9_\.-]/i'''$source );
  152.                 $result ltrim($result'.');
  153.                 break;
  154.  
  155.             case 'BASE64' :
  156.                 $result = (string) preg_replace'/[^A-Z0-9\/+=]/i'''$source );
  157.                 break;
  158.  
  159.             case 'STRING' :
  160.                 $filter    JFilterInput::getInstance();
  161.                 $result = (string) $filter->_remove($filter->_decode((string) $source));
  162.                 break;
  163.  
  164.             case 'ARRAY' :
  165.                 $result = (array) $source;
  166.                 break;
  167.  
  168.             case 'PATH' :
  169.                 $pattern '/^[A-Za-z0-9_-]+[A-Za-z0-9_\.-]*([\\\\\/][A-Za-z0-9_-]+[A-Za-z0-9_\.-]*)*$/';
  170.                 preg_match($pattern(string) $source$matches);
  171.                 $result (string) $matches[0];
  172.                 break;
  173.  
  174.             case 'USERNAME' :
  175.                 $result = (string) preg_replace'/[\x00-\x1F\x7F<>"\'%&]/'''$source );
  176.                 break;
  177.  
  178.             default :
  179.                 // Are we dealing with an array?
  180.                 $filter    JFilterInput::getInstance();
  181.                 if (is_array($source)) {
  182.                     foreach ($source as $key => $value)
  183.                     {
  184.                         // filter element for XSS and other 'bad' code etc.
  185.                         if (is_string($value)) {
  186.                             $source[$key$filter->_remove($filter->_decode($value));
  187.                         }
  188.                     }
  189.                     $result $source;
  190.                 else {
  191.                     // Or a string?
  192.                     if (is_string($source&& !empty ($source)) {
  193.                         // filter source for XSS and other 'bad' code etc.
  194.                         $result $filter->_remove($filter->_decode($source));
  195.                     else {
  196.                         // Not an array or string.. return the passed parameter
  197.                         $result $source;
  198.                     }
  199.                 }
  200.                 break;
  201.         }
  202.         return $result;
  203.     }
  204.  
  205.     /**
  206.      * Function to determine if contents of an attribute is safe
  207.      *
  208.      * @static
  209.      * @param    array    $attrSubSet    A 2 element array for attributes name,value
  210.      * @return    boolean True if bad code is detected
  211.      * @since    1.5
  212.      */
  213.     function checkAttribute($attrSubSet)
  214.     {
  215.         $attrSubSet[0strtolower($attrSubSet[0]);
  216.         $attrSubSet[1strtolower($attrSubSet[1]);
  217.         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));
  218.     }
  219.  
  220.     /**
  221.      * Internal method to iteratively remove all unwanted tags and attributes
  222.      *
  223.      * @access    protected
  224.      * @param    string    $source    Input string to be 'cleaned'
  225.      * @return    string    'Cleaned' version of input parameter
  226.      * @since    1.5
  227.      */
  228.     function _remove($source)
  229.     {
  230.         $loopCounter 0;
  231.  
  232.         // Iteration provides nested tag protection
  233.         while ($source != $this->_cleanTags($source))
  234.         {
  235.             $source $this->_cleanTags($source);
  236.             $loopCounter ++;
  237.         }
  238.         return $source;
  239.     }
  240.  
  241.     /**
  242.      * Internal method to strip a string of certain tags
  243.      *
  244.      * @access    protected
  245.      * @param    string    $source    Input string to be 'cleaned'
  246.      * @return    string    'Cleaned' version of input parameter
  247.      * @since    1.5
  248.      */
  249.     function _cleanTags($source)
  250.     {
  251.         /*
  252.          * In the beginning we don't really have a tag, so everything is
  253.          * postTag
  254.          */
  255.         $preTag        null;
  256.         $postTag    $source;
  257.         $currentSpace false;
  258.         $attr '';     // moffats: setting to null due to issues in migration system - undefined variable errors
  259.  
  260.         // Is there a tag? If so it will certainly start with a '<'
  261.         $tagOpen_start    strpos($source'<');
  262.  
  263.         while ($tagOpen_start !== false)
  264.         {
  265.             // Get some information about the tag we are processing
  266.             $preTag            .= substr($postTag0$tagOpen_start);
  267.             $postTag        substr($postTag$tagOpen_start);
  268.             $fromTagOpen    substr($postTag1);
  269.             $tagOpen_end    strpos($fromTagOpen'>');
  270.  
  271.             // Let's catch any non-terminated tags and skip over them
  272.             if ($tagOpen_end === false{
  273.                 $postTag        substr($postTag$tagOpen_start +1);
  274.                 $tagOpen_start    strpos($postTag'<');
  275.                 continue;
  276.             }
  277.  
  278.             // Do we have a nested tag?
  279.             $tagOpen_nested strpos($fromTagOpen'<');
  280.             $tagOpen_nested_end    strpos(substr($postTag$tagOpen_end)'>');
  281.             if (($tagOpen_nested !== false&& ($tagOpen_nested $tagOpen_end)) {
  282.                 $preTag            .= substr($postTag0($tagOpen_nested +1));
  283.                 $postTag        substr($postTag($tagOpen_nested +1));
  284.                 $tagOpen_start    strpos($postTag'<');
  285.                 continue;
  286.             }
  287.  
  288.             // Lets get some information about our tag and setup attribute pairs
  289.             $tagOpen_nested    (strpos($fromTagOpen'<'$tagOpen_start +1);
  290.             $currentTag        substr($fromTagOpen0$tagOpen_end);
  291.             $tagLength        strlen($currentTag);
  292.             $tagLeft        $currentTag;
  293.             $attrSet        array ();
  294.             $currentSpace    strpos($tagLeft' ');
  295.  
  296.             // Are we an open tag or a close tag?
  297.             if (substr($currentTag01== '/'{
  298.                 // Close Tag
  299.                 $isCloseTag        true;
  300.                 list ($tagName)    explode(' '$currentTag);
  301.                 $tagName        substr($tagName1);
  302.             else {
  303.                 // Open Tag
  304.                 $isCloseTag        false;
  305.                 list ($tagName)    explode(' '$currentTag);
  306.             }
  307.  
  308.             /*
  309.              * Exclude all "non-regular" tagnames
  310.              * OR no tagname
  311.              * OR remove if xssauto is on and tag is blacklisted
  312.              */
  313.             if ((!preg_match("/^[a-z][a-z0-9]*$/i"$tagName)) || (!$tagName|| ((in_array(strtolower($tagName)$this->tagBlacklist)) && ($this->xssAuto))) {
  314.                 $postTag        substr($postTag($tagLength +2));
  315.                 $tagOpen_start    strpos($postTag'<');
  316.                 // Strip tag
  317.                 continue;
  318.             }
  319.  
  320.             /*
  321.              * Time to grab any attributes from the tag... need this section in
  322.              * case attributes have spaces in the values.
  323.              */
  324.             while ($currentSpace !== false)
  325.             {
  326.                 $fromSpace        substr($tagLeft($currentSpace +1));
  327.                 $nextSpace        strpos($fromSpace' ');
  328.                 $openQuotes        strpos($fromSpace'"');
  329.                 $closeQuotes    strpos(substr($fromSpace($openQuotes +1))'"'$openQuotes +1;
  330.  
  331.                 // Do we have an attribute to process? [check for equal sign]
  332.                 if (strpos($fromSpace'='!== false{
  333.                     /*
  334.                      * If the attribute value is wrapped in quotes we need to
  335.                      * grab the substring from the closing quote, otherwise grab
  336.                      * till the next space
  337.                      */
  338.                     if (($openQuotes !== false&& (strpos(substr($fromSpace($openQuotes +1))'"'!== false)) {
  339.                         $attr substr($fromSpace0($closeQuotes +1));
  340.                     else {
  341.                         $attr substr($fromSpace0$nextSpace);
  342.                     }
  343.                 else {
  344.                     /*
  345.                      * No more equal signs so add any extra text in the tag into
  346.                      * the attribute array [eg. checked]
  347.                      */
  348.                     if ($fromSpace != '/'{
  349.                         $attr substr($fromSpace0$nextSpace);
  350.                     }
  351.                 }
  352.  
  353.                 // Last Attribute Pair
  354.                 if (!$attr && $fromSpace != '/'{
  355.                     $attr $fromSpace;
  356.                 }
  357.  
  358.                 // Add attribute pair to the attribute array
  359.                 $attrSet[$attr;
  360.  
  361.                 // Move search point and continue iteration
  362.                 $tagLeft        substr($fromSpacestrlen($attr));
  363.                 $currentSpace    strpos($tagLeft' ');
  364.             }
  365.  
  366.             // Is our tag in the user input array?
  367.             $tagFound in_array(strtolower($tagName)$this->tagsArray);
  368.  
  369.             // If the tag is allowed lets append it to the output string
  370.             if ((!$tagFound && $this->tagsMethod|| ($tagFound && !$this->tagsMethod)) {
  371.  
  372.                 // Reconstruct tag with allowed attributes
  373.                 if (!$isCloseTag{
  374.                     // Open or Single tag
  375.                     $attrSet $this->_cleanAttributes($attrSet);
  376.                     $preTag .= '<'.$tagName;
  377.                     for ($i 0$i count($attrSet)$i ++)
  378.                     {
  379.                         $preTag .= ' '.$attrSet[$i];
  380.                     }
  381.  
  382.                     // Reformat single tags to XHTML
  383.                     if (strpos($fromTagOpen'</'.$tagName)) {
  384.                         $preTag .= '>';
  385.                     else {
  386.                         $preTag .= ' />';
  387.                     }
  388.                 else {
  389.                     // Closing Tag
  390.                     $preTag .= '</'.$tagName.'>';
  391.                 }
  392.             }
  393.  
  394.             // Find next tag's start and continue iteration
  395.             $postTag        substr($postTag($tagLength +2));
  396.             $tagOpen_start    strpos($postTag'<');
  397.       &n