Secure XSS filter PHP function

XSS is a well-known vulnerability of websites. This can cause great harm to website and servers. Most of the web developers do not know about this. This is the main reason why cyber crimes are on the way. Every web developer should know about these website vulnerabilities. In one of my post I wrote some website security tips for web developers  While surfing on internet I came across some code to filter PHP XSS attacks.

License:
This code is public domain, you are free to do whatever you want with it, including adding it to your own project which can be under any license. Put this in a function in your webpage and pass every variable to filter CSS strings. The goal of this function is to be a generic function that can be used to parse almost any input and render it XSS safe.

function RemoveXSS($val) { 
// remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed 
// this prevents some character re-spacing such as
// note that you have to handle splits with n, r, and t later since they *are* allowed in some inputs 
$val = preg_replace('/([x00-x08,x0b-x0c,x0e-x19])/', '', $val); 


// straight replacements, the user should never need these since they're normal characters 
//this prevents like <’IMG SRC=&#X40&#X61&#X76&#X61&#X73&#X63&#X72
&#X69&#X70&#X74&#X3A&#X61&#X6C&#X65&#X72&#X74&#X28
&#X27&#X58&#X53&#X53&#X27&#X29’>
$search = 'abcdefghijklmnopqrstuvwxyz'; 
$search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; 
$search .= '[email protected]#$%^&*()'; 
$search .= '~`";:?+/={}[]-_|''; 
for ($i = 0; $i 
// ;? matches the ;, which is optional 
// 0{0,7} matches any padded zeros, which are optional and go up to 8 chars 


// &#x0040 @ search for the hex values 
$val = preg_replace('/(&#[xX]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val); // with a ; 
// &#00064 @ 0{0,7} matches '0' zero to seven times 
$val = preg_replace('/(&#0{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ; 
} 


// now the only remaining whitespace attacks are t, n, and r 
$ra1 = Array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link',
'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer',
'bgsound', 'title', 'base'); 
$ra2 = Array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate',
'onbeforecopy','onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint',
'onbeforeunload','onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu',
'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete',
'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover',
'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocu
sin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload',
'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout',
'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste',
'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart',
'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange',
'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload'); 
$ra = array_merge($ra1, $ra2); 


$found = true; // keep replacing as long as the previous round replaced something 
while ($found == true) { 
$val_before = $val; 
for ($i = 0; $i 
$pattern = '/'; 
for ($j = 0; $j 
if ($j > 0) { 
$pattern .= '('; 
$pattern .= '(&#[xX]0{0,8}([9ab]);)'; 
$pattern .= '|'; 
$pattern .= '|(&#0{0,8}([9|10|13]);)'; 
$pattern .= ')*'; 
} 
$pattern .= $ra[$i][$j]; 
} 
$pattern .= '/i'; 
$replacement = substr($ra[$i], 0, 2).''.substr($ra[$i], 2); // add in to nerf the tag 
$val = preg_replace($pattern, $replacement, $val); // filter out the hex tags 
if ($val_before == $val) { 
// no replacements were made, so exit the loop 
$found = false; 
} 
} 
} 
return $val; 

}

I also came to know about another XSS filtering function in PHP. It can clean various UTF encodings & nested exploits.

function xss_clean($data)
{
        // Fix &entity\n;
        $data = str_replace(array('&','<','>'), array('&amp;','&lt;','&gt;'), $data);
        $data = preg_replace('/(&#*\w+)[\x00-\x20]+;/u', '$1;', $data);
        $data = preg_replace('/(&#x*[0-9A-F]+);*/iu', '$1;', $data);
        $data = html_entity_decode($data, ENT_COMPAT, 'UTF-8');
 
        // Remove any attribute starting with "on" or xmlns
        $data = preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu', '$1>', $data);
 
        // Remove javascript: and vbscript: protocols
        $data = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2nojavascript...', $data);
        $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2novbscript...', $data);
        $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $data);
 
        // Only works in IE: <span style="width: expression(alert('Ping!'));"></span>
        $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
        $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
        $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu', '$1>', $data);
 
        // Remove namespaced elements (we do not need them)
        $data = preg_replace('#</*\w+:\w[^>]*+>#i', '', $data);
 
        do
        {
                // Remove really unwanted tags
                $old_data = $data;
                $data = preg_replace('#</*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|i(?:frame|layer)|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|title|xml)[^>]*+>#i', '', $data);
        }
        while ($old_data !== $data);
 
        // we are done...
        return $data;
}

Advertisement

Tags: |

Deepanker Verma is the founder of Techlomedia. He is a tech blogger, developer and gadget freak.


Similar Articles

0 Comments

Leave a comment

Comment policy: We love comments and appreciate the time that readers spend to share ideas and give feedback. However, all comments are manually moderated and those deemed to be spam or solely promotional will be deleted.

2020 UseThisTip | Part of Techlomedia Internet Pvt Ltd Developed By Deepanker