当前位置: 首页 > 知识库问答 >
问题:

Webflux多部分/表单数据,启用csrf,无论是否上载文件,都会获取无效的csrf令牌

朱梓
2023-03-14

禁用csrf后,我可以上传文件,但我需要启用它。问题仅发生在表单enctype是multipart/form-data时,即带有403的“无效CSRF令牌”。

通常,当我将enctype设置为多部分/表单数据时,即使对于没有文件上载的表单,也会出现相同的错误。

使用此依赖项:

<dependency>
  <groupId>org.synchronoss.cloud</groupId>
  <artifactId>nio-multipart-parser</artifactId>
  <version>...</version>
</dependency>

尝试在表单中包含隐藏的csrf输入,并尝试将其附加到url,但出现相同错误

    <form  method="post" th:action="${'/add/' + id + '/documents?' + _csrf.headerName + '=' + _csrf.token}"  enctype="multipart/form-data">
        <input type="file" name="documents" multiple="multiple">
        <input  type="hidden"
                th:name="${_csrf.headerName}"
                th:value="${_csrf.token}" />
        <input type="hidden" name="_csrf" th:value="${_csrf.token}">
        <button class="btn btn-success btn-l">Upload</button>
    </form>

对于csrf注入,有这样的控制器建议

@ControllerAdvice
public class SecurityAdvice {@ModelAttribute("_csrf")Mono<CsrfToken> csrfToken(final ServerWebExchange exchange) {
    final Mono<CsrfToken> csrfToken = exchange.getAttributeOrDefault(org.springframework.security.web.server.csrf.CsrfToken.class.getName(), Mono.empty());
    return csrfToken;
}

在安全方面,我有以下bean:

 @Bean
    public ServerCsrfTokenRepository csrfTokenRepository() {
        WebSessionServerCsrfTokenRepository repository =
                new WebSessionServerCsrfTokenRepository();
        repository.setHeaderName("X-CSRF-TK");

        return repository;
    }

在我的SecurityWebFilterChain中这样使用它:

.and().csrf().csrfTokenRepository(csrfTokenRepository())

更新:

为几个URL禁用csrf也就足够了。找到了一些示例,但都是基于Servlet的版本。https://sdqali.in/blog/2016/07/20/csrf-protection-with-spring-security-and-angular-js/

共有2个答案

萧嘉茂
2023-03-14

您只需在过滤器链中启用它。

参考文件:

@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
        // ...
        .csrf(csrf -> csrf.tokenFromMultipartDataEnabled(true))
    return http.build();
}
哈栋
2023-03-14

看看Spring Security的官方建议:https://docs.spring.io/spring-security/site/docs/current/reference/html/csrf.html#csrf-多部分

基本上有两种方法:(1)将MultipartFilter放在Spring Security过滤器之前,以及(2)在表单操作中包含CSRF令牌,就像您正在做的那样。第一个选项是推荐的选项:

第一个选项是确保在Spring Security过滤器之前指定MultipartFilter。在Spring Security过滤器之前指定MultipartFilter意味着没有调用MultipartFilter的授权,这意味着任何人都可以在服务器上放置临时文件。但是,只有授权用户才能提交由您的应用程序处理的文件。通常,这是推荐的方法,因为临时文件上载对大多数服务器的影响应该可以忽略。

为确保MultipartFilter在带有java配置的Spring Security过滤器之前指定,用户可以在SpringSecurityFilterChain之前覆盖,如下所示:

public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {

    @Override
    protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
        insertFilters(servletContext, new MultipartFilter());
    }
}

为了确保在使用XML配置的Spring Security过滤器之前指定MultipartFilter,用户可以确保将MultipartFilter的元素放置在web中springSecurityFilterChain之前。xml如下所示:

<filter>
    <filter-name>MultipartFilter</filter-name>
    <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
</filter>
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>MultipartFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

请注意,如果您仍然想使用表单操作,查询参数可能会泄露。尝试将您的“HeaderName”更改为“参数名称”:

<form action="./upload?${_csrf.parameterName}=${_csrf.token}" method="post" enctype="multipart/form-data">

编辑:如果您无法切换到基于servlet的容器(例如Jetty或Tomcat)并且表单操作建议不起作用,最近有一个Stack Overflow线程讨论了这个问题。

一位开发人员报告说,他使用AJAX解决了这个问题:

我通过以下方式解决了此问题:

  • 使用vanilla javascript发送多部分文件,如Mozilla指南中所述
  • 在超文本标记语言标头、元标记中添加_csrf标记,就像使用Ajax发送CSRF标记的Spring指南一样
  • 而不是使用jQuery,直接将其添加到XHR对象

var csrfToken=$(“meta[名称='\u csrf']”)。属性(“内容”);var csrfHeader=$(“meta[名称='\u csrf\u header']”)。属性(“内容”);XHR。setRequestHeader(csrfHeader、csrfToken);XHR。setRequestHeader('Content-Type','multipart/form data;boundary='boundary);XHR。发送(数据)

同一位开发人员向Spring报告了此问题,但尚未引起任何注意。

 类似资料:
  • 我想从angularJS客户端上传文件。我已经启用了CSRF保护,它可以正常工作,但当我尝试上载文件时,会出现错误: 在请求参数“_CSRF”或标头“X-XSRF-Token”上发现无效的CSRF令牌“null”。 但是令牌在请求头中,并且是正确的!当我禁用CSRF保护时,我可以毫无问题地上传文件。 除此之外,CSRF保护工作正常。 以下是我当前的配置: 安全Configuration.java

  • 我正在尝试使用Spring MVC 4上载带有web应用程序的文件,但出现错误: 在请求参数“\u CSRF”或标头“X-CSRF-Token”上发现无效的CSRF令牌“null”。 Spring版本: Spring版本4.1.7。发布 Spring安全4.0.1。发布 代码: web.xml spring web servlet。xml Settings.jsp 提交名为test的文件后,发布请

  • 此文件包含上载文件的表单 上传orm.jsp 我的控制器方法是 我上传时出现以下错误: HTTP状态403-在请求参数“\u CSRF”或标头“X-CSRF-Token”上发现无效的CSRF令牌“null” 我也用过Spring安全。但是我总是给出同样的错误。我尝试了很多,但无法解决它。你能帮忙解决这个问题吗?

  • 我一直在使用Spring Security 3.2.0中的新csrf功能。并注意到它似乎不适用于enctype=“多部分/表单数据”表单。 我有一个简单的Spring形状: 隐藏的csrf输入按预期呈现: 但是请求没有通过csrf检查(如果我删除enctype="multipart/form-data",它会很好地工作)。我发现的唯一方法是附加“?_csrf=...”到我的操作url,这是丑陋的,

  • 配置Spring Security 3.2后,csrf。令牌未绑定到请求或会话对象。 这是Spring Security配置: login.jsp文件 它呈现下一个html: 结果是403 HTTP状态: 在一些调试之后更新,请求对象以良好的形式发出DelegatingFilterProxy,但在CoyoteAdapter的469行中,它执行请求。回收();这会删除所有属性。。。 我使用JDK 1

  • 问题内容: 我在网站上启用了CSRF保护,但是CSRF令牌唯一一次放置在隐藏字段中的时间是何时使用。我正在通过ajax发布数据,并且也需要发送CSRF以防止出现500个错误。 我以为有一种方法可以将CSRF令牌显式地嵌入到页面中,但是我似乎找不到它。 如果页面上没有表单,如何获得CSRF令牌? 问题答案: 您可以通过安全性类获取CSRF令牌名称和值: