为什么我的spring安全配置似乎拒绝有效的csrf令牌
作者:互联网
我通过几个教程学习了Spring Security(和前端开发).但是,我对CSRF令牌感到非常困惑,而且我显然做错了什么.
我的Spring Security是使用java配置的,当我禁用CSRF(使用下一个代码片段)时,表单提交没有问题.
http.csrf().disable();
我从here开始的理解是我需要完成的步骤是:
1) Use proper verbs
2) Enable csrf protection
3) include _csrf as hidden fields in form.
所有这些步骤听起来很简单,但似乎对我不起作用,我得到错误:
HTTP Status 403 - Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.
当我尝试提交我的注册表时.
我做了一些检查,并将’_csrf’字段作为临时步骤包含在表单上作为可见字段.我假设这个没有填充,但是在我发布表单之前,令牌清晰可见,因此问题似乎发生在数据发布时,而不是数据生成时.
整个表单语法如下所示:
<form method="POST" th:object="${individualRegistrationInfo}">
<table>
<tr>
<td>Name:</td>
<td><input type="text" th:field="*{name}" /></td>
<td th:if="${#fields.hasErrors('name')}"><p th:errors="*{name}">Incorrect Name</p></td>
</tr>
<tr>
<td>Username:</td>
<td><input type="text" th:field="*{username}" /></td>
<td th:if="${#fields.hasErrors('username')}"><p th:errors="*{username}">Incorrect Username</p></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="password" th:field="*{password}" /></td>
<td th:if="${#fields.hasErrors('password')}"><p th:errors="*{password}">Incorrect Password</p></td>
</tr>
<tr>
<td>Email:</td>
<td><input type="email" th:field="*{email}" /></td>
<td th:if="${#fields.hasErrors('email')}"><p th:errors="*{email}">Incorrect Email</p></td>
</tr>
<tr>
<td>Confirm Email:</td>
<td><input type="email" th:field="*{confirmEmail}" /></td>
<td th:if="${#fields.hasErrors('confirmEmail')}"><p th:errors="*{confirmEmail}">Incorrect Email Confirmation</p></td>
</tr>
<tr>
<td>Region:</td>
<td><select th:field="*{regionName}">
<option value="NONE">----Select----</option>
<option th:each="region : ${regions}" th:value="${region}" th:text="${region}">RegionTemplate</option>
</select></td>
<td th:if="${#fields.hasErrors('regionName')}"><p th:errors="*{regionName}">Region Name</p></td>
</tr>
<tr><td>
<span th:text= "${_csrf.parameterName}">CSRF Parm Name</span></td>
<td> <span th:text= "${_csrf.token}">CSRF Token value</span> </td></tr>
<tr>
<td colspan="3">
<input type="submit" value="Register" />
</td>
</tr>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</table>
</form>
上一篇文章提到了一个项目,具体来说:
“一个问题是预期的CSRF令牌存储在HttpSession中,因此只要HttpSession到期,您配置的AccessDeniedHandler就会收到InvalidCsrfTokenException.如果您使用的是默认的AccessDeniedHandler,浏览器将获得HTTP 403并显示错误的错误信息.”
我根本不控制HttpSession,所以我不确定会话超时是设置的(或如何覆盖它.)但是,由于我正在重建应用程序然后直接测试,因此会话超时似乎不太可能.
我使用以下gradle依赖项:
compile group: 'org.springframework', name: 'spring-webmvc', version:'4.0.5.RELEASE'
compile group: 'org.springframework', name: 'spring-context-support', version:'4.0.5.RELEASE'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version:'1.1.4.RELEASE'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-thymeleaf', version:'1.1.4.RELEASE'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-security', version:'1.1.4.RELEASE'
testCompile group: 'org.springframework.security', name:'spring-security-test', version:'4.0.0.M1'
因此,当我通过三个建议的步骤进行最终检查时:
1) Use proper verbs (POST is clearly visible on the form code snippet)
2) Enable csrf protection (http.csrf().disable(); is commented out and _CSRF shows in form)
3) include _csrf as hidden fields in form. (clearly visible on the form code snippet)
因此我到了
4) I am missing something !
任何人都可以建议我可能会缺少什么?
感谢您抽出时间来阅读.
7月29日中午更新
有another guide,其中包括有关CSRF保护的其他信息.
特别是,可以使用AccessDeniedHandler.
我现在已经实现了这个,并在此例程中包含了一些额外的日志记录详细信息.
我目前观察到的是,在提交之前页面上显示的_csrf令牌也在AccessDeniedHandler中报告.
处理程序实现如下:
static class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException)
throws IOException, ServletException {
logger.warn("Arrived in custom access denied handler.");
HttpSession session = request.getSession();
System.out.println("Session is " +session );
System.out.println("Session id = " + session.getId());
System.out.println("Session max interval="+session.getMaxInactiveInterval());
System.out.println("Session last used="+session.getLastAccessedTime());
System.out.println("Time now="+new Date().getTime());
System.out.println();
System.out.println("csrf:");
Object csrf = request.getAttribute("_csrf");
if (csrf==null) {
System.out.println("csrf is null");
} else {
System.out.println(csrf.toString());
if (csrf instanceof DefaultCsrfToken) {
DefaultCsrfToken token = (DefaultCsrfToken) csrf;
System.out.println("Parm name " + token.getParameterName());
System.out.println("Token " + token.getToken());
}
}
System.out.println();
System.out.println("Request:");
System.out.println(request.toString());
System.out.println();
System.out.println("Response:");
System.out.println(response.toString());
System.out.println();
System.out.println("Exception:");
System.out.println(accessDeniedException.toString());
}
}
此附加日志记录的输出是:
Session is org.apache.catalina.session.StandardSessionFacade@579ebbb8
Session id = 7CC03DAFF6BC34E28F5E91974C7E4BA5
Session max interval=1800
Session last used=1406630832436
Time now=1406630878254
csrf:
org.springframework.security.web.csrf.DefaultCsrfToken@763659f8
Parm name _csrf
Token 1e9cb3cf-c111-4b05-aace-4f8480b7d67b
Request:
org.springframework.security.web.context.HttpSessionSecurityContextRepository$Servlet3SaveToSessionRequestWrapper@6a4ce569
Response:
org.springframework.security.web.context.HttpSessionSecurityContextRepository$SaveToSessionResponseWrapper@5e698704
Exception:
org.springframework.security.web.csrf.InvalidCsrfTokenException: Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.
在这种特殊情况下,1e9cb3cf-c111-4b05-aace-4f8480b7d67b的标记嵌入在表格中.
该错误表明找到了CSRF令牌“null”,但打印的行显示请求属性中存在“_csrf”值.
7月30日更新
我已将此问题复制到一个较小的示例项目中,现在可以在GitHub上使用:
https://github.com/Mark-Allen/csrf-example.git
在其初始状态,应用程序接受名称,然后重新显示该名称.
当SecurityWebConfiguration的第52行被注释掉时(请参阅下面的代码需要注释)然后应用程序失败.
http.csrf().disable();
按照@Bart的建议,我已经包含了几个调试语句来显示关键阶段的会话ID.
希望这可能使某人能够理解我做错了什么.
解决方法:
我不知道你是否找到了答案(或转移到电梯的其他部分),但我遇到了类似的问题并找到了解决方案.
我正在运行类似的设置,看起来你也在使用Thymeleaf.
当我阅读Spring Security集成的Thymeleaf文档(http://www.thymeleaf.org/doc/springsecurity.html)时,我注意到他们在表单标记中使用“th:action”而不是“action”.这解决了我的问题.
看起来没有“th:action”属性,Thymeleaf不知道“注入”csrf令牌.
标签:spring,spring-security,csrf,csrf-protection 来源: https://codeday.me/bug/20190628/1320905.html