There are 3 basic ways to protect any service that returns JSON or JavaScript against the <script> tag hack.
If you can only support one of these protections, this is the one to chose. Including a secret in the request allows the server to reject the request as invalid before any actions take place. It is common to include the secret in the URL, however this is a slightly vulnerable position for a secret since it is likely to turn up in web server log files and so on.
DWR uses a secret in a POST body to allow the server to deny forged requests. (This secret comes from the JSESSIONID cookie read using Javascript). You can turn this protection off (to allow cross-domain requests for public data) by setting the crossDomainSessionSecurity=false init-param in web.xml.
A future version of DWR will not use the JSESSIONID cookie but have a different way to get security credentials. This will allow DWR to work with systems that have had their session cookies marked as HttpOnly.
Since <script> tag remoting does not allow you to process the JSON or JavaScript before it is eval()ed you can protect your JSON by forcing it to be manipulated before it is eval()ed. There are 3 ways to do this:
/* { 'data':'protected' } */
. When this is eval()ed, there will be no result, however if you have fetched the data using XHR or iframe, you can do some string manipulation before eval() to remove the leading /* and trailing */.
while(1);
'. Since this is an infinite loop, it causes browsers to hang, and maybe give an error message. Either way the script does not get executed.
function while() {}
'. None of the current browsers allow this though.
while(1);
' is possibly better than 'while(true);
' in case there are any browsers that allow you to redefine truth.throw new Error("message");
'. This is a neat solution in that it allows you to explain what is wrong to users that get the message by mistake.
Since browsers use GET to process <script>
tags, you can prevent <script>
tags from working by denying GET requests for some JavaScript resource. This is the most common solution, however it is also perhaps the weakest.
Firstly XHR-POST doesn't work with older versions of Safari, so some support for GET is often useful.
More importantly future versions of Firefox are touted to include cross-domain XHR support. While we don't have exact knowledge of how this will happen, it would be foolish to base your security plans on this technique holding up.
Finally, we're working in an environment where new possibilities are popping up every day - betting your security on a system that works more by fluke than design isn't a great idea in my opinion.
By default DWR denies POST requests for belt and braces security, however this is customizable using the allowGetForSafariButMakeForgeryEasier=true init-param in web.xml.