; last updated - 7 minutes read

One of the implementation details of JSF hardly any developer knows anything about is the hidden field "javax.faces.ViewState" that's included in every JSF view. On the one hand, it's good that you don't have to know anything about this field. Thing is, this lack of knowledge is filled with many myths. One of these myths is that the view state protects JSF pages against "cross-site request forgery attacks" (CSRF for short). Which is true, but it's not the primary purpose of the field.

I started to investigate the topic because our professional penetration tester managed to run a CSRF attack using a simple replay script. He made me believe that the view state doesn't protect JSF application against CSRF attacks. But what is it for? And while we're at it, how to protect your application against CSRF?

Kudos

At this point I'd like to thank a couple of people who helped me to correct the original version of this article. In particular I'd like to thank John Waterwood, who sent me a link containing in-depth background information about JSF and CSRF and two guys I only know from Reddit, purplepharao and thesystemx, who took the time to explain how a CSRF attack is conducted. Thanks!

CSRF explained

But first, let's shed some light on what CSRF is. According to OWASP,

Cross-Site Request Forgery (CSRF) is a type of attack that occurs when a malicious Web site, email, blog, instant message, or program causes a user’s Web browser to perform an unwanted action on a trusted site for which the user is currently authenticated.

Other than I claimed in the original version of this article, CSRF attack are not man-in-the-middle attack. The attacker does not intercept your request to the server. Instead, they try to trick you into starting a request that's beneficial to the attacker.

Things get interesting when you're logged in to an application. When you click the poisoned link, your browser also sends your session cookie - and the server accepts it, because the request comes from the correct browser. That's called "session riding". Consider online banking. The attacker prepares a link that makes the online banking application transfer money to them. Since you're logged in, and you've sent a valid session id, the banking application has no reason to believe the request is not from you.

Thing is, CSRF attacks work by preparing poisoned links in advance. This means the attacker has to define the link before you're logged in. This, in turn, makes it surprisingly simple to prevent the CSRF attack.

CSRF countermeasures

OWASP recommends adopting the "synchronizer pattern" to detect and prevent CSRF attacks. Add a random hidden field to every request. It's important that the attacker can't guess the value of the field ahead of time. The server knows which value to expect. Hence it can reject old tokens as well as wrong or missing tokens.

OWASP also recommends to use a unique CSRF token per session, and that's precisely what javax.faces.ViewState is (with a twist - more on this later). You can make your protection more effective by using one-time-tokens that are changed with every request.

However, when you do this, you run into all kinds of trouble. These days, users expect your application to cope with the browser's "back" button. And, of course, they expect it to work with the "F5" key - which is basically a replay attack, only it's initiated by you, not by a hostile attacker.

For the sake of completeness, I ought to mention that you should encrypt your random token using a good encryption algorithm such as a 256-bit BASE64 hash, use java.security.SecureRandom to generate truly random tokens and use SSL to make it more difficult to intercept the request.

The true meaning of javax.faces.ViewState

The hidden view state field is very similar to a CSRF token, but it's purpose is to find the session state associated with the request. Basically, that's already what the session id does. But JSF also supports the browser's "back" button. To do so, it stores more than one session state. The JSF implementation I analyzed this morning, Mojarra 2.2.12, uses an LRU cache with up to 15 entries. In other words: you can go back as far as fifteen steps in the history. That should be enough for most use cases.

The view state token consists of two parts. The first part never changes. It's associated with the session. The second part is associated with a certain point of time in history. In other words: it changes with every post request. In that, it's similar to the one-time-token I mentioned above.

However, it never changes on AJAX requests (at least not in Mojarra 2.2.12, the JSF implementation I examined this morning. At the time of writing, I can't say anything about MyFaces or about other versions of Mojarra).

How to protect your JSF application

Earlier versions of JSF weren't sufficiently protected against CSRF, but JSF 2.2 changed that. JSF applications are already protected. You don't have to do anything about CSRF attacks. It doesn't meet the high expectations of our penetration tester (who insist on one-time and per-request-tokens), but it's surprisingly effective.

Do not use the EJSF library

The OWASP project pages contain (at least) two interesting libraries. The first library, ESAPI, seems to be rock-solid and well implemented. Using it makes your application more secure. However, I'm reluctant to recommend it, because I didn't analyze the source code yet. But I did analyze the source code of a second project on the OWASP page. At first glance, it solves precisely our problem. It solves CSRF vulnerabilities by adding a one-time token to the request. But it also offers to solve quite a few other vulnerabilities without being up to the task. It's a university project containing a lot of good ideas, but it's far from being mature enough to put it into production. However, what you can do is to clone the source code repository, to examine the approaches to solve injection attacks, cross-site scripting and CSRF and to implement your own solution based on what you've learned.

Users are responsible, too!

By the way, the replay attack I'm talking about fails if the user has already logged out. Logging out, let alone logging in again some time later, is nothing users appreciate. Maybe you can convince them by telling them their application becomes more vulnerable the longer they stay logged in. But then, user acceptance is a difficult topic, anyways: most users don't deal with important data (or they aren't aware of dealing with important data), so they believe they are secure. Plus, security is annoying, tedious and a distraction from your work.

Wrapping it up

It's astonishing that such a ubiquitous item such as the hidden javax.faces.ViewState field is widely unknown in the JSF community. That's a good sign: developers consider JSF to be mature. But it's also a bad thing, because it gives rise to myths. But it's always fun to bust such myths!

By the way, the BootsFaces project is considering to implement a CSRF protection into our framework. Whether we do that, or not, depends on whether we find a solution to the AJAX problem. It doesn't help anybody to raise security by disabling AJAX, the "back" button or the "F5" button.

Dig deeper

OWASP CSRF prevention sheet

OWASP ESAPI library

Wikipedia on CSRF

in-depth background information about JSF and CSRF

The reddit discussion about this article


Comments