Ordinarily, I try to stay away from defending against specific attacks. Instead, I prefer to write software that is resistant to attacks using generalized defenses like input protections. However, there are some cases where an attack is so pervasive and its results so devastating that it deserves its own set of defenses.
Cross site scripting (XSS) is one such attack.
Sitting at #3 in OWASP’s most recent top ten vulnerabilities, XSS is at once easy and astoundingly hard to protect against. It’s easy because in many cases, simple input sanitization like we’ve been discussing on this blog can reduce your exposure. Astoundingly hard because there are so many attack vectors, and input controls only address some of them.
In this post, I’ll present some techniques for shoring up your application’s defenses against XSS.
X-XSS-Protection: Cobbled Together…
The first technique seems to almost have been thrown together in desperation. As XSS took its place in the OWASP top 10 and refused to leave, it became obvious that developers just weren’t giving it the attention it needed. So, browser manufacturers tried to take steps to protect applications. They came up with X-XSS-Protection. All browsers don’t support it, but Internet Explorer after version 8, Chrome, Opera, Edge, and Safari do support it, so it’s worth understanding.
X-XSS-Protection involves the browser’s built-in XSS protection. It’s only as good as the vendor’s implementation, but the goal, like HTMLPurifier, is to detect an XSS attack and take steps against it. Is it perfect? No. Is it better nothing? Absolutely! It has several options that control how it reacts to a detected attack, but I’m most interested in the default:
This turns the browser’s protections on. In this mode, the browser will attempt to detect and neutralize any XSS attacks. It will also continue displaying the page. This seems like a good compromise. If you trust the X-XSS-Protection directive to do its job, then we might as well trust it to neutralize the attack and not inconvenience our customers.
There are a couple of other options, like:
X-XSS-Protection: 1; mode=block
In that mode, if the browser detects an XSS attack, it will stop rendering the page. That might seem more safe, but think about it: if we trust X-XSS-Protection to detect the attack, then we know it’ll block said attack. In other words, if we trust X-XSS-Protection enough to enable it, we might as well trust it to neutralize the attack. Of course, specific circumstances might incline you to use this option, and that’s fine! Let your experience be your guide!
Another option would be to log a report if X-XSS-Protection detects an attack. For example:
X-XSS-Protection: 1; report=/asfg_protectinginput_php/warntheworld.php
In this case, the browser will attempt to sanitize the XSS and then report the error to the URI you provide. If your company has no security team monitoring the network, applications servers, and applications, it might be a good idea to set this so you get warnings if malicious actors try to use your site to launch XSS attacks.
You can set X-XSS-Protection in two ways. For PHP, you’d use this before you send any HTML to the browser:
For Java, you’d do this at the top of a JSP or in a servlet before sending HTML to the client:
Attacks from Afar (or from A-near…)
Some XSS attack vectors involve HTML Frames. So, the browser community has evolved a means to protect against the introduction of scripting from a frame: X-Frame-Options.
You can stop that sort of nonsense with X-Frame-Options. The code would look like this:
X-Frame-Options: ALLOW-FROM https://www.no_evi_here.com/coolcode/
- Chrome: Basic
- Edge: Full
- FireFox (1.9.2 and after, Basic; 18 and after, full)
- Internet Explorer (starting with 8.0)
- Opera (basic)
- Safari (basic, after 4.0; otherwise, full)
For PHP, you can enable it with this function (before sending any HTML to the client):
header("X-Content-Security-Policy: ALLOW-FROM https://www.no_evi_here.com/coolcode/");
From a Tomcat servlet, you can do about the same thing with something like this:
If you plan ahead, X-Frame-Options can give you another layer of protection.
Tag-Teaming with the Browser
We can help the browser protect itself another way: by explicitly telling it what character sets (encoding type) our web application is using. There are multiple character sets that are well-described here, and when a browser first hits your site, it has no idea which one you’re using. Why should you be explicit? Because malicious actors have learned to take advantage of a browser’s good nature. If you don’t set the encoding type on your server application, the browser will try to guess. Generally speaking, it guesses well, but malicious actors have learned how to use non-default encoding to fool the browser into executing XSS attacks.
Setting the character set is pretty easy: just use a META tag in the HTML HEAD. The same technique would work in PHP or Java/Tomcat:
<head> <title>Explicitly Setting the App’s Encoding</title> <meta http-equiv="Content-type" content="text/html;charset=UTF-8"> </head>
When to Deviate from Conviction
Like I said the opening paragraph, I prefer to protect my applications by applying broad security principles like input sanitization and validation. That approach protects my applications against many different kinds of attacks, even ones that I don’t anticipate. There are times, though, when a specific kind of attack is so pervasive and dangerous that it warrants its own kinds of protection. XSS is just one of those kinds of attacks. The techniques I’ve summarized in this post should help you protect your server and your customers from XSS attacks.
Important note: None of these techniques replace input sanitization or other forms of protection. Rather, it augments them. Without input sanitization, your application is just cannon fodder for attackers, so please don’t interpret this post as a suggestion not to use it!
Have you used either X-XSS-Protection or X-Frame-Options? What’s your experience been? Let me know in the comments!
by Terrance A. Crow
Terrance has been writing professionally since the late 1990s — yes, he’s been writing since the last century! Though he started writing about programming techniques and security for Lotus Notes Domino, he went on to write about Microsoft technologies like SQL Server, ActiveX Data Objects, and C#. He now focuses on application security for professional developers because… Well, you’ve watched the news. You know why!