Input Filter > Class.inputfilter.php

Questions? Need Help? Want to share? » PHP Forum
Script Name:
Input Filter

Download:
input-filter.zip

Category:
Form Processors

Archive Content:

Content:

MACOSX

Content:

MACOSX

Content:

MACOSX

Content:

Class.inputfilter.php:


<?php

/** @class: InputFilter (PHP4 & PHP5,  with comments)
  * @project: PHP Input Filter
  * @date: 10-05-2005
  * @version: 1.2.2_php4/php5
  * @author: Daniel Morris
  * @contributors: Gianpaolo Racca,  Ghislain Picard,  Marco Wandschneider,  Chris Tobin and Andrew Eddie.
  * @copyright: Daniel Morris
  * @email: dan <at> rootcube <dot> com
  * @license: GNU General Public License (GPL)
  */
class InputFilter {
    var 
$tagsArray;            // default = empty array
    
var $attrArray;            // default = empty array

    
var $tagsMethod;        // default = 0
    
var $attrMethod;        // default = 0

    
var $xssAuto;           // default = 1
    
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');
    var 
$attrBlacklist = array('action',  'background',  'codebase',  'dynsrc',  'lowsrc');  // also will strip ALL event handlers
        
    /** 
      * Constructor for inputFilter class. Only first parameter is required.
      * @access constructor
      * @param Array $tagsArray - list of user-defined tags
      * @param Array $attrArray - list of user-defined attributes
      * @param int $tagsMethod - 0= allow just user-defined,  1= allow all but user-defined
      * @param int $attrMethod - 0= allow just user-defined,  1= allow all but user-defined
      * @param int $xssAuto - 0= only auto clean essentials,  1= allow clean blacklisted tags/attr
      */
    
function inputFilter($tagsArray = array(),  $attrArray = array(),  $tagsMethod 0,  $attrMethod 0,  $xssAuto 1) {        
        
// make sure user defined arrays are in lowercase
        
for ($i 0$i count($tagsArray); $i++) $tagsArray[$i] = strtolower($tagsArray[$i]);
        for (
$i 0$i count($attrArray); $i++) $attrArray[$i] = strtolower($attrArray[$i]);
        
// assign to member vars
        
$this->tagsArray = (array) $tagsArray;
        
$this->attrArray = (array) $attrArray;
        
$this->tagsMethod $tagsMethod;
        
$this->attrMethod $attrMethod;
        
$this->xssAuto $xssAuto;
    }
    
    
/** 
      * Method to be called by another php script. Processes for XSS and specified bad code.
      * @access public
      * @param Mixed $source - input string/array-of-string to be 'cleaned'
      * @return String $source - 'cleaned' version of input parameter
      */
    
function process($source) {
        
// clean all elements in this array
        
if (is_array($source)) {
            foreach(
$source as $key => $value)
                
// filter element for XSS and other 'bad' code etc.
                
if (is_string($value)) $source[$key] = $this->remove($this->decode($value));
            return 
$source;
        
// clean this string
        
} else if (is_string($source)) {
            
// filter source for XSS and other 'bad' code etc.
            
return $this->remove($this->decode($source));
        
// return parameter as given
        
} else return $source;    
    }

    
/** 
      * Internal method to iteratively remove all unwanted tags and attributes
      * @access protected
      * @param String $source - input string to be 'cleaned'
      * @return String $source - 'cleaned' version of input parameter
      */
    
function remove($source) {
        
$loopCounter=0;
        
// provides nested-tag protection
        
while($source != $this->filterTags($source)) {
            
$source $this->filterTags($source);
            
$loopCounter++;
        }
        return 
$source;
    }    
    
    
/** 
      * Internal method to strip a string of certain tags
      * @access protected
      * @param String $source - input string to be 'cleaned'
      * @return String $source - 'cleaned' version of input parameter
      */
    
function filterTags($source) {
        
// filter pass setup
        
$preTag NULL;
        
$postTag $source;
        
// find initial tag's position
        
$tagOpen_start strpos($source,  '<');
        
// interate through string until no tags left
        
while($tagOpen_start !== FALSE) {
            
// process tag interatively
            
$preTag .= substr($postTag,  0,  $tagOpen_start);
            
$postTag substr($postTag,  $tagOpen_start);
            
$fromTagOpen substr($postTag,  1);
            
// end of tag
            
$tagOpen_end strpos($fromTagOpen,  '>');
            if (
$tagOpen_end === false) break;
            
// next start of tag (for nested tag assessment)
            
$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,  '<');
                continue;
            } 
            
$tagOpen_nested = (strpos($fromTagOpen,  '<') + $tagOpen_start 1);
            
$currentTag substr($fromTagOpen,  0,  $tagOpen_end);
            
$tagLength strlen($currentTag);
            if (!
$tagOpen_end) {
                
$preTag .= $postTag;
                
$tagOpen_start strpos($postTag,  '<');            
            }
            
// iterate through tag finding attribute pairs - setup
            
$tagLeft $currentTag;
            
$attrSet = array();
            
$currentSpace strpos($tagLeft,  ' ');
            
// is end tag
            
if (substr($currentTag,  0,  1) == "/") {
                
$isCloseTag TRUE;
                list(
$tagName) = explode(' ',  $currentTag);
                
$tagName substr($tagName,  1);
            
// is start tag
            
} else {
                
$isCloseTag FALSE;
                list(
$tagName) = explode(' ',  $currentTag);
            }        
            
// excludes all "non-regular" tagnames OR no tagname OR remove if xssauto is on and tag is blacklisted
            
if ((!preg_match("/^[a-z][a-z0-9]*$/i", $tagName)) || (!$tagName) || ((in_array(strtolower($tagName),  $this->tagBlacklist)) && ($this->xssAuto))) {                 
                
$postTag substr($postTag,  ($tagLength 2));
                
$tagOpen_start strpos($postTag,  '<');
                
// don't append this tag
                
continue;
            }
            
// this while is needed to support attribute values with spaces in!
            
while ($currentSpace !== FALSE) {
                
$fromSpace substr($tagLeft,  ($currentSpace+1));
                
$nextSpace strpos($fromSpace,  ' ');
                
$openQuotes strpos($fromSpace,  '"');
                
$closeQuotes strpos(substr($fromSpace,  ($openQuotes+1)),  '"') + $openQuotes 1;
                
// another equals exists
                
if (strpos($fromSpace,  '=') !== FALSE) {
                    
// opening and closing quotes exists
                    
if (($openQuotes !== FALSE) && (strpos(substr($fromSpace,  ($openQuotes+1)),  '"') !== FALSE))
                        
$attr substr($fromSpace,  0,  ($closeQuotes+1));
                    
// one or neither exist
                    
else $attr substr($fromSpace,  0,  $nextSpace);
                
// no more equals exist
                
} else $attr substr($fromSpace,  0,  $nextSpace);
                
// last attr pair
                
if (!$attr$attr $fromSpace;
                
// add to attribute pairs array
                
$attrSet[] = $attr;
                
// next inc
                
$tagLeft substr($fromSpace,  strlen($attr));
                
$currentSpace strpos($tagLeft,  ' ');
            }
            
// appears in array specified by user
            
$tagFound in_array(strtolower($tagName),  $this->tagsArray);            
            
// remove this tag on condition
            
if ((!$tagFound && $this->tagsMethod) || ($tagFound && !$this->tagsMethod)) {
                
// reconstruct tag with allowed attributes
                
if (!$isCloseTag) {
                    
$attrSet $this->filterAttr($attrSet);
                    
$preTag .= '<' $tagName;
                    for (
$i 0$i count($attrSet); $i++)
                        
$preTag .= ' ' $attrSet[$i];
                    
// reformat single tags to XHTML
                    
if (strpos($fromTagOpen,  "</" $tagName)) $preTag .= '>';
                    else 
$preTag .= ' />';
                
// just the tagname
                
} else $preTag .= '</' $tagName '>';
            }
            
// find next tag's start
            
$postTag substr($postTag,  ($tagLength 2));
            
$tagOpen_start strpos($postTag,  '<');            
        }
        
// append any code after end of tags
        
$preTag .= $postTag;
        return 
$preTag;
    }

    
/** 
      * Internal method to strip a tag of certain attributes
      * @access protected
      * @param Array $attrSet
      * @return Array $newSet
      */
    
function filterAttr($attrSet) {    
        
$newSet = array();
        
// process attributes
        
for ($i 0$i <count($attrSet); $i++) {
            
// skip blank spaces in tag
            
if (!$attrSet[$i]) continue;
            
// split into attr name and value
            
$attrSubSet explode('=',  trim($attrSet[$i]));
            list(
$attrSubSet[0]) = explode(' ',  $attrSubSet[0]);
            
// removes all "non-regular" attr names AND also attr blacklisted
            
if ((!eregi("^[a-z]*$", $attrSubSet[0])) || (($this->xssAuto) && ((in_array(strtolower($attrSubSet[0]),  $this->attrBlacklist)) || (substr($attrSubSet[0],  0,  2) == 'on')))) 
                continue;
            
// xss attr value filtering
            
if ($attrSubSet[1]) {
                
// strips unicode,  hex,  etc
                
$attrSubSet[1] = str_replace('&#',  '',  $attrSubSet[1]);
                
// strip normal newline within attr value
                
$attrSubSet[1] = preg_replace('/\s+/',  '',  $attrSubSet[1]);
                
// strip double quotes
                
$attrSubSet[1] = str_replace('"',  '',  $attrSubSet[1]);
                
// [requested feature] convert single quotes from either side to doubles (Single quotes shouldn't be used to pad attr value)
                
if ((substr($attrSubSet[1],  0,  1) == "'") && (substr($attrSubSet[1],  (strlen($attrSubSet[1]) - 1),  1) == "'"))
                    
$attrSubSet[1] = substr($attrSubSet[1],  1,  (strlen($attrSubSet[1]) - 2));
                
// strip slashes
                
$attrSubSet[1] = stripslashes($attrSubSet[1]);
            }
            
// auto strip attr's with "javascript:
            
if (    ((strpos(strtolower($attrSubSet[1]),  'expression') !== false) &&    (strtolower($attrSubSet[0]) == 'style')) ||
                    (
strpos(strtolower($attrSubSet[1]),  'javascript:') !== false) ||
                    (
strpos(strtolower($attrSubSet[1]),  'behaviour:') !== false) ||
                    (
strpos(strtolower($attrSubSet[1]),  'vbscript:') !== false) ||
                    (
strpos(strtolower($attrSubSet[1]),  'mocha:') !== false) ||
                    (
strpos(strtolower($attrSubSet[1]),  'livescript:') !== false
            ) continue;

            
// if matches user defined array
            
$attrFound in_array(strtolower($attrSubSet[0]),  $this->attrArray);
            
// keep this attr on condition
            
if ((!$attrFound && $this->attrMethod) || ($attrFound && !$this->attrMethod)) {
                
// attr has value
                
if ($attrSubSet[1]) $newSet[] = $attrSubSet[0] . '="' $attrSubSet[1] . '"';
                
// attr has decimal zero as value
                
else if ($attrSubSet[1] == "0"$newSet[] = $attrSubSet[0] . '="0"';
                
// reformat single attributes to XHTML
                
else $newSet[] = $attrSubSet[0] . '="' $attrSubSet[0] . '"';
            }    
        }
        return 
$newSet;
    }
    
    
/** 
      * Try to convert to plaintext
      * @access protected
      * @param String $source
      * @return String $source
      */
    
function decode($source) {
        
// url decode
        
$source html_entity_decode($source,  ENT_QUOTES,  "ISO-8859-1");
        
// convert decimal
        
$source preg_replace('/&#(\d+);/me', "chr(\\1)",  $source);                // decimal notation
        // convert hex
        
$source preg_replace('/&#x([a-f0-9]+);/mei', "chr(0x\\1)",  $source);    // hex notation
        
return $source;
    }

    
/** 
      * Method to be called by another php script. Processes for SQL injection
      * @access public
      * @param Mixed $source - input string/array-of-string to be 'cleaned'
      * @param Buffer $connection - An open MySQL connection
      * @return String $source - 'cleaned' version of input parameter
      */
    
function safeSQL($source,  &$connection) {
        
// clean all elements in this array
        
if (is_array($source)) {
            foreach(
$source as $key => $value)
                
// filter element for SQL injection
                
if (is_string($value)) $source[$key] = $this->quoteSmart($this->decode($value),  $connection);
            return 
$source;
        
// clean this string
        
} else if (is_string($source)) {
            
// filter source for SQL injection
            
if (is_string($source)) return $this->quoteSmart($this->decode($source),  $connection);
        
// return parameter as given
        
} else return $source;    
    }

    
/** 
      * @author Chris Tobin
      * @author Daniel Morris
      * @access protected
      * @param String $source
      * @param Resource $connection - An open MySQL connection
      * @return String $source
      */
    
function quoteSmart($source,  &$connection) {
        
// strip slashes
        
if (get_magic_quotes_gpc()) $source stripslashes($source);
        
// quote both numeric and text
        
$source $this->escapeString($source,  $connection);
        return 
$source;
    }
    
    
/** 
      * @author Chris Tobin
      * @author Daniel Morris
      * @access protected
      * @param String $source
      * @param Resource $connection - An open MySQL connection
      * @return String $source
      */    
    
function escapeString($string,  &$connection) {
        
// depreciated function
        
if (version_compare(phpversion(), "4.3.0",  "<")) mysql_escape_string($string);
        
// current function
        
else mysql_real_escape_string($string);
        return 
$string;
    }
}

?>


Other Form Processors Scripts:

WebMaster Resources Home

©RingsWorld.com