Two important headers that can mitigate XSS are:
So what is the difference?
Well browsers such as Internet Explorer and Chrome include an “XSS auditor” which attempts to help prevent reflected XSS from firing. The first header controls that within the browser.
Details are here, but basically the four supported options are:
X-XSS-Protection: 0 X-XSS-Protection: 1 X-XSS-Protection: 1; mode=block X-XSS-Protection: 1; report=<reporting-uri>
It should be noted that the auditor is active by default, unless the user (or their administrator) has disabled it.
will turn it back on for the user.
What I wanted to show you was the difference between specifying
block, and either not including the header at all (which therefore will take on the setting in the browser) or specifying
block. Also, for good measure I will show you the Content Security Policy mitigation for cross-site scripting.
I will show you a way that if a site has specified
block, how this can be abused.
The linked page has the following code in it:
<script>document.write("one potato")</script><br /> <script>document.write("two potato")</script><br /> three potato
Now if we link straight there from the current page you’re reading, the two script blocks should fire:
To demonstrate how the XSS auditors work, let’s imagine we tried to inject that script into the page ourselves by appending this query string:
?xss1=<script>document.write("one potato")</script>&xss2=<script>document.write("two potato")</script>
Note that the following will not work from Firefox, as at the time of writing Firefox doesn’t include any XSS auditor and therefore is very open to reflected XSS should the visited site be vulnerable. There is the add-on noscript that you can use to protect yourself, should Firefox be your browser of choice. Note the following has been tested in Chrome 64 only. I will also enable your XSS filter in supported browsers by adding
X-XSS-Protection: 1 to the output.
Note how the browser now thinks that the two script blocks have been injected, and therefore blocks them and only outputs the plain HTML. View source to see the code if you don’t believe it is still there.
Viewing F12 developer tools shows us the auditor has done its stuff:
Viewing source shows us which script has been blocked in red:
Now what could an attacker do to abuse the XSS auditor? Well they could manipulate the page to prevent scripts of their choosing to be blocked.
Viewing the source shows the attacker has just blocked what they wanted by specifying the source code in the URl:
Of course, editing their own link is fruitless, they would have to be passing the link onto their victim(s) in some way by sending it to via email, Facebook, Skype, etc …
What are the risks in this? Well The Web Application Hacker’s Handbook puts it better than I could:
So, how can we defend against this? Well, you guessed it, the
X-XSS-Protection: 1; mode=block
So let’s try this again with that specified:
So by specifying
block we can prevent an attacker from crafting links that neutralise our existing script!
So in summary it is always good to specify
block as by default XSS auditors only attempt to block what they think is being injected, which might not actually be the evil script itself.
Content Security Policy then?
Just to demo the difference, if we output a CSP header that prevents inline script and don’t attempt to inject anything:
Chrome shows us this is solely down to Content Security Policy:
To get round this as site developers we can either specify the SHA-256 hash as described in our CSP, or simply move our code to a separate
.js file as long as we white-list
self in our policy. Any attacker injecting inline script will be foiled. Of course the problem with Content Security Policy is that it still seems to be an after-thought and trying to come up with a policy that fits an existing site is very hard unless your site is pretty much static. However, it is a great mitigation if done properly. Any weaknesses in the policy though may be ripe for exploitation. Hopefully I’ll have a post on that in the future if I come across it in any engagements.
*Yeh yeh, you’re not using X-XSS-Protection for evil, but lack of
block of course, and if no-one has messed with the browser settings it is as though
X-XSS-Protection: 1 has been output.