jsfcsrfglassfish-4.1

How can I prevent JSF 2.2 from accepting a ViewState from a different session?


I am using JSF 2.2 (Glassfish 4.1). Our webapp has Primefaces 6.0 and Omnifaces 2.6.9 as dependencies. JSF state is stored on server. As an example say I have this form where userModel is a javax.faces.view.ViewScoped bean.

<h:form id="user">
    <p:inputText id="name" value="#{userModel.name}"/>
    <p:inputText id="pass" value="#{userModel.pass}"/>
    <p:commandButton id="create" value="#{msgs.lbl_add}" action="#{userModel.addUser()}"/>
</h:form>

A company scanned our web app for security issues and claims it has a CSRF vulnerability. An attacker can provide a made up form to one of our app users like this to perform unwanted actions.

<!DOCTYPE html>
<html>
    <body>
        <form action="http://appserver:8080/myapp/users.jsf" method="POST">
            <input type="hidden" name="javax.faces.source" value="user:create"/>
            <input type="hidden" name="user:create" value="user:create"/>
            <input type="hidden" name="user" value="user"/>
            <div>
                <input type="text" name="user:name" value="FAKEUSER"/>
                <input type="text" name="user:pass" value="FAKEPASSWORD"/>
                <input type="text" name="javax.faces.ViewState" value="1185295409278172717:-3206872038807094332"/>
            </div>
            <input type="submit" name="submit" value="Create User"/>
        </form>
    </body>
</html>

I read on SO that ViewState is JSF way of preventing CSRF. But following scenario are possible in our web app (with HTTP protocol if it matters).

  1. Attacker visits our app login page to find a valid ViewState in his page source.
  2. Attacker prepares HTML file mentioned above with his ViewState and sends to victim.
  3. Victim opens HTML file in browser and submits (from local file system e.g. file:///C:/... or hosted by local webserver)
  4. User is created.

The conclusion is that our webapp/JSF in this scenario does not check if the received ViewState belongs to the session identified by JSESSIONID.

Isn't this a vulnerability? How can I avoid this?


Solution

  • In our case it was a bug in our application. We also use Deltaspike library and registered a 'Handle-All-Exceptions' class in our app.

    import org.apache.deltaspike.core.api.exception.control.ExceptionHandler;
    import org.apache.deltaspike.core.api.exception.control.Handles;
    import org.apache.deltaspike.core.api.exception.control.event.ExceptionEvent;
    
    @ExceptionHandler
    public class ExceptionDispatcher {
    
        public void processException(@Handles ExceptionEvent<Throwable> evt) {
            // Handle exception by just logging
        }
    }
    

    This also "handled" javax.faces.application.ViewExpiredException which occur if JSF finds a invalid ViewState. As a result the request was normally processed.

    A better implementation will redirect to an error page and invalidate session.