Two important headers that can mitigate XSS are:

  • X-XSS-Protection
  • Content-Security-Policy

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.

Therefore,

X-XSS-Protection: 1

will turn it back on for the user.

The second header, Content Security Policy, is a newer header that controls where an HTML page can load its content from, including JavaScript. Basically including anything other than unsafe-inline as a directive means that injected JavaScript into the page will not execute, and can mitigate both reflected and stored XSS. CSP is a much larger topic than I’m going to cover here, however, detailed information regarding the header can be found here.

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 1 without 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 X-XSS-Protection without 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:

normal

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.

injected

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:

Chrome F12

Viewing source shows us which script has been blocked in red:

Source code

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.

?xss2=<script>document.write("two potato")</script>

abused

Viewing the source shows the attacker has just blocked what they wanted by specifying the source code in the URl:

abused source code

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:

web app hackers handbook quote

So, how can we defend against this? Well, you guessed it, the block directive:

X-XSS-Protection: 1; mode=block

So let’s try this again with that specified:

abused but blocked

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:

CSP example image

Chrome shows us this is solely down to Content Security Policy:

csp chrome error

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.