CSRF令牌在登录期间到期

时间:2014-12-16 09:22:36

标签: spring spring-security csrf

我正在使用Spring Web应用程序,我需要避免登录页面上的expire csrf令牌问题,因为如果用户等待太长时间并且尝试只登录一种解决csrf问题的方法就是重新加载页面并尝试再次登录。但它不是用户友好的,我想避免这种情况。

第一个问题:一般情况下(春季安全3.2.4)是否可行?没有禁用csrf。

我尝试使用security =" none"对于登录页面和spring seciruty" login_check",但它无法正常工作,我得到无限重定向或者我收到错误,没有映射为url" myhost / login_check"。

第二个问题:我该怎么做?

4 个答案:

答案 0 :(得分:18)

推荐的解决方案

我想说你不应该在生产站点上禁用csrf令牌。您可以使会话(因此csrf令牌)持续更长时间(但通常不应该持续超过一天,特别是对于未登录的用户,因为它是DOS向量),但真正的解决方案可能是自动刷新csrf令牌过期时的登录页面。你可以使用

<META HTTP-EQUIV="REFRESH" CONTENT="csrf_timeout_in_seconds">

在您的登录页面标题中。如果用户让登录页面停留数小时,则不应该打扰他刷新页面。

第二种解决方案

一种可能的解决方案,它不需要您实际存储会话但允许无限超时,您可以使用会话ID和服务器端密钥进行散列生成csrf令牌:

csrf = hash(sessionid+secret)

但是请注意,您需要真正挖掘和覆盖spring-security内部机制,即:

  • 如果请求到达并且没有此类会话,则即时重新创建匿名会话
  • 从会话ID
  • 动态重新创建csrf令牌

选择一种非常安全的散列算法,最好是sha-512。

第三种解决方案

你可以有一个小的javascript定期调用服务器上的无操作页面(就在会话超时之前),从而扩展你的会话。只有当浏览器一直处于打开状态时,才会导致无限会话超时,因此可以减轻DOS方面的负担。

好的,最后一个解决方案

您可以更改CSRF令牌检查代码,并为登录页面禁用它。这实际上是第二个解决方案的同义词,但是特定于登录页面,通常不适用于所有匿名会话。

你可以这样做,例如通过在HttpSecurity中设置自定义RequestMatcher:

http.csrf().requireCsrfProtectionMatcher(new MyCsrfRequestMatcher());
...
class MyCsrfRequestMatcher implements RequestMatcher {
    @Override
    public boolean matches(HttpServletRequest request) {
        return !request.getServletPath().equals("/login");
    }
}

答案 1 :(得分:0)

默认情况下,另一个选项是没有为会话设置超时,然后,当用户通过身份验证时,将超时更改为您想要的任何内容。您可以看到如何执行此操作的示例here

答案 2 :(得分:0)

您还可以使CSRF保护依赖于cookie而非服务器端会话状态。 Spring Security完全支持这一点。

CookieCsrfTokenRepository

如果您的Cookie过期,您只会收到超时。这很好地扩展,因为它基本上是无状态的(从服务器的角度来看)。

@EnableWebSecurity
public class WebSecurityConfig extends
        WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf()
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
    }
}

安德鲁

答案 3 :(得分:0)

在我从事的项目之一中,我实现了以下内容:

  1. 实现一个异常处理程序,该异常处理程序处理CsrfException(就我而言,通常为AccessDeniedException)。将请求转发到控制器方法。

    @ExceptionHandler(AccessDeniedException.class)
    @ResponseStatus(value = HttpStatus.FORBIDDEN)
    public void handleAccessDeniedException(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        request.setAttribute(WebAttributes.ACCESS_DENIED_403, accessDeniedException);
        request.getRequestDispatcher("/Access_Denied").forward(request, response);
    }
    
  2. 在控制器方法中,检查原始请求是否针对登录页面。如果是这样,请在登录页面中显示适当的消息。

    if ("/login".equals(request.getAttribute(RequestDispatcher.FORWARD_SERVLET_PATH))) {
        model.addAttribute("error", "An invalid security token has been detected. Please try again.");
        return "login.jsp";
    } else {
        return "accessDenied.jsp";
    }
    

使用这种方法,用户无需刷新即可重新尝试登录。