Uploaded image for project: 'Shiro'
  1. Shiro
  2. SHIRO-578

Race condition between Session.touch() and AbstractValidatingSessionManager.

    XMLWordPrintableJSON

    Details

    • Type: Bug
    • Status: Open
    • Priority: Major
    • Resolution: Unresolved
    • Affects Version/s: 1.2.2, 1.3.0
    • Fix Version/s: None
    • Component/s: Session Management
    • Labels:

      Description

      I think I've found a race condition using Shiro 1.3.0 and a trivial web application between Session.touch() and the AbstractValidatingSessionManager.validateSessions(). The reason I'm looking that this code so carefully for race conditions is that our production web site sees the occasional (a few a week) UnknownSessionException in a way that is likely presenting the user with a 500 error screen.

      I set my breakpoints at SimpleSession.touch() and AbstractValidatingSessionManager.validateSession().

      Here are the steps to reproduce:

      1. I login, which creates my session and takes me to my default Servlet/JSP.
      2. I refresh my web page, which will hit the Servlet again, but I stop at the "touch" breakpoint.
      3. I wait for the ExecutorServiceSessionValidationScheduler to hit my breakpoint in AbstractValidatingSessionManager.
      4. Step through and let AbstractValidatingSessionManager.validateSession() invalidate my session and delete it because it is expired.
      5. Resume at the "touch" breakpoint and I get a 500 error page with UnknownSessionException:

      It really seems like there could be a race condition were a user reloads a web page right as their session is expiring. The user get the session, but the ExecutorServiceSessionValidationScheduler immediately invalidates it before the user can "touch" it. This results in the Servlet failing if it needs something out the Session (such as the Principal - it has no problem retrieving the Subject).

      Here is my shiro.ini file:

      [main]
      cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager
      securityManager.cacheManager = $cacheManager
      
      # Set the timeout to one minute, so we don't have to wait so long
      sessionDAO = org.apache.shiro.session.mgt.eis.MemorySessionDAO
      sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
      sessionManager.globalSessionTimeout = 60000
      sessionManager.sessionDAO = $sessionDAO
      securityManager.sessionManager = $sessionManager
      sessionValidationScheduler = org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler
      sessionValidationScheduler.interval = 60000
      sessionValidationScheduler.sessionManager = $sessionManager
      securityManager.sessionManager.sessionValidationScheduler = $sessionValidationScheduler
      #securityManager.sessionManager.sessionValidationInterval = 6000
      securityManager.sessionManager.sessionValidationSchedulerEnabled = true
      
      testRealm = com.hcs.shiro.race.TestRealm
      testFilter = org.apache.shiro.web.filter.authc.FormAuthenticationFilter
      testFilter.loginUrl = /login.jsp
      testFilter.usernameParam = j_username
      testFilter.passwordParam = j_password
      testFilter.rememberMeParam = rememberMe
      testFilter.successUrl = /myAccount
      
      logout = org.apache.shiro.web.filter.authc.LogoutFilter
      logout.redirectUrl = /login.jsp
      
      # Permission
      [urls]
      /logout = logout
      /** = testFilter
      

      As you can see, the only custom code I'm using is the TestRealm, which authenticates anyone who provides a username and password.

      Here is my trivial Servlet:

      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      	Subject subject = SecurityUtils.getSubject();
      	Object principal = subject.getPrincipal();
      	request.setAttribute(ATTR_PRINCIPAL, principal.toString());
      	ServletContext context = getServletContext();
              RequestDispatcher dispatcher = context.getRequestDispatcher("/myAccount.jsp");
              dispatcher.forward(request, response);
      }
      

      It fails on "subject.getPrincipal()" in the scenario described above and has been seen to fail in my production code in a similar line with a similar error message.

      Here is the exception:

      org.apache.shiro.session.StoppedSessionException: Session with id [1c59c589-2920-4ba6-b3c8-118997110844] has been explicitly stopped.  No further interaction under this session is allowed.
      	org.apache.shiro.session.mgt.SimpleSession.validate(SimpleSession.java:270)
      	org.apache.shiro.session.mgt.AbstractValidatingSessionManager.doValidate(AbstractValidatingSessionManager.java:186)
      	org.apache.shiro.session.mgt.AbstractValidatingSessionManager.validate(AbstractValidatingSessionManager.java:143)
      	org.apache.shiro.session.mgt.AbstractValidatingSessionManager.doGetSession(AbstractValidatingSessionManager.java:120)
      	org.apache.shiro.session.mgt.AbstractNativeSessionManager.lookupSession(AbstractNativeSessionManager.java:148)
      	org.apache.shiro.session.mgt.AbstractNativeSessionManager.lookupRequiredSession(AbstractNativeSessionManager.java:152)
      	org.apache.shiro.session.mgt.AbstractNativeSessionManager.getAttribute(AbstractNativeSessionManager.java:249)
      	org.apache.shiro.session.mgt.DelegatingSession.getAttribute(DelegatingSession.java:141)
      	org.apache.shiro.session.ProxiedSession.getAttribute(ProxiedSession.java:121)
      	org.apache.shiro.subject.support.DelegatingSubject.getRunAsPrincipalsStack(DelegatingSubject.java:469)
      	org.apache.shiro.subject.support.DelegatingSubject.getPrincipals(DelegatingSubject.java:153)
      	org.apache.shiro.subject.support.DelegatingSubject.getPrincipal(DelegatingSubject.java:149)
      	com.hcs.shiro.race.MyAccount.doGet(MyAccount.java:30)
      	javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
      

      Original discussion for in Shiro Users' Group: http://shiro-user.582556.n2.nabble.com/Race-condition-between-Session-touch-and-AbstractValidatingSessionManager-td7581196.html

      Note: My trivial test case uses Shiro 1.3.0 code while my production code is using 1.2.2.

        Attachments

        1. ShiroRace2.war
          7 kB
          Stephen McCants

          Activity

            People

            • Assignee:
              Unassigned
              Reporter:
              smccants Stephen McCants
            • Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

              Dates

              • Created:
                Updated: