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

Posted

技术标签:

【中文标题】如何在 JSF-Spring 集成应用中启用 CSRF 保护【英文标题】:How to enable CSRF protection in JSF-Spring integrated application 【发布时间】:2015-01-09 06:07:05 【问题描述】:

我有一个 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 [无 Spring] 示例应用程序,在这种情况下,我可以看到此示例应用程序受 CSRF 保护。

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

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

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

我正在使用 Pinata 进行测试。

【问题讨论】:

使用 OWASP_CSRFGuard_Project 实现了 CSRF 保护。这种方法让我可以灵活地不用到处添加代码更改。 你写“避免代码更改”的时候,是包括JSF代码,还是只指java代码? modifying form elements 涉及的修改最少。至于 AJAX,你必须将它添加到 ajax.settings presend “但是我能够通过 CSRF 攻击渗透我的应用程序。” 没有说明什么,很难相信...可能是一些应用程序代码错误?跨度> @Sam 如果对答案不满意,您需要接受答案或提供 cmets 【参考方案1】:

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

别忘了在你的 spring 上下文中启用 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 中检索令牌。

【讨论】:

JSF AJAX中如何添加CSRF? @ameya 您应该将其添加到请求的标头中。您可以从表单输入中获取令牌值。您仍然需要在 DOM 元素中使用来自服务器的令牌的隐藏输入,以便您可以抓取它。检查您的页面,您会在输入字段中找到令牌 @Ameya 请创建另一个 SO 问题。无论如何,在 Spring 检查 Token 时添加断点将帮助您检查哪个已注册 删除了我之前的评论。通过覆盖 jquery beforeSend 方法,我可以在请求调用中添加 CSRF 标头。现在我的问题是哪个是正确的_csrf(这已经存在于表单数据中)或X-CSRF-TOKEN。另外,我们是否应该在请求标头中有两个 CSRT 令牌(覆盖 jquery beforeSend 方法),另一个在表单数据中(覆盖请求渲染器方法)?还是其中一个没问题? @Ameya 正如我所说,我不确定,所以您应该添加一个断点,在其中检查已注册的令牌以查看允许使用的令牌。【参考方案2】:

@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 令牌的工作方式。这意味着您没有检查客户端。此外,当请求到达服务器时,很难在其主体上写入内容,因为它是缓冲的。

【讨论】:

以上是关于如何在 JSF-Spring 集成应用中启用 CSRF 保护的主要内容,如果未能解决你的问题,请参考以下文章

如何在电子 webview 中启用节点集成?

JSF - Spring 安全集成问题

如何将ONLYOFFICE与Nextcloud集成

怎么停止和重新启用hadoop的DataNode

Dapr 集成 APISIX 做API网关

如何检查“显示通知”是启用还是禁用?