How to enable CSRF protection in JSF-Spring integrated application
Asked Answered
T

2

1

I have a JSF-Spring integrated application. Spring security is also integrated in this application. These are the versions in my application:

  • JSF 2.2
  • Spring 4.0.3.RELEASE
  • Spring Security 3.2.4.RELEASE

As per the JSF doc all the POST request in JSF2.x [or even old versions] will be CSRF protected. However I am able to penetrate my application with CSRF attack.

I tried a different JSF2.2 only [no Spring] example application, in that case I can see this example application is CSRF protected.

So my understanding is, the JSF/Spring /Spring security combination is giving issue in my original application. Unfortunately there is no helping info from the log files.

I can try with the Spring Security CSRF protection. In that case the challenge is I need to edit the code in all POST cases.

I am looking to enable JSF CSRF protection to avoid this code change. Any suggestion?

I am doing my testing with Pinata.

To answered 12/11, 2014 at 11:41 Comment(5)
Implemented CSRF protection with OWASP_CSRFGuard_Project. This approach gives me the flexibility for not to add code changes all over.To
When you write "avoid code change", does this include JSF code, or do you only mean java code?Degreeday
least modification involved would be by modifying form elements. as for AJAX you have to add it to the ajax.settings presendSusanasusanetta
"However I am able to penetrate my application with CSRF attack." without stating what, it is hard to believe... Might be some application code error?Litman
@To you need to accept an answer or provide comments if not satisfied with the answersSusanasusanetta
S
7

Tested with Spring 4.3.7 and Spring Security 4.2.2.

You need to add a CSRF token to every form in your application. Any PATCH, POST, PUT and DELETE will be protected by Spring security (for the basic verbs). To avoid inserting a hidden input in every form manually you can create a FormRenderer on top of the provided one :

import com.sun.faces.renderkit.html_basic.FormRenderer;

import javax.el.ELContext;
import javax.el.ExpressionFactory;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import java.io.IOException;

public class FormWithCSRFRenderer extends FormRenderer {

    @Override
    public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
        log.debug("FormWithCSRFRenderer - Adding CSRF Token to form element");
        ELContext elContext = context.getELContext();
        ExpressionFactory expFactory = context.getApplication().getExpressionFactory();

        ResponseWriter writer = context.getResponseWriter();
        writer.startElement("input", component);
        writer.writeAttribute("type", "hidden", null);
        writer.writeAttribute("name", expFactory.createValueExpression(elContext, "${_csrf.parameterName}", String.class).getValue(elContext), null);
        writer.writeAttribute("value", expFactory.createValueExpression(elContext, "${_csrf.token}", String.class).getValue(elContext), null);
        writer.endElement("input");
        writer.write("\n");
        super.encodeEnd(context, component);
    }
}

Then register it to override the FormRenderer by setting it in faces-config.xml :

<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
              version="2.2">
    <render-kit>
        <renderer>
            <component-family>javax.faces.Form</component-family>
            <renderer-type>javax.faces.Form</renderer-type>
            <renderer-class>com.acme.FormWithCSRFRenderer</renderer-class>
        </renderer>
    </render-kit>
</faces-config>

Also don't forget to enable CSRF in your spring context :

<security:http auto-config="true" entry-point-ref="preAuthenticatedProcessingFilterEntryPoint"
               use-expressions="true">
    <security:csrf/>
    <security:access-denied-handler error-page="/exception/accessDenied.xhtml"/>

    <security:intercept-url pattern="/**" access="hasAnyRole('ROLE_ADMINISTRATOR','ROLE_GUEST')"/>
    <security:intercept-url pattern="/exception/accessDenied.xhtml" access="permitAll"/>
</security:http>

For your AJAX calls, you will also need to add this token in the data of any protected HTTP Verb. You can retrieve the token directly from the DOM.

Susanasusanetta answered 24/4, 2017 at 15:29 Comment(5)
How do you add CSRF in JSF AJAX ?Iene
@ameya you should add it to the header of your requests. you can grab the token value from the form input. you will still need the hidden input with the token from the server in a DOM element so that you can grab it. inspect your page you will find the token in a input fieldSusanasusanetta
@Iene create another SO question please. in any case, adding a breakpoint when Spring check for the Token will help you check which one is registeredSusanasusanetta
Deleted my older comment. By overriding the jquery beforeSend method I can add the CSRF header in the request call. Now my question is which one is correct _csrf (this is already there in Form Data) or X-CSRF-TOKEN. Also, should we have two CSRT token one in request header (overriding the jquery beforeSend method ) and the other one in form data (override the request renderer method)? or one of them is OK?Iene
@Iene as I said, I'm not sure so you should add a breakpoint where it check against registered token to see which one is allowed.Susanasusanetta
B
-1

@Ameya you can check from below: Reusing ViewState value in other session (CSRF)

I gave the example in order to ignore ajax partialSubmit="true" case. I recommend to ignore it if there is not any huge impact on data when a manipulation is made on partial submit request.

If you want to add csrf to partialSubmit, you should consider following approches:

  • Add an "event" property and a "listener". create a jQuery and add it into "listener". In jQuery, you should add csrf token value with "_csrf" name into form data of ajax request.
  • By analyzing the primafaces files, get the renderer/handler/..vs class of the ajax partial submit. Extend and customize it. Add this custom file to faces-config.xml.

Not try:

  • Adding crsf token to FacesContext on JSF controller of the xhtml file. Since the ajax request has not a csrf token value in request body, controller won't be called.
  • Adding csrf token on server side. This is not the way of csrf token working. This means you are not checking client. Additionally, when the request comes to server, it is hard to write something on its body, since it is buffered.
Bravo answered 30/6, 2021 at 14:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.