There's no two ways about it, this is evil. Plain evil. Preventing XSS attacks is hard. The sad fact is I've never seen a customer's app that was correctly encoding data in its Grails GSP views. This means there is a high likelihood your current Grails application has exploitable vulnerabilities in it, right now, in production.
I am saying that not to scare you but to make you realise how important it is that you grasp this issue, and how Grails can help you do this now and in the future.
So what is XSS (cross site scripting) and why should you care? If you already know you can skip this section.
Here's an example that hacks grails.org at the time of writing:
Just click this link - it is harmless, it just outputs a message and patches the DOM to hide the fact it was a bad search - to see how even the best practitioners are easily vulnerable:
In the above you could simply inject code that uses
document.write to pull in an entire remote JS file that uses your
logged in grails.org account to trash plugin data and so on.
In essence they can do anything the user can do with their data and account on your service. This is why you must encode all untrusted data correctly for the context within which it is being rendered. This is the only action needed, it is never optional and you always, always, always have to have this kind of thing in your mind when working on web apps.
You must talk to every developer on your team about this, and really make sure they fully understand the problems and requirements. The person who does not understand this is the weakest link in your application's security. I suggest spending just a few hours together as a team crafting real exploits for your own app to prove you have all mastered the problem. By definition There will be existing exploits if you are reading this article. How severe they are is another matter. Some attacks are only possible if you are a registered user, but these are still a risk.
“Why are we vulnerable to this stuff? The framework should handle all this!” I hear you say. I would wholeheartedly agree, however the implementation is both nuanced and nontrivial and the fact is that you will never be free of the responsibility to understand and protect against these problems. Grails is likely no worse than many other frameworks in this regard. Things could be better though.
As of Grails 2.3 we should have nearly all the common cases covered out of the box, as Lari Hotari has bravely implemented the proposal put forward by Peter Ledbrook and myself some time ago.
With existing pre-2.3 versions of Grails we have the built in codec mechanism we can use either explicitly or implicitly using the default codec. There are however some gotchas due to the different ways content can be rendered.
In a nutshell, to protect your app from code injection XSS exploits you must:
- Set the default
grails.views.default.codecin config to "HTML"
- In any pages where you render some model data to JS code, you must turn OFF default codec on that page and use
encodeAsHTML()explicitly everywhere OR move the code out of the page and write a tag to generate the JS safely (good practice anyway)
- Review all grails core tag invocations for those that do not HTML escape their output by default.
g:messageis a good example of this, you must add
encodeAs="HTML"or other appropriate codec. Tag output is not escaped even if the page or default codec is set. Many tags handle this themselves, as they should. See
p:textin platform-core also as a better replacement.
- Review invocation of tags as methods in GSPs and Controllers to ensure results are encoded by the tag implementation or add it explicitly in the GSP or Controller as appropriate.
- Review plugin tags to make sure they encode and have options for this and if they don't you should JIRA the authors immediately, and fork and patch it yourself to fix your app quickly
- Explicitly encode results of inline groovy script blocks <% ... %> and <%= ... %> or better still remove those and put any special logic into tags. Use of those tags is not a design smell, it is a foul stench indicating total lack of design!
One issue here is that turning on default codec can lead to double encoding of values output by your code or by plugin views, if explicit
encodeAsXXX is currently being used. This is pretty common in existing Grails plugins but the thinking is that double-encoding is better than none. It will produce bad output but that can be noticed and fixed.
The various ways content can be rendered contribute to the trickiness of solving the issue. Here's the difference between them:
- GSP EL expansion in HTML/text sequences: uses default codec or page codec if overrides
- GSP EL expansion in Grails TagLib attributes: no codec is applied as values are passed as their original types to the tag implementation
- Inline groovy and legacy JSP evacuation syntax: no codec applied
Here's some quick examples of various usage scenarios where there is no default codec set (or you are in a page where multiple codecs are required):
This is packed with exploitable vulnerabilities if the end user can set
user. Here's how you would fix it with explicit codecs:
You see the use of three different codecs here. Note that the
params.q value in JS code has to double escaped to be valid.
Also note that the github URL use URL + HTML escaping even though it is a URL path element and not a query value. Why? Well if you look at the truly thrilling RFC 1738 which specifies the valid characters in URLS, for HTTP the query
%xx escaping is supported, and while more characters are permitted in path elements than in query values, we don't have a "URL path element" codec in Grails, so using the URL codec (misnamed, sadly) to apply query-value escaping is a convenient shortcut.
You can see how the JS makes this ugly, and in fact if you moved all the JS generation to a Taglib that managed the escaping property, you could set the page's or application default codec to
HTML and remove all the encodeAsHTML() calls.
So bear all this in mind, and thing about every single evaluation of content in the view. As a general rule:
- Anything that is not the output of a tag must be encoded explicitly or a default or page codec set.
- Any tag output is safer, but make sure the tag handles encoding
- Never use inline Groovy script
Hopefully come 2.3 these problems will go away. The ability for tags to set the codec used will mean that tags rendering JS will automatically escape to JS correctly, no matter what the default codec is. It will also prevent double-encoding.
I'm looking forward to it, but it won't absolve us from the responsibility of understanding and thinking about these things all the time.