我正在使用Spring Web应用程序,我需要避免登录页面上的expire csrf令牌问题,因为如果用户等待太长时间并且尝试只登录一种解决csrf问题的方法就是重新加载页面并尝试再次登录。但它不是用户友好的,我想避免这种情况。
第一个问题:一般情况下(春季安全3.2.4)是否可行?没有禁用csrf。
我尝试使用security =" none"对于登录页面和spring seciruty" login_check",但它无法正常工作,我得到无限重定向或者我收到错误,没有映射为url" myhost / login_check"。
第二个问题:我该怎么做?
答案 0 :(得分:18)
我想说你不应该在生产站点上禁用csrf令牌。您可以使会话(因此csrf令牌)持续更长时间(但通常不应该持续超过一天,特别是对于未登录的用户,因为它是DOS向量),但真正的解决方案可能是自动刷新csrf令牌过期时的登录页面。你可以使用
<META HTTP-EQUIV="REFRESH" CONTENT="csrf_timeout_in_seconds">
在您的登录页面标题中。如果用户让登录页面停留数小时,则不应该打扰他刷新页面。
一种可能的解决方案,它不需要您实际存储会话但允许无限超时,您可以使用会话ID和服务器端密钥进行散列生成csrf令牌:
csrf = hash(sessionid+secret)
但是请注意,您需要真正挖掘和覆盖spring-security内部机制,即:
选择一种非常安全的散列算法,最好是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完全支持这一点。
如果您的Cookie过期,您只会收到超时。这很好地扩展,因为它基本上是无状态的(从服务器的角度来看)。
@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
}
安德鲁
答案 3 :(得分:0)
在我从事的项目之一中,我实现了以下内容:
实现一个异常处理程序,该异常处理程序处理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);
}
在控制器方法中,检查原始请求是否针对登录页面。如果是这样,请在登录页面中显示适当的消息。
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";
}
使用这种方法,用户无需刷新即可重新尝试登录。