The postings on this site are the contributor's and don’t necessarily represent IBM’s positions, strategies or opinions.
May 30th, 2010 Alex Moffat Posted in EffectiveGWT, GWT | 8 Comments »
This is a minimal set of changes to protect existing GWT 2.0.3 RPC methods from CSRF (aka XSRF) attacks. GWT 2.0.3 has two different RPC mechanisms, the orignal one, and the new direct-eval RPC implementation. I’m going to talk mostly about the orignal RPC mechanism.
There are two parts to the protection. The first relies on the difficulty of setting a header on an HTTP request made from the browser. The only way to add a header to a request via JavaScript is to use an XMLHttpRequest. If you make a simple GET or POST request from a form element or a GET request using an img element or similar you can not modify the headers sent with the request. Access to the XMLHttpRequest is protected by the same origin policy. This means that simply setting a non standard header on the request and then checking on the server that it is present protects against CSRF done via JavaScript because the code setting the header must have been loaded from the server receiving the request. In GWT 2 custom headers are already set on the client for all RPC requests and are checked by the direct-eval RPC RpcServlet, so this protection already exists for direct-eval RPC calls. The only change needed is to add the check to the RemoteServiceServlet. If you look at the source for the doFinish method in the RpcRequestBuilder class you’ll see that two headers, X-GWT-Permutation and X-GWT-Module-Base are added to all XMLHttpRequest RPC calls.
protected void doFinish(RequestBuilder rb) {
rb.setHeader(STRONG_NAME_HEADER, GWT.getPermutationStrongName());
rb.setHeader(MODULE_BASE_HEADER, GWT.getModuleBaseURL());
}
RpcRequestBuilder is used on the client by both the original and direct-eval RPC mechanisms. The servlet for the server side of the direct-eval RPC is called RpcServlet and if you look at the source for the getClientOracle method you can see
String permutationStrongName = getPermutationStrongName();
if (permutationStrongName == null) {
throw new SecurityException(
"Blocked request without GWT permutation header (CSRF attack?)");
}
To add the same protection to the original RPC mechanism you need to add a similar check to the subclasses of RemoteServiceServlet you implement. I use a common subclass of RemoteServiceServlet as a superclass for all my remote service implementations so in that class I overrode the onBeforeRequestDeserialized method to add a check.
@Override
protected void onBeforeRequestDeserialized(String serializedRequest) {
String permutationStrongName = getPermutationStrongName();
if (permutationStrongName == null) {
throw new SecurityException(
"Blocked request without GWT permutation header (CSRF attack?)");
}
}
This change will protect against CSRF attacks made from JavaScript. However, there are apparently ways to set headers from Flash that open a window of vulnerability. There’s more information available about other non JavaScript request mechanisms in part two of the Browser Security Handbook. To protect against this I add another header to the request, this one containing the value of the JSESSIONID cookie, which is accessible on the server as the session id. A cookie value is only accessible to code loaded from the server that set the cookie so checking on the server that the value passed from the client is correct protects against attack by non-javascript mechanisms, provided of course they, and the browser they are using, obey the same origin policy for cookies.
Implementing this second protection requires more work. On the server the principle is the same, retrieve the header value and check it, though you have to handle missing sessions and sessions that have just been created and the client isn’t aware of yet. The example code below would work in the onBeforeRequestDeserialize method.
HttpServletRequest servletRequest = getThreadLocalRequest();
HttpSession session = servletRequest.getSession(false);
// If there is currently no session or if the client doesn't know about it yet then don't check.
// Otherwise the client must provide the id of the session in a header.
if (session != null && !session.isNew()) {
String sessionId = servletRequest.getHeader(SESSION_HEADER);
if (sessionId == null || !sessionId.equals(servletRequest.getSession().getId())) {
throw new SecurityException(
"Blocked request without session header (CSRF attack?)"));
}
}
On the client you have to go to more effort to be able to set an additional header, specifically you need to subclass RpcRequestBuilder and then use your subclass when creating your remote service implementation. For example if the subclass is SubRpcRequestBuilder then the code below creates and configures SomeService correctly.
SomeServiceAsync svc = (SomeServiceAsync)GWT.create(SomeService.class);
((ServiceDefTarget) svc).setRpcRequestBuilder(new SubRpcRequestBuilder());
In SubRpcRequestBuilder you can access the JSESSION id cookie and set the session header in an override of the doFinish method
String sessionId = Cookies.getCookie("JSESSIONID");
if (sessionId != null) {
rb.setHeader(SESSION_HEADER, sessionId);
}
I’m interested if anyone has any suggestions for further security enhancements or holes I’ve overlooked.
May 30th, 2010 at 11:08 pm
Since I posted the question on SO, I realized that the regular RPC mechanism also checks the request headers to prevent a XSRF attack. See method RemoteServiceServlet.checkPermutationStrongName(). Its no longer necessary to add that check on your own; however older versions of GWT may not have that check in place.
However, I think the bigger problem is poor session management. Every website verifies that the user is logged in, but almost nobody verifies that the current user has access to perform an operation. An attacker just needs to get an account on your website and make RPC calls using that login.
It is trivial to enumerate and execute RPC methods in a given GWT website WITHOUT access to sourcecode – see http://code.google.com/p/degwt/. So if a website doesn’t have a sound session management policy, the default protection provided by GWT won’t suffice.
May 31st, 2010 at 3:01 pm
Hmm. I’m not seeing checkPermutationStrongName in RemoteServiceServlet in 2.0.3, which version of GWT are you using? I agree totally about poor session management, you must always apply security / access control / validation checks on the server as well as on the client. The basic “never trust the client” approach is still valid in the gwt world.
May 31st, 2010 at 5:37 pm
re. checkPermutationStrongName – its there on trunk – http://code.google.com/p/google-web-toolkit/source/browse/trunk/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java, and also in the 2.1 branch …
… but your advice is still valid for older versions of GWT. Its a trivial change and is pretty good way of protecting against XSRF.
May 31st, 2010 at 7:39 pm
Excellent. Once we upgrade to 2.1 I’ll remove the permutation header check.
June 1st, 2010 at 5:47 am
Hi Alex!
thanks for sharing!
additionally to learn a bit more about securing a GWT application, your usage of the setRpcRequestBuilder method gave me some good ideas
regards
Michael
June 1st, 2010 at 7:42 am
Glad it was useful.
June 7th, 2010 at 12:42 am
XSRF is an old name for CSRF.
MITRE, OWASP, and WASC all agreed in 2006 to call it CSRF. This is the term of art in the web app sec community. We obviously know what XSRF is, but if folks are searching for GWT CSRF, they may not make it here, despite this being a really good article.
Andrew van der Stock
Project Lead, OWASP Top 10 2007, Project Lead OWASP Developer Guide 2.0, and general henchman on OWASP ESAPI for PHP
June 7th, 2010 at 8:54 pm
I’ve changed the title and the references in the text so they now say CSRF. Thanks for the note.