X-XSS-Protection header only helps protect against certain reflected XSS attacks. It does nothing for stored XSS attacks. Don’t rely on it to protect your site from XSS!
What it can do: Block reflected XSS attacks
Reflected XSS occurs when a malicious query parameter in a page’s URL is rendered unsanitized on the page.
The XSS protection against this is not implemented uniformly across browsers (and it’s not supported at all by Firefox). But the basic idea is that the browser searches through the html looking for a script tag that exactly matches the script tag in the URL. If it finds a match, then the page is prevented from loading.
As an example, suppose our page takes the ‘user’ query parameter from the URL and displays it in a welcome message:
We’ll turn off Chrome’s default XSS auditor by including the following header in our server responses (this is not something you should normally do):
Now we’ll replace ‘Mario’ with a script tag and see what happens:
X-XSS-Protection turned off, the script runs and an alert box appears.
Now we’ll turn the
X-XSS-Protection back on by including the following header in our server responses (this is the usual setting we recommend):
X-XSS-Protection: 1; mode=block
We’re blocked when we attempt to reload the page:
We see the following message in the Chrome dev console:
The XSS Auditor blocked access to ‘http://localhost:8080/todos?user=%3Cscript%3Ealert(1)%3C/script%3E’ because the source code of a script was found within the request. The server sent an ‘X-XSS-Protection’ header requesting this behavior.
Here we have an example of the
X-XSS-Protection in action. It has signaled to the browser to block the loading of the page, because
appears both in the URL and within the page’s html.
We generally recommend setting
X-XSS-Protection: 1; mode=block, which (as we saw above) prevents the loading of the page if a reflected script is detected. An alternative is to set
X-XSS-Protection: 1, which signals the browser to sanitize the reflected script and then continue to load the page.
What it can’t do: Block stored XSS attacks
Stored XSS occurs when a user submits malicious input to the website, the website stores this input, and then redisplays it, unsanitized, on one or more pages on the site.
As an example, suppose the todos on our site are displayed unsanitized, and the user submits a new todo with an
When the user clicks Submit, the todo is stored server-side and the page refreshes with the updated list of todos from the server. When we hover over the malicious todo we see the alert popup:
X-XSS-Protection does nothing against this sort of attack.
Generally speaking you should include the
X-XSS-Protection header in your server responses:
X-XSS-Protection: 1; mode=block
But realize this only helps protect against certain reflected XSS attacks, and it’s not implemented uniformly across browsers. It will not protect against stored XSS attacks.
So how can you protect your site from stored XSS attacks?
- Have a strong
- Sanitize (or reject) malicious user input before it is stored server-side.
- Always sanitize user input before it is displayed client-side. (Modern frameworks will usually take care of this for you, assuming you follow their best practices.)
- Don’t assume 3rd party libraries (markdown editors, chart creators, etc.) are properly sanitizing all possible user input.