请求参数“_csrf”或头部“X-CSRF-TOKEN”中发现无效的CSRF令牌“null”。

97

在配置了Spring Security 3.2之后,_csrf.token未绑定到请求或会话对象。

这是Spring Security的配置:

<http pattern="/login.jsp" security="none"/>

<http>
    <intercept-url pattern="/**" access="ROLE_USER"/>
    <form-login login-page="/login.jsp"
                authentication-failure-url="/login.jsp?error=1"
                default-target-url="/index.jsp"/>
    <logout/>
    <csrf />
</http>

<authentication-manager>
    <authentication-provider>
        <user-service>
            <user name="test" password="test" authorities="ROLE_USER/>
        </user-service>
    </authentication-provider>
</authentication-manager>

登录页面(login.jsp文件)

<form name="f" action="${contextPath}/j_spring_security_check" method="post" >
    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
    <button id="ingresarButton"
            name="submit"
            type="submit"
            class="right"
            style="margin-right: 10px;">Ingresar</button>
    <span>
        <label for="usuario">Usuario :</label>
        <input type="text" name="j_username" id="u" class="" value=''/>
    </span>
    <span>
        <label for="clave">Contrase&ntilde;a :</label>

        <input type="password"
               name="j_password"
               id="p"
               class=""
               onfocus="vc_psfocus = 1;"
               value="">
    </span>
</form>

并且它会渲染出下一个 HTML:

<input type="hidden" name="" value="" />

结果是403 HTTP状态:

Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.

更新: 经过一些调试,请求对象从DelegatingFilterProxy中得到了正确的输出,但在CoyoteAdapter的469行执行request.recycle()时,它会擦除所有属性...

我使用Tomcat 6.0.36、7.0.50和JDK 1.7进行测试。

我还没有理解这种行为,不过如果有人能指点我一个使用CSRF正常工作的Spring Security 3.2的应用程序示例war包,那将是可能的。


1
你使用的Spring版本是什么?对我来说,这个东西也适用(但是在spring-security.xml中有一些差异),使用Spring 4.0.0 RELEASE (GA)和Spring Security 3.2.0 RELEASE (GA)(虽然它与Struts 2.3.16集成,但我没有尝试过仅使用Spring MVC)。但是,当请求为multipart以上传文件并返回状态403时,它会失败。我正在努力寻找解决方案。 - Tiny
Spring 3.2.6,Spring Security 3.2.0,CSRF token已添加到http请求对象中,会话对象与请求线程相同,但在渲染jsp之前,当退出时会删除所有属性,只留下一个属性...filter_applied。 - Hugo Robayo
@Tiny:你找到multipart问题的解决方案了吗?我也遇到了_完全相同_的问题。 - Rob Johansen
1
@AlienBishop:是的,请查看这个答案(它使用了Spring和Struts的组合)。如果你只有Spring MVC,那么请查看这个答案。需要注意的是web.xml中过滤器的顺序非常重要。MultipartFilter必须在springSecurityFilterChain之前声明。希望能帮到你。谢谢。 - Tiny
11个回答

116

看起来你的Spring应用程序启用了CSRF(跨站点请求伪造)保护。实际上,默认情况下启用了它。

根据spring.io的说法:

什么情况下应该使用CSRF保护?我们的建议是对任何可能由普通用户通过浏览器处理的请求使用CSRF保护。如果你只创建一个由非浏览器客户端使用的服务,你可能希望禁用CSRF保护。

因此,要禁用它:

@Configuration
public class RestSecurityConfig extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable();
  }
}

如果您希望保持CSRF保护功能,则必须在表单中包含csrftoken。您可以这样做:

<form .... >
  ....other fields here....
  <input type="hidden"  name="${_csrf.parameterName}"   value="${_csrf.token}"/>
</form>

你甚至可以将CSRF令牌包含在表单的操作中:

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

2
这应该被接受为答案,因为它不仅解释了要做什么,还解释了在停止这些错误之前应该考虑什么。 - Thomas Carlisle
1
你也可以使用.csrf().ignoringAntMatchers("/h2-console/**") - insan-e
在上述答案中,避免使用查询参数样式。如果这样做,您会将令牌暴露给公众。 - Pramod S. Nikam
问题,我使用了.csrf().disable(),但仍然出现了空令牌错误,这怎么可能?这让我疯狂了整整一个星期。 - undefined

32

你不应该向登录表单中添加内容吗?

<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> 

正如在Spring安全文档这里所述


12

如果您应用security="none",则不会生成任何csrf token。该页面将不会通过安全过滤器。请使用ANONYMOUS角色。

我没有深入研究,但是对我来说它是可行的。

 <http auto-config="true" use-expressions="true">
   <intercept-url pattern="/login.jsp" access="hasRole('ANONYMOUS')" />
   <!-- you configuration -->
   </http>

我之前使用的是security=none,但是按照你的回答更改后问题得到了解决。很棒的是thymeleaf自动添加了csrf令牌。谢谢! - rxx

7
尝试将此更改为:<csrf disabled="true"/>。它应该禁用CSFR。

7

使用Thymeleaf您可以添加:

<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>

6

请勿复制现有答案。 - james.garriss

4

我曾经也有同样的问题。

你的配置使用了security="none",所以无法生成_csrf:

<http pattern="/login.jsp" security="none"/>

你可以将/access配置为"IS_AUTHENTICATED_ANONYMOUSLY",以替换/login.jsp页面的上述配置:

<http>
    <intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
    <intercept-url pattern="/**" access="ROLE_USER"/>
    <form-login login-page="/login.jsp"
            authentication-failure-url="/login.jsp?error=1"
            default-target-url="/index.jsp"/>
    <logout/>
    <csrf />
</http>

2
我认为CSRF只适用于Spring表单。
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

将标签更改为form:form,然后查看它是否正常工作。


你不需要使用Spring表单:http://docs.spring.io/spring-security/site/docs/3.2.7.RELEASE/reference/htmlsingle/#csrf-include-csrf-token - Bart Swennenhuis

1
请查看我的工作示例应用在Github上,并与您的设置进行比较。

我将降级到Spring 3.2.6,希望它能在没有Spring MVC的情况下正常工作。 - Hugo Robayo
是的,它应该没有问题,因为我是基于Spring 3.1.4上的现有应用程序创建了示例应用程序。 - manish
哈哈哈哈哈,太好了,仅仅降级并不是解决办法,bhaiya ji @manish。 - Kuldeep Singh

1

两种解决方案都对我无效。在Spring表单中,唯一有效的方法是:

action="./upload?${_csrf.parameterName}=${_csrf.token}"

REPLACED WITH:

action="./upload?_csrf=${_csrf.token}"

使用Java配置启用csrf的Spring 5


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接