Source for file input.php
Documentation is available at input.php
* @package Joomla.Platform
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
* JFilterInput is a class for filtering input from any data source
* Forked from the php input filter library by: Daniel Morris <dan@rootcube.com>
* Original Contributors: Gianpaolo Racca, Ghislain Picard, Marco Wandschneider, Chris Tobin and Andrew Eddie.
* @package Joomla.Platform
* A container for JFilterInput instances.
protected static $instances =
array();
* The array of permitted tags (white list).
* The array of permitted tag attributes (white list).
* The method for sanitising tags: WhiteList method = 0 (default), BlackList method = 1
* The method for sanitising attributes: WhiteList method = 0 (default), BlackList method = 1
* A flag for XSS checks. Only auto clean essentials = 0, Allow clean blacklisted tags/attr = 1
* The list of the default blacklisted tags.
* The list of the default blacklisted tag attributes. All event handlers implicit.
* Constructor for inputFilter class. Only first parameter is required.
* @param array $tagsArray List of user-defined tags
* @param array $attrArray List of user-defined attributes
* @param integer $tagsMethod WhiteList method = 0, BlackList method = 1
* @param integer $attrMethod WhiteList method = 0, BlackList method = 1
* @param integer $xssAuto Only auto clean essentials = 0, Allow clean blacklisted tags/attr = 1
public function __construct($tagsArray =
array(), $attrArray =
array(), $tagsMethod =
0, $attrMethod =
0, $xssAuto =
1)
// Make sure user defined arrays are in lowercase
$tagsArray =
array_map('strtolower', (array)
$tagsArray);
$attrArray =
array_map('strtolower', (array)
$attrArray);
// Assign member variables
* Returns an input filter object, only creating it if it doesn't already exist.
* @param array $tagsArray List of user-defined tags
* @param array $attrArray List of user-defined attributes
* @param integer $tagsMethod WhiteList method = 0, BlackList method = 1
* @param integer $attrMethod WhiteList method = 0, BlackList method = 1
* @param integer $xssAuto Only auto clean essentials = 0, Allow clean blacklisted tags/attr = 1
* @return JFilterInput The JFilterInput object.
public static function &getInstance($tagsArray =
array(), $attrArray =
array(), $tagsMethod =
0, $attrMethod =
0, $xssAuto =
1)
$sig =
md5(serialize(array($tagsArray, $attrArray, $tagsMethod, $attrMethod, $xssAuto)));
if (empty(self::$instances[$sig]))
self::$instances[$sig] =
new JFilterInput($tagsArray, $attrArray, $tagsMethod, $attrMethod, $xssAuto);
return self::$instances[$sig];
* Method to be called by another php script. Processes for XSS and
* @param mixed $source Input string/array-of-string to be 'cleaned'
* @param string $type The return type for the variable:
* UINT: An unsigned integer,
* FLOAT: A floating point number,
* BOOLEAN: A boolean value,
* WORD: A string containing A-Z or underscores only (not case sensitive),
* ALNUM: A string containing A-Z or 0-9 only (not case sensitive),
* CMD: A string containing A-Z, 0-9, underscores, periods or hyphens (not case sensitive),
* BASE64: A string containing A-Z, 0-9, forward slashes, plus or equals (not case sensitive),
* STRING: A fully decoded and sanitised string (default),
* HTML: A sanitised string,
* PATH: A sanitised file path,
* USERNAME: Do not use (use an application specific filter),
* RAW: The raw string is returned with no filtering,
* unknown: An unknown filter will act like STRING. If the input is an array it will return an
* array of fully decoded and sanitised strings.
* @return mixed 'Cleaned' version of input parameter
public function clean($source, $type =
'string')
// Handle the type constraint
switch (strtoupper($type))
// Only use the first integer value
preg_match('/-?[0-9]+/', (string)
$source, $matches);
$result =
@ (int)
$matches[0];
// Only use the first integer value
preg_match('/-?[0-9]+/', (string)
$source, $matches);
$result =
@ abs((int)
$matches[0]);
// Only use the first floating point value
preg_match('/-?[0-9]+(\.[0-9]+)?/', (string)
$source, $matches);
$result =
@ (float)
$matches[0];
$result = (bool)
$source;
$result = (string)
preg_replace('/[^A-Z0-9]/i', '', $source);
$result = (string)
preg_replace('/[^A-Z0-9_\.-]/i', '', $source);
$result =
ltrim($result, '.');
$result = (string)
preg_replace('/[^A-Z0-9\/+=]/i', '', $source);
$result = (string)
$this->_remove((string)
$source);
$result = (array)
$source;
$pattern =
'/^[A-Za-z0-9_-]+[A-Za-z0-9_\.-]*([\\\\\/][A-Za-z0-9_-]+[A-Za-z0-9_\.-]*)*$/';
$result =
@ (string)
$matches[0];
$result = (string)
preg_replace('/[\x00-\x1F\x7F<>"\'%&]/', '', $source);
// Are we dealing with an array?
foreach ($source as $key =>
$value)
// Filter element for XSS and other 'bad' code etc.
// Filter source for XSS and other 'bad' code etc.
// Not an array or string.. return the passed parameter
* Function to determine if contents of an attribute are safe
* @param array $attrSubSet A 2 element array for attribute's name, value
* @return boolean True if bad code is detected
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));
* Internal method to iteratively remove all unwanted tags and attributes
* @param string $source Input string to be 'cleaned'
* @return string 'Cleaned' version of input parameter
protected function _remove($source)
// Iteration provides nested tag protection
* Internal method to strip a string of certain tags
* @param string $source Input string to be 'cleaned'
* @return string 'Cleaned' version of input parameter
// First, pre-process this for illegal characters inside attribute values
// In the beginning we don't really have a tag, so everything is postTag
// Setting to null to deal with undefined variables
// Is there a tag? If so it will certainly start with a '<'.
$tagOpen_start =
strpos($source, '<');
while ($tagOpen_start !==
false)
// Get some information about the tag we are processing
$preTag .=
substr($postTag, 0, $tagOpen_start);
$postTag =
substr($postTag, $tagOpen_start);
$fromTagOpen =
substr($postTag, 1);
$tagOpen_end =
strpos($fromTagOpen, '>');
// Check for mal-formed tag where we have a second '<' before the first '>'
$nextOpenTag =
(strlen($postTag) >
$tagOpen_start) ?
strpos($postTag, '<', $tagOpen_start +
1) :
false;
if (($nextOpenTag !==
false) &&
($nextOpenTag <
$tagOpen_end))
// At this point we have a mal-formed tag -- remove the offending open
$postTag =
substr($postTag, 0, $tagOpen_start) .
substr($postTag, $tagOpen_start +
1);
$tagOpen_start =
strpos($postTag, '<');
// Let's catch any non-terminated tags and skip over them
if ($tagOpen_end ===
false)
$postTag =
substr($postTag, $tagOpen_start +
1);
$tagOpen_start =
strpos($postTag, '<');
// Do we have a nested tag?
$tagOpen_nested =
strpos($fromTagOpen, '<');
if (($tagOpen_nested !==
false) &&
($tagOpen_nested <
$tagOpen_end))
$preTag .=
substr($postTag, 0, ($tagOpen_nested +
1));
$postTag =
substr($postTag, ($tagOpen_nested +
1));
$tagOpen_start =
strpos($postTag, '<');
// Let's get some information about our tag and setup attribute pairs
$tagOpen_nested =
(strpos($fromTagOpen, '<') +
$tagOpen_start +
1);
$currentTag =
substr($fromTagOpen, 0, $tagOpen_end);
$tagLength =
strlen($currentTag);
$currentSpace =
strpos($tagLeft, ' ');
// Are we an open tag or a close tag?
if (substr($currentTag, 0, 1) ==
'/')
list
($tagName) =
explode(' ', $currentTag);
$tagName =
substr($tagName, 1);
list
($tagName) =
explode(' ', $currentTag);
* Exclude all "non-regular" tagnames
* OR remove if xssauto is on and tag is blacklisted
$postTag =
substr($postTag, ($tagLength +
2));
$tagOpen_start =
strpos($postTag, '<');
* Time to grab any attributes from the tag... need this section in
* case attributes have spaces in the values.
while ($currentSpace !==
false)
$fromSpace =
substr($tagLeft, ($currentSpace +
1));
$nextEqual =
strpos($fromSpace, '=');
$nextSpace =
strpos($fromSpace, ' ');
$openQuotes =
strpos($fromSpace, '"');
$closeQuotes =
strpos(substr($fromSpace, ($openQuotes +
1)), '"') +
$openQuotes +
1;
// Find position of equal and open quotes ignoring
if (preg_match('#\s*=\s*\"#', $fromSpace, $matches, PREG_OFFSET_CAPTURE))
$startAtt =
$matches[0][0];
$startAttPosition =
$matches[0][1];
$closeQuotes =
strpos(substr($fromSpace, ($startAttPosition +
strlen($startAtt))), '"') +
$startAttPosition +
strlen($startAtt);
$nextEqual =
$startAttPosition +
strpos($startAtt, '=');
$openQuotes =
$startAttPosition +
strpos($startAtt, '"');
$nextSpace =
strpos(substr($fromSpace, $closeQuotes), ' ') +
$closeQuotes;
// Do we have an attribute to process? [check for equal sign]
if ($fromSpace !=
'/' &&
(($nextEqual &&
$nextSpace &&
$nextSpace <
$nextEqual) ||
!$nextEqual))
$attribEnd =
strpos($fromSpace, '/') -
1;
$attribEnd =
$nextSpace -
1;
// If there is an ending, use this, if not, do not worry.
$fromSpace =
substr($fromSpace, $attribEnd +
1);
if (strpos($fromSpace, '=') !==
false)
// If the attribute value is wrapped in quotes we need to grab the substring from
// the closing quote, otherwise grab until the next space.
if (($openQuotes !==
false) &&
(strpos(substr($fromSpace, ($openQuotes +
1)), '"') !==
false))
$attr =
substr($fromSpace, 0, ($closeQuotes +
1));
$attr =
substr($fromSpace, 0, $nextSpace);
// No more equal signs so add any extra text in the tag into the attribute array [eg. checked]
$attr =
substr($fromSpace, 0, $nextSpace);
if (!$attr &&
$fromSpace !=
'/')
// Add attribute pair to the attribute array
// Move search point and continue iteration
$currentSpace =
strpos($tagLeft, ' ');
// Is our tag in the user input array?
// If the tag is allowed let's append it to the output string.
// Reconstruct tag with allowed attributes
$preTag .=
'<' .
$tagName;
for ($i =
0, $count =
count($attrSet); $i <
$count; $i++
)
$preTag .=
' ' .
$attrSet[$i];
// Reformat single tags to XHTML
if (strpos($fromTagOpen, '</' .
$tagName))
$preTag .=
'</' .
$tagName .
'>';
// Find next tag's start and continue iteration
$postTag =
substr($postTag, ($tagLength +
2));
$tagOpen_start =
strpos($postTag, '<');
// Append any code after the end of tags and return
* Internal method to strip a tag of certain attributes
* @param array $attrSet Array of attribute pairs to filter
* @return array Filtered array of attribute pairs
$count =
count($attrSet);
// Iterate through attribute pairs
for ($i =
0; $i <
$count; $i++
)
// Split into name/value pairs
// Take the last attribute in case there is an attribute with no value
// Remove all "non-regular" attribute names
// AND blacklisted attributes
||
(substr($attrSubSet[0], 0, 2) ==
'on'))))
// XSS attribute value filtering
if (isset
($attrSubSet[1]))
// Trim leading and trailing spaces
$attrSubSet[1] =
trim($attrSubSet[1]);
// Strips unicode, hex, etc
$attrSubSet[1] =
str_replace('&#', '', $attrSubSet[1]);
// Strip normal newline within attr value
$attrSubSet[1] =
preg_replace('/[\n\r]/', '', $attrSubSet[1]);
// Convert single quotes from either side to doubles (Single quotes shouldn't be used to pad attr values)
if ((substr($attrSubSet[1], 0, 1) ==
"'") &&
(substr($attrSubSet[1], (strlen($attrSubSet[1]) -
1), 1) ==
"'"))
$attrSubSet[1] =
substr($attrSubSet[1], 1, (strlen($attrSubSet[1]) -
2));
if (self::checkAttribute($attrSubSet))
// Is our attribute in the user input array?
// If the tag is allowed lets keep it
// Does the attribute have a value?
if (empty($attrSubSet[1]) ===
false)
$newSet[] =
$attrSubSet[0] .
'="' .
$attrSubSet[1] .
'"';
elseif ($attrSubSet[1] ===
"0")
$newSet[] =
$attrSubSet[0] .
'="0"';
// Leave empty attributes alone
$newSet[] =
$attrSubSet[0] .
'=""';
* Try to convert to plaintext
* @param string $source The source string.
* @return string Plaintext string
protected function _decode($source)
foreach ($trans_tbl as $k =>
$v)
$source =
strtr($source, $ttr);
* Escape < > and " inside attribute values
* @param string $source The source string.
* @return string Filtered string
$badChars =
array('<', '"', '>');
$escapedChars =
array('<', '"', '>');
// Process each portion based on presence of =" and "<space>, "/>, or ">
// See if there are any more attributes to process
while (preg_match('#<[^>]*?=\s*?(\"|\')#s', $remainder, $matches, PREG_OFFSET_CAPTURE))
// Get the portion before the attribute value
$quotePosition =
$matches[0][1];
$nextBefore =
$quotePosition +
strlen($matches[0][0]);
// Figure out if we have a single or double quote and look for the matching closing quote
// Closing quote should be "/>, ">, "<space>, or " at the end of the string
$quote =
substr($matches[0][0], -
1);
$pregMatch =
($quote ==
'"') ?
'#(\"\s*/\s*>|\"\s*>|\"\s+|\"$)#' :
"#(\'\s*/\s*>|\'\s*>|\'\s+|\'$)#";
// Get the portion after attribute value
if (preg_match($pregMatch, substr($remainder, $nextBefore), $matches, PREG_OFFSET_CAPTURE))
// We have a closing quote
$nextAfter =
$nextBefore +
$matches[0][1];
$nextAfter =
strlen($remainder);
// Get the actual attribute value
$attributeValue =
substr($remainder, $nextBefore, $nextAfter -
$nextBefore);
$attributeValue =
str_replace($badChars, $escapedChars, $attributeValue);
$alreadyFiltered .=
substr($remainder, 0, $nextBefore) .
$attributeValue .
$quote;
$remainder =
substr($remainder, $nextAfter +
1);
// At this point, we just have to return the $alreadyFiltered and the $remainder
return $alreadyFiltered .
$remainder;
* Remove CSS Expressions in the form of <property>:expression(...)
* @param string $source The source string.
* @return string Filtered string
// Strip any comments out (in the form of /*...*/)
if (!stripos($test, ':expression'))
// Not found, so we are done
// At this point, we have stripped out the comments and have found :expression
// Test stripped string for :expression followed by a '('
// If found, remove :expression
Documentation generated on Tue, 19 Nov 2013 15:05:35 +0100 by phpDocumentor 1.4.3