Montag, 21. September 2015

JSFs/Mojarras Cross Site Request Forgery protection is vulnerable

Intro

With JSF 2.2 the generation of the javax.faces.ViewState hidden parameter (http://jdevelopment.nl/jsf-22/#869) was improved to be sufficiently random. Therefor this parameter can also be use implicit as a CSRF-Token.
This practice is widely recommended for JSF applications.

As also written at http://jdevelopment.nl/jsf-22/#869 or https://blog.dominikschadow.de/2014/04/jsf-stateless-views-and-csrf-protection/ when you use the transient views (<f:view transient="true">...</f:view>) your view becomes stateless and the value of javax.faces.ViewState becomes the fixed value stateless.

This is usually not a problem when using transient view on public pages as there is no login protected session and therefor it makes no sense to submit values in the context of a logged in user. In my opinion you prevent attacks creating thousands of sessions and viewstates which might exceed your servers memory.
This feature was added with Mojarra 2.1.19 (http://balusc.omnifaces.org/2013/02/stateless-jsf.html)

What is the problem

The problem is that Mojarra accepts stateless as a value of javax.faces.ViewState even in non transient views. This means you do not need to guess a valid random javax.faces.ViewState you just use this fixed value.

Details

You can test your application by open a JSF page which has the hidden of javax.faces.ViewState field.
Use FireBug or any other tool to manipulate the DOM or the recorded post submit.
When you change the value of javax.faces.ViewState to any other string you will get a ViewStateExpiredException as expected.
When you change it to stateless your submission will be accepted, the defined action method is triggered and therefor the submitted values are processed by your business logic, e.g. saved into the database. The last one is the critical part of this issue and this actually should be prevented with this CSRF-Token.
The response will look like a view which is newly created; so any session data won't be displayed.

You can test this e.g. at http://www.primefaces.org/showcase/ui/message/messages.xhtml. Make sure that still Mojarra below 2.2.11 is used.
Change the javax.faces.ViewState to another value containing a ':' and you will get a ViewStateExpiredException when submitting. When you change it to stateless you won't get any error. Instead the page seems to work. If you look in details the applications error messages are not displayed correctly. But this is an effect after the actual critical part in the JSF lifecycle.

Solution

Oracle accepted the issue as "a big catch" and fixed it in Mojarra 2.2.11, so you can use a higher version.
Unfortunately they haven't mentioned it in the release notes nor in there security alerts.
One drawback is that because of this patch Mojarra is not working with Servlet API 2.5/Tomcat 6 any more.
Yes officially JSF 2.2.x needs Servlet API 3.0. But if you do not use the flesh scope it will work.

In my project we were not allowed to use another tomcat version but we developers were to lazy to use a lower JSF version.

So if updating to Mojarra 2.2.11 or above is not an option (of what ever reason) for you a PhaseListener is a nice solution:


 public void afterPhase(PhaseEvent arg0){  
  FacesContext fc = FacesContext.getCurrentInstance();  
  HttpServletRequest request = (HttpServletRequest) fc.getExternalContext().getRequest();  
  UIViewRoot viewroot = fc.getViewRoot();  
  if (viewroot != null) {  
    String state = request.getParameter("javax.faces.ViewState");  
    if (!viewroot.isTransient()) {  
      if (state != null && state.equalsIgnoreCase("stateless") && !viewroot.getViewId().equals("/error.xhtml")) {  
       fc.responseComplete();  
       throw new Exception("csrf attack");  
      }  
    }  
   }  
 }  

Basically we test if stateless is used for a non transient view. If so this issue was used by someone

Keine Kommentare:

Kommentar veröffentlichen