其他分享
首页 > 其他分享> > 为什么我的spring安全配置似乎拒绝有效的csrf令牌

为什么我的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