问题
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.
回答1:
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.
来源:https://stackoverflow.com/questions/26886121/how-to-enable-csrf-protection-in-jsf-spring-integrated-application