Wednesday, December 19, 2007

Cross Site Scripting blacklist vs whitelist

Here are some examples of why blacklists are not effective to protect against cross site scripting attacks. It is always recommended to utilize white list filtering instead. These may be simple examples, but they illustrate the larger issue. Examples are in PHP.

Test 1

Lets start with the basics of filtering for <script> and </script>

Blacklist Filter


$pattern = '/<script>|<\/script>/i';
$replacement = '';
$clean_data= preg_replace($pattern, $replacement, $dirty_data);

XSS which bypasses filter

http://localhost/javascript/blacklist_test.php
?arg=<scrip<script>t>alert('xss')</sc</script>ript>

Discussion

The filter replaces an instance of <script> or </script> with nothing. By nesting <script> within itself ( <scrip<script>> ) the script will remove the first occurrence of the term <script> and then leave a remaining, and now complete, term of <script>

<scrip<script>t>alert('xss')</sc</script>ript> --> <script>alert('xss')</script>

Test 2

Lets modify the filter to prevent the nesting of 'script ' by substituting something for the removed value. Now, the nested script of <scrip<script>t> will become <scripxt>, which will not be executed as javascript by the browser.

Blacklist Filter

$pattern = '/<script>|<\/script>/i';
$replacement = 'x';
$clean_data= preg_replace($pattern, $replacement, $dirty_data);

XSS which bypasses filter

http://localhost/javascript/blacklist_test.php?arg=<img src="" onerror=alert(/xss/)>

Discussion

Cross site scripting attacks don't have to be launched from a simple <script></script>. They can also be executed as part of an img request. As we see here, this doesn't require the script tags at all and passes through the filter fine.

The point of these two simple examples is to demonstrate that for each black list filter there is a crafty method of bypassing the control. If the above attacks hadn't worked here are a few more.

http://localhost/javascript/blacklist_test.php?arg= <BODY ONLOAD=alert('XSS')>
http://localhost/javascript/blacklist_test.php?arg=<IFRAME src="javascript:alert('XSS');"></IFRAME>

Check out http://htmlpurifier.org/live/smoketests/xssAttacks.php to get a better idea of the number of variations possible for XSS attacks

Solution?

Don't rely on black list filtering. For every regular expression you create, someone will find a crafty way to bypass it. This method puts you in an endless mode of reacting to new attack vectors. Instead, use white list filtering. Only allow the types of characters which are required for the particular portion of the application. If the app needs numbers and letters then specifically allow text which contains numbers and letters, if there is anything else then strip it.

Here is an example whitelist function for php which allows alpha numeric characters only

function whitelist($dirty_data)
{


$dirty_array = str_split($dirty_data);
$clean_data="";
foreach($dirty_array as $char)
{
$clean_char=preg_replace( "/[^a-zA-Z0-9_]/", "", $char );
$clean_data=$clean_data.$clean_char;
}


return $clean_data;
}


Remember, black lists are less effective and require more work. Always go with white list filtering.

-Michael Coates