During the last few years, while working as a web developer, I’ve seen awareness of the potential for XSS increase. I’ve also seen frameworks and open source applications come with better tools for avoiding XSS. There is still work to be done, though.
The most common way to avoid XSS is to have a very convenient way to put the escaped contents of a variable into HTML. What this does, is instead of inserting the HTML, <script>
, it inserts the text, <script>
, using the HTML, <script>. In Ruby On Rails and Django, variables are now by default escaped. In Django if you want a variable to not be escaped, you either need to mark it as safe in the python code, or include the template tag “|safe” in the template code. In mustache, you have double-mustaches for escaped ({{name}}
) and triple-mustaches for unescaped ({{{html}}}
). These encourage using the escaped option. With WordPress, using standard interpolated PHP, you have to be more careful. The simple <? echo $name; ?>
will result in an unescaped value. To get an escaped value, the slightly longer <?php echo esc_attr($name); ?>
or <?php esc_attr_e($name ?>
can be used.
The above methods work for values inside and outside of tags, as they escape, at a minimum, <>"'
. The angle bracket escaping deals with variables inserted outside of tags, and the quotes deal with data inside of tags (such as attributes).
There is one important case that they don’t cover, though: sometimes attribute values can do bad things. An href
attribute can be set to javascript code, and if the users click on it, it will run. This is a security problem.
This means that, assuming the data isn’t validated first, the following mustache code is insufficient:
<a href="{{url}}">{{name}}</a>
Before it gets rendered, the URL must be sanitized to remove the javascript:
URL scheme if it’s present.
Different tools have different ways of dealing with this:
- Django doesn’t appear to have anything built-in, but the Bleach library covers this in its sanitization of HTML, at least partially.
- Ruby On Rails has a built-in HTML sanitizer.
- mustache, while extremely convenient for escaping HTML, doesn’t solve the URL problem, and so developers using it will need to look elsewhere. Google Caja would be one option.
- WordPress has my favorite solution of all of these: the esc_url function. Running a full HTML sanitizer is overkill when using templates. I think it got a nice solution out of necessity: WP can be set up so users that aren’t logged in are allowed to comment and leave a link to their website, and malicious users could easily submit a link with a
javascript:
URI scheme. I took a look at the source for esc_uri
and the functions it calls and it seems to handle a lot of cases, including a value starting with javascript:javascript:
and the URI scheme being in upper case.
Most of these aren’t commonly known, and yet many, many apps provide ways to add URLs. Because of this, I think this must be a very common way of doing XSS attacks. It’s frustrating that I don’t have a quick way for developers to get up to speed on this, no matter what tool they’re using.
A better solution
I’d very much like a JSON test suite and a list of recipes to run the test suite in various languages, with extra recipes for languages where there is a framework or a library that makes the code shorter.