Spring Security无效的remember-me令牌(系列/令牌)不匹配。意味着以前的cookie盗窃攻击

时间:2013-11-18 16:46:17

标签: spring spring-security tomcat7 remember-me

我有一个使用在tomcat 7中运行的Spring Security3.1.2的GWT应用程序。我使用UsernamePasswordAuthenticationFilter和PersistentTokenBasedRememberMeServices来持久登录数据库。此外,我正在使用tomcat PersistentManager来保存数据库中的会话。 现在我的问题是每次我尝试登录时都会得到无效的记住我的令牌(系列/令牌)不匹配CookieTheftException (我在下面添加了堆栈)。 我尝试从tomcat_sessions表中删除会话,如下所示

  1. 关闭tomcat
  2. 从tomcat_sessions表中删除记录
  3. 启动tomcat
  4. 尝试登录我再次收到CookieTheftException的应用程序......
  5. 我也注意到,即使删除了tomcat_sessions表中的所有记录,当我重新启动tomcat时,tomcat_sessions也会填满我之前删除的所有会话...

    我还删除了Spring persistent_logins表中的所有记录,并禁用了tomcat PersistentManager,但仍然遇到了同样的问题......

    任何想法可能是什么问题?谢谢

    SEVERE: Servlet.service() for servlet [springMvcServlet] in context with path [/brate] threw exception
    org.springframework.security.web.authentication.rememberme.CookieTheftException: Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack.
        at org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices.processAutoLoginCookie(PersistentTokenBasedRememberMeServices.java:102)
        at org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices.autoLogin(AbstractRememberMeServices.java:115)
        at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:97)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
        at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:54)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
        at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
        at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:183)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
        at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
        at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
        at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at com.brate.admin.server.servlet.crawler.GoogleBotFilter.doFilter(GoogleBotFilter.java:202)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
        at java.lang.Thread.run(Thread.java:695)
    

3 个答案:

答案 0 :(得分:25)

为了让我们在同一页面上,我将首先花一点时间来解释我如何理解这种持久令牌机制的工作原理。

从头开始(persistent_logins表中没有条目):

登录成功: 将使用一些随机哈希为用户创建持久性令牌。 为用户创建一个cookie,其上包含令牌详细信息。 为用户创建会话。

只要用户仍然有活动会话,那么在记住身份验证时就不会记住我的功能。

用户会话过期后 记住我的功能,并使用cookie从数据库中获取持久性令牌。如果持久化令牌与cookie中的令牌匹配,那么每个人都很高兴,因为用户获得了身份验证,生成了一个新的随机哈希,并使用它更新了持久令牌,并且用户的cookie也会随后更新。

但是如果来自cookie的令牌与持久令牌的令牌不匹配那么您将获得CookieTheftException。令牌不匹配的最常见原因是快速连续发出了2个或更多请求,其中第一个请求将通过,为后续请求生成新哈希,但第二个请求仍将具有旧令牌它因此导致例外。

要在很大程度上避免CookieTheftException,请确保对您的webapp内容(例如图像,字体,脚本等)的请求不会通过Springs身份验证过滤器。要执行此操作,只需在正常安全配置之上添加另一个<http>配置,并指定您不希望对资源请求有任何安全性(使用相关路径而不是/resources/**):

<http pattern="/resources/**" security="none"/>
<http ... (normal config) ... 

(对于Java Config,请参见此处:How do I define http "security = 'none' in JavaConfig?

如果从数据库中删除用户的令牌(并且其会话已过期),则用户将在下次请求时注销。所以你所说的关于持久性令牌(在persistent_logins表中)自动重新创建的内容几乎没有意义,我非常怀疑是这种情况。 PersistentTokenRepository的{​​{1}}方法仅在登录成功时被调用。

最后,如果您仍然获得异常,则有助于附加createNewToken(PersistentRememberMeToken token)的源并在PersistentTokenBasedRememberMeServices方法中设置断点以查看哪个请求导致CookieTheftException。

我希望这会有所帮助。

答案 1 :(得分:2)

我遇到了同样的错误,并注意到它正在尝试自动登录忽略安全链的每个请求。您可以通过

查看哪些
public void configure(WebSecurity web) throws Exception {
    web
        .debug(true)
        .ignoring()
        .antMatchers("/css/**", "/js/**", "/img/**");
}

在此之后我注意到跳过安全链的js文件和css文件,我删除了那些映射并记住我开始正常工作。

public void configure(WebSecurity web) throws Exception {
    web
        .debug(true)
        .ignoring()
        .antMatchers("/img/**");
}

答案 2 :(得分:1)

我的配置缺少部分是RememberMeAuthenticationProvider。 (http://docs.spring.io/spring-security/site/docs/3.2.2.RELEASE/reference/htmlsingle/#remember-me-impls

请注意,RememberMeAuthenticationProvider的软件包已更改,并且与文档中的软件包不同。

不要忘记为PersistentTokenBasedRememberMeServices和RememberMeAuthenticationProvider定义相同的密钥

这是我的配置:

<s:http auto-config="false"
            use-expressions="true"
            create-session="ifRequired">
        <s:remember-me services-ref="rememberMeServices"
                       authentication-success-handler-ref="rememberMeAuthenticationSuccessHandler"/>
...
</s:http>

 <bean id="rememberMeServices"
          class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
     <constructor-arg index="0" value="${remember.me.key}" />
...
</bean>

<bean id="rememberMeAuthenticationProvider" class=
        "org.springframework.security.authentication.RememberMeAuthenticationProvider">
        <property name="key" value="${remember.me.key}"/>
    </bean>

<s:authentication-manager alias="authenticationManager">
        <s:authentication-provider ref="rememberMeAuthenticationProvider" />
...
    </s:authentication-manager>

Markus Coetzee的回答确实为我解决了问题。谢谢!