如何在JSF-Spring集成应用程序中启用CSRF保护

时间:2014-11-12 11:41:53

标签: java spring jsf spring-security csrf-protection

我有一个JSF-Spring集成应用程序。 Spring安全性也集成在此应用程序中。这些是我的应用程序中的版本:

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

根据JSF doc,JSF2.x [甚至旧版本]中的所有POST请求都将受CSRF保护。但是我能够通过CSRF攻击来渗透我的应用程序。

我尝试了一个不同的JSF2.2 [no Spring]示例应用程序,在这种情况下,我可以看到这个示例应用程序受CSRF保护。

所以我的理解是,JSF / Spring / Spring安全组合在我的原始应用程序中给出了问题。 遗憾的是,日志文件中没有帮助信息。

我可以试试Spring Security CSRF protection。在这种情况下,我需要在所有POST情况下编辑代码。

我希望启用JSF CSRF保护以避免此代码更改。有什么建议吗?

我正在使用Pinata进行测试。

2 个答案:

答案 0 :(得分:3)

使用Spring 4.3.7和Spring Security 4.2.2进行测试。

您需要为应用程序中的每个form添加一个CSRF令牌。 Any PATCH, POST, PUT and DELETE将受Spring安全保护(对于基本动词)。 为避免手动在每个表单中插入隐藏输入,您可以在提供的表单上创建FormRenderer:

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);
    }
}

然后通过在faces-config.xml中设置它来注册它以覆盖FormRenderer:

<?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>

也不要忘记在春天的环境中启用CSRF:

<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>

对于您的AJAX调用,您还需要在任何受保护的HTTP谓词的数据中添加此标记。您可以直接从DOM中检索令牌。

答案 1 :(得分:-1)

@Ameya 你可以从下面查看: Reusing ViewState value in other session (CSRF)

我举了这个例子是为了忽略ajax partialSubmit="true"的情况。如果对部分提交请求进行操作时对数据没有太大影响,我建议忽略它。

如果您想将 csrf 添加到 partialSubmit,您应该考虑以下方法:

  • 添加“事件”属性和“侦听器”。创建一个 jQuery 并将其添加到“侦听器”中。在jQuery中,您应该将带有“_csrf”名称的csrf令牌值添加到ajax请求的表单数据中。
  • 通过分析primafaces文件,得到ajax部分提交的renderer/handler/..vs类。扩展和定制它。将此自定义文件添加到 faces-config.xml。

不尝试:

  • 将 crsf 标记添加到 xhtml 文件的 JSF 控制器上的 FacesContext。由于 ajax 请求在请求正文中没有 csrf 令牌值,因此不会调用控制器。
  • 在服务器端添加 csrf 令牌。这不是 csrf 令牌的工作方式。这意味着您没有检查客户端。此外,当请求到达服务器时,很难在它的主体上写一些东西,因为它被缓冲了。