Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SessionDestroyedEvent not getting triggered under certain conditions #12140

Closed
attitudemattrs opened this issue Nov 4, 2022 · 13 comments
Closed
Assignees
Labels
in: web An issue in web modules (web, webmvc) type: bug A general bug

Comments

@attitudemattrs
Copy link

attitudemattrs commented Nov 4, 2022

We currently have class SessionDestroyedListener implements ApplicationListener<SessionDestroyedEvent> defined which handles logout auditing and application session cleanup within the onApplicationEvent(SessionDestroyedEvent evt) method.

Within the web.xml we have the HttpSessionEventPublisher defined as below:

   <listener>
       <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
   </listener>

Within the <sec:http> definition we have <sec:session-management invalid-session-url="/login"/> defined, allowing multiple logins for the same user principal. Have also tried the configuration below:

   <sec:session-management invalid-session-url="/login">
         <sec:concurrency-control max-sessions="-1" session-registry-alias="sessionRegistry"/>
   </sec:session-management>

When a user logs into the application we use session.setMaxInactiveInterval(time) to set the max inactive interval for the session to 3 minutes (for example).

Under normal circumstances, if the user logs into the application and logs out or closes the browser completely we get the SessionDestoryedEvent as expected, and we can do our auditing and cleanup. However, in the described scenario below we are seeing an issue.

The issue is best explained using the steps to reproduce below.

Expected Behavior

  1. Open Chrome (or Edge or Firefox) browser
  2. Login to the application using a user 'user1'
  3. Close the browser (so that no tabs or other instances of the browser are open)
  4. Open a new instance of the same browser again, in this case Chrome
  5. Login to the application using the same (or different) user
  6. Close the browser (so that no tabs or other instances of the browser are open)
  7. Wait for the configured MaxInactiveInterval and the SessionDestroyedEvent gets called twice, once for each previous session, as expected.
[2022-11-04 08:19:28,642] DEBUG [org.springframework.security.core.session.SessionRegistryImpl] (default task-138) Registering session E8NJiFuMgIrrBEPAoaC1PHIMfBKGiDHUWans3yZF, for principal user1
[2022-11-04 08:19:28,644] TRACE [org.springframework.security.core.session.SessionRegistryImpl] (default task-138) Sessions used by 'user1' : [E8NJiFuMgIrrBEPAoaC1PHIMfBKGiDHUWans3yZF]
[2022-11-04 08:19:28,659] DEBUG [com.company.app.client.security.AppAuthenticationSuccessHandler] (default task-138) Using default Url: /home
    |
[2022-11-04 08:19:45,252] DEBUG [org.springframework.security.core.session.SessionRegistryImpl] (default task-170) Registering session ofWZ6OHTj-MAnsQ9jVXoz2eidjs380sMtD4xibE4, for principal user1
[2022-11-04 08:19:45,252] TRACE [org.springframework.security.core.session.SessionRegistryImpl] (default task-170) Sessions used by 'user1' : [E8NJiFuMgIrrBEPAoaC1PHIMfBKGiDHUWans3yZF, ofWZ6OHTj-MAnsQ9jVXoz2eidjs380sMtD4xibE4]
[2022-11-04 08:19:45,255] DEBUG [com.company.app.client.security.AppAuthenticationSuccessHandler] (default task-170) Using default Url: /home
   |
[2022-11-04 08:19:56,091] DEBUG [com.company.app.client.security.SessionDestroyedListener] (default task-191) SessionDestroyedListener: about to audit: Successful logout
[2022-11-04 08:19:56,266] DEBUG [com.company.app.cesp.core.bo.dao.DAOBase] (default task-191) Successfully deleted session record for user = user1
[2022-11-04 08:19:56,266] DEBUG [org.springframework.security.core.session.SessionRegistryImpl] (default task-191) Removing session E8NJiFuMgIrrBEPAoaC1PHIMfBKGiDHUWans3yZF from set of registered sessions
[2022-11-04 08:19:56,268] DEBUG [org.springframework.security.core.session.SessionRegistryImpl] (default task-191) Removing session E8NJiFuMgIrrBEPAoaC1PHIMfBKGiDHUWans3yZF from principal's set of registered sessions
[2022-11-04 08:19:56,268] TRACE [org.springframework.security.core.session.SessionRegistryImpl] (default task-191) Sessions used by 'user1' : [ofWZ6OHTj-MAnsQ9jVXoz2eidjs380sMtD4xibE4]
   |
[2022-11-04 08:21:06,860] DEBUG [com.company.app.client.security.SessionDestroyedListener] (ServerService Thread Pool -- 117) SessionDestroyedListener: about to audit: Successful logout
[2022-11-04 08:21:07,023] DEBUG [com.company.app.cesp.core.bo.dao.DAOBase] (ServerService Thread Pool -- 117) Successfully deleted session record for user = user1
[2022-11-04 08:21:07,023] DEBUG [org.springframework.security.core.session.SessionRegistryImpl] (ServerService Thread Pool -- 117) Removing session ofWZ6OHTj-MAnsQ9jVXoz2eidjs380sMtD4xibE4 from set of registered sessions
[2022-11-04 08:21:07,023] DEBUG [org.springframework.security.core.session.SessionRegistryImpl] (ServerService Thread Pool -- 117) Removing session ofWZ6OHTj-MAnsQ9jVXoz2eidjs380sMtD4xibE4 from principal's set of registered sessions
[2022-11-04 08:21:07,023] DEBUG [org.springframework.security.core.session.SessionRegistryImpl] (ServerService Thread Pool -- 117) Removing principal user1 from registry
[2022-11-04 08:21:07,023] TRACE [org.springframework.security.core.session.SessionRegistryImpl] (ServerService Thread Pool -- 117) Sessions used by 'user1' : null

Unexpected Behavior

  1. Open Chrome (or Edge or Firefox) browser
  2. Navigate to some non-application page, such as google.com and leave browser open
  3. Open a new instance of the same browser, in this case Chrome
  4. Login to the application using a user 'user1'
  5. Close the browser (leave the original browser instance open - i.e., google.com)
  6. Open a new instance of the same browser again
  7. Login to the application using the same (or different) user
  8. Close the browser (leave the original browser instance open - i.e., google.com)
  9. Wait for the configured MaxInactiveInterval and the SessionDestroyedEvent gets called ONCE, only for the 2nd application session established.
[2022-11-04 08:22:00,616] DEBUG [org.springframework.security.core.session.SessionRegistryImpl] (default task-197) Registering session yeRXm12kS6hs0OAmizS3ntEhYTkIDer5LVWWyQF0, for principal user1
[2022-11-04 08:22:00,617] TRACE [org.springframework.security.core.session.SessionRegistryImpl] (default task-197) Sessions used by 'user1' : [yeRXm12kS6hs0OAmizS3ntEhYTkIDer5LVWWyQF0]
[2022-11-04 08:22:00,623] DEBUG [com.company.app.client.security.AppAuthenticationSuccessHandler] (default task-197) Using default Url: /home
   |
[2022-11-04 08:22:20,618] DEBUG [org.springframework.security.core.session.SessionRegistryImpl] (default task-218) Registering session RK_J-V2xprRUqWif26Pw91Jtp2m6CJo1yrWWGhXB, for principal user1
[2022-11-04 08:22:20,618] TRACE [org.springframework.security.core.session.SessionRegistryImpl] (default task-218) Sessions used by 'user1' : [yeRXm12kS6hs0OAmizS3ntEhYTkIDer5LVWWyQF0, RK_J-V2xprRUqWif26Pw91Jtp2m6CJo1yrWWGhXB]
[2022-11-04 08:22:20,624] DEBUG [com.company.app.client.security.AppAuthenticationSuccessHandler] (default task-218) Using default Url: /home
   |
[2022-11-04 08:25:25,892] DEBUG [com.company.app.client.security.SessionDestroyedListener] (default task-248) SessionDestroyedListener: about to audit: Successful logout
[2022-11-04 08:25:26,064] DEBUG [com.company.app.cesp.core.bo.dao.DAOBase] (default task-248) Successfully deleted session record for user = user1
[2022-11-04 08:25:26,064] DEBUG [org.springframework.security.core.session.SessionRegistryImpl] (default task-248) Removing session RK_J-V2xprRUqWif26Pw91Jtp2m6CJo1yrWWGhXB from set of registered sessions
[2022-11-04 08:25:26,064] DEBUG [org.springframework.security.core.session.SessionRegistryImpl] (default task-248) Removing session RK_J-V2xprRUqWif26Pw91Jtp2m6CJo1yrWWGhXB from principal's set of registered sessions
[2022-11-04 08:25:26,064] TRACE [org.springframework.security.core.session.SessionRegistryImpl] (default task-248) Sessions used by 'user1' : [yeRXm12kS6hs0OAmizS3ntEhYTkIDer5LVWWyQF0]

As you can see, in the Unexpected Behavior scenario, only the 2nd session triggers the SessionDestroyedEvent and gets cleaned up. The first one remains out there and seems to never expire. Looking through the SessionRegistry in the debugger in subsequent requests, the last activity time remains static at the last activity time before the original browser was closed and the session expired flag remains false for the remaining session. Even if I close the remaining browser windows the session never gets cleaned up until the application server is shutdown. Though in my Un-Expected Behavior scenario I used the same user, this exact behavior can be repeated even if the second user login is done using a completely unique principal. See below:

[2022-11-04 09:12:15,579] DEBUG [org.springframework.security.core.session.SessionRegistryImpl] (default task-248) Registering session 0Hn13-u69b_ARczcAWJvuRmIJy-qwrkzXoqCxmMf, for principal user1
[2022-11-04 09:12:15,580] TRACE [org.springframework.security.core.session.SessionRegistryImpl] (default task-248) Sessions used by 'user1' : [0Hn13-u69b_ARczcAWJvuRmIJy-qwrkzXoqCxmMf]
[2022-11-04 09:12:15,584] DEBUG [com.company.app.client.security.AppAuthenticationSuccessHandler] (default task-248) Using default Url: /home
   |
[2022-11-04 09:12:47,804] DEBUG [org.springframework.security.core.session.SessionRegistryImpl] (default task-297) Registering session 2Vu_bVJ6fB8fGPABBXqgWa7b-Bd88ZKeTaZqh6AH, for principal user2
[2022-11-04 09:12:47,804] TRACE [org.springframework.security.core.session.SessionRegistryImpl] (default task-297) Sessions used by 'user2' : [2Vu_bVJ6fB8fGPABBXqgWa7b-Bd88ZKeTaZqh6AH]
[2022-11-04 09:12:47,810] DEBUG [com.company.app.client.security.AppAuthenticationSuccessHandler] (default task-297) Using default Url: /home
   |
[2022-11-04 09:15:52,965] DEBUG [com.company.app.client.security.SessionDestroyedListener] (default task-318) SessionDestroyedListener: about to audit: Successful logout
[2022-11-04 09:15:53,146] DEBUG [com.company.app.cesp.core.bo.dao.DAOBase] (default task-318) Successfully deleted session record for user = user2
[2022-11-04 09:15:53,146] DEBUG [org.springframework.security.core.session.SessionRegistryImpl] (default task-318) Removing session 2Vu_bVJ6fB8fGPABBXqgWa7b-Bd88ZKeTaZqh6AH from set of registered sessions
[2022-11-04 09:15:53,147] DEBUG [org.springframework.security.core.session.SessionRegistryImpl] (default task-318) Removing session 2Vu_bVJ6fB8fGPABBXqgWa7b-Bd88ZKeTaZqh6AH from principal's set of registered sessions
[2022-11-04 09:15:53,148] DEBUG [org.springframework.security.core.session.SessionRegistryImpl] (default task-318) Removing principal user2 from registry
[2022-11-04 09:15:53,148] TRACE [org.springframework.security.core.session.SessionRegistryImpl] (default task-318) Sessions used by 'user2' : null

Note, I even changed the user principal to be unique within the application code by adding a unique UUID to it and it didn't matter, despite the fact that even with the same user logging in, the principal was always unique. Just the existence of another instance of the same browser (even browsing a different site) seems to affect the behavior.

One final remark. In the Unexpected Behavior scenario, if I stop at step 5 and wait, without logging in again, the SessionDestroyedEvent does get triggered and the session is cleaned up as expected.

This behavior was repeated with Chrome, Edge, and Firefox.

I'm not ruling out configuration issues on the application side, but this certainly seems like odd and incorrect behavior to me that seems outside the control of the application or its configuration.

Any assistance in this matter would be greatly appreciated!

Thanks

@attitudemattrs attitudemattrs added status: waiting-for-triage An issue we've not yet triaged type: bug A general bug labels Nov 4, 2022
@marcusdacoregio marcusdacoregio added in: web An issue in web modules (web, webmvc) and removed status: waiting-for-triage An issue we've not yet triaged labels Nov 4, 2022
@marcusdacoregio
Copy link
Contributor

Hi @attitudemattrs, thanks for the report.

In order to help us to debug the issue, can you provide a minimal, reproducible sample where we can start the application and go through those steps?

@attitudemattrs
Copy link
Author

attitudemattrs commented Nov 6, 2022

Hi @marcusdacoregio, appreciate your response.

I believe I have been able to create a small Spring Boot example based on a slightly modified version of the Security a Web Application project found here.

I have provided a zip file SessionDestroyedEvent-12140.zip that contains the Maven project. The project is a Java 11 project but is setup to build using Java 1.8 code compatibility. Within the provided zip file you will find a SessionDestroyedEvent-12140 directory containing a demo directory. The demo directory contains the clean Maven project source.

A few things to note about the application:

  • When the application is started, just go to http://localhost:8080 and you will be presented with a Login page. There are two valid users defined in WebSecurityConfig, user1 and user2. The password for both is password. If you enter a valid user and password a session will be created.
  • The SessionDestoryedEventListener will receive the SessionDestroyedEvent similar to what our application does. The handler simply outputs the principal and session id of the destroyed event.
  • The application.properties file contains only one property, server.servlet.session.timeout=1m, which sets the session inactivity timeout to 1 minute (feel free to change).
  • I did not include SessionRegistry logging in this example, so the only logging you will see is from the SessionDestroyedEvent.

To build the project just execute mvnw clean package from the demo directory. You can then execute java -jar target/demo-0.0.1.jar using Java 1.8 or higher.

The steps to reproduce are identical to the ones I provided earlier, though I will repeat them here just to add some clarifying statements and to show exactly what I did.

Expected Behavior

Make sure you do steps 4 - 6 within the server.servlet.session.timeout period of completing step 3. Do not wait for the user1 session to timeout

  1. Open Chrome (or Edge or Firefox) browser
  2. Go to http://localhost:8080 and login to the application using user1 with password password
  3. Close the browser (so that no tabs or other instances of the browser are open)
  4. Open a new instance of the same browser again, in this case Chrome
  5. Go to http://localhost:8080 and login to the application using user2 (could have used the same user) with password password
  6. Close the browser (so that no tabs or other instances of the browser are open)
  7. Wait for the configured server.servlet.session.timeout period (might take a little bit longer) and you will see the SessionDestoryedEvent gets triggered twice, once for the user1 session and one for the user2 session.
SessionDestroyedEvent: principal = org.springframework.security.core.userdetails.User [Username=user1, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_USER]] : [A9A592AE140BFA6A549B408EDB0E85ED]
SessionDestroyedEvent: principal = org.springframework.security.core.userdetails.User [Username=user2, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_USER]] : [5378E1FA5BEB04E5FE3AAB2B9D42C1CC]

Unexpected Behavior

Make sure you do steps 6 - 9 within the server.servlet.session.timeout period on completing step 5. Do not wait for the user1 session to timeout

  1. Open Chrome (or Edge or Firefox) browser
  2. Navigate to some non-application page, such as https://spring.io and leave browser open
  3. Open a new instance of the same browser, in this case Chrome
  4. Go to http://localhost:8080 and login to the application using user1 with password password
  5. Close the browser (leave the original browser instance open to https://spring.io)
  6. Open a new instance of the same browser again
  7. Go to http://localhost:8080 and login to the application using user2 (could have used the same user) with password password
  8. Close the browser (leave the original browser instance open to https://spring.io)
  9. Wait for the configured server.servlet.session.timeout period (might take a little bit longer) and you will see the SessionDestoryedEvent gets triggered only once, for the user2 session.
SessionDestroyedEvent: principal = org.springframework.security.core.userdetails.User [Username=user2, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_USER]] : [C019560E9B0ED1E52AB26B6F829DA3D9]

Hopefully this gives you what you need and that I haven't left anything out. If you have any questions or would like me to provide a pre-built executable jar, please let me know.

Thanks again for looking into this issue.

Apologize the missteps for the attachments. I didn't see them embedded in the middle of my text so I made multiple attempts

SessionDestroyedEvent-12140.zip

@marcusdacoregio marcusdacoregio added the status: waiting-for-triage An issue we've not yet triaged label Nov 7, 2022
@marcusdacoregio
Copy link
Contributor

Thanks for putting the effort into the sample @attitudemattrs.

Once the team finishes the Spring Security 6 release we can start looking into this, I appreciate your patience.

@attitudemattrs
Copy link
Author

Hi @marcusdacoregio,

In researching to see if there was a work-around for this issue, I came across something that I believe addresses our problem. If you recall from my earlier posts (and my demo application), the session management was defined in our application configuration files as follows:

<sec:session-management invalid-session-url="/login"/>

The demo application I uploaded had a similar configuration, but in WebSecurityConfig.java, in that it used the default session management configuration.

I created a class HttpCustomSessionEventPublisher extends HttpSessionEventPublisher and set a breakpoint in the sessionCreated method while debugging our application. What I found was, in the Unexpected Scenario, the sessionCreated method was being called for the first login, but was not being called for the second one, third one, etc. as long as the logins were done before the first login was invalidated due to inactivity.

This got me researching further how the sessions were being managed and took me to the section of the Spring Security documentation on Session Fixation Attack Protection and the description of the different session fixation settings. Unfortunately, I'm not well versed on the differences for some of these, especially migrateSession vs. newSession and the pros/cons for using each. However, I was aware that the default setting for session fixation in Spring is migrateSession. So, on a hunch (given the discussion about migrateSession copying session attributes to the new session created), I decided to change the configuration to use newSession as follows:

<sec:session-management invalid-session-url="/login" session-fixation-protection="newSession"/>

In the demo application I changed the WebSecurityConfig.java to have the following:

http
	.sessionManagement().sessionFixation().newSession().and()
	.authorizeHttpRequests((requests) -> requests
		.antMatchers("/", "/login").permitAll()
		.anyRequest().authenticated()
	)
	.formLogin((form) -> form
		.loginPage("/login")
		.permitAll().defaultSuccessUrl("/hello")
	)
	.logout((logout) -> logout.permitAll().invalidateHttpSession(true).deleteCookies("JSESSIONID"));

In both cases I reran my tests and they worked exactly as we need it to act. In fact, even better. Results documented in the scenario below (previously Unexpected Behavior):

Make sure you do steps 6 - 9 within the server.servlet.session.timeout period on completing step 5. Do not wait for the user1 session to timeout

  1. Open Chrome (or Edge or Firefox) browser
  2. Navigate to some non-application page, such as https://spring.io and leave browser open
  3. Open a new instance of the same browser, in this case Chrome
  4. Go to http://localhost:8080 and login to the application using user1 with password password
  5. Close the browser (leave the original browser instance open to https://spring.io)
  6. Open a new instance of the same browser again
  7. Go to http://localhost:8080 and login to the application using user2 (could have used the same user) with password password

At this point the SessionDestroyedEvent is immediately triggered for the user1 session and it is cleaned up and a new session created for the user2 session

  1. Close the browser (leave the original browser instance open to https://spring.io)
  2. Wait for the configured server.servlet.session.timeout period (might take a little bit longer) and you will see the SessionDestoryedEvent gets triggered for the user2 session.

Based on my testing so far, it seems that using the session fixation setting of newSession makes the application work exactly as expected and all the expected events are triggered and processed.

So, at this point I don't know if what I logged before is "expected" behavior when using migrateSession or not, or why changing it to newSession resolves the issue.

Before I proceed forward with using newSession (to make sure the application still works properly - which it has in my limited testing this morning), I was wondering if you could educate me on the key differences between migrateSession and newSession and why using the default migrateSession might have behave the way it does. More importantly to me, the application and demo seem to work with newSession, but are there any gotchas I should be aware of when using newSession? I don't want to create new issues for myself by using this setting. It sure does seem like the right one though.

Thanks in advance for your help!

@marcusdacoregio marcusdacoregio self-assigned this Nov 21, 2022
@marcusdacoregio marcusdacoregio removed the status: waiting-for-triage An issue we've not yet triaged label Nov 21, 2022
@marcusdacoregio
Copy link
Contributor

marcusdacoregio commented Nov 21, 2022

Hi @attitudemattrs, thanks for your patience.

The strategy that Spring Security uses by default for session fixation is changing the session id, see ChangeSessionIdAuthenticationStrategy. The strategy will change the session id of the current session associated with the request. Note that it does not invalidate/destroy or create another session, it simply changes the session id, therefore, after the 1 minute passes, there is only one session to invalidate.

The first one remains out there and seems to never expire. Looking through the SessionRegistry in the debugger in subsequent requests, the last activity time remains static at the last activity time before the original browser was closed and the session expired flag remains false for the remaining session. Even if I close the remaining browser windows the session never gets cleaned up until the application server is shutdown.

The SessionRegistryImpl saves the created session in a regular collection and is not aware of any max inactive interval. Since the ChangeSessionIdAuthenticationStrategy does not update SessionRegistryImpl with the new id, that old id will stay there until the application is restarted.

With SessionFixationProtectionStrategy you can see in the implementation that it invokes session.invalidate(); which causes the session to be destroyed right after a new one is created.

In summary, your scenario is working fine and the session is being destroyed correctly, the SessionRegistryImpl is just not being updated. You might want to create a custom implementation of AbstractSessionFixationProtectionStrategy and use it by replacing ChangeSessionIdAuthenticationStrategy, something like:

public final class MyAuthenticationStrategy extends AbstractSessionFixationProtectionStrategy {

        private final SessionRegistry sessionRegistry;

	@Override
	HttpSession applySessionFixation(HttpServletRequest request) {
                String oldSessionId = request.getSession(false).getId()
		request.changeSessionId();
                this.sessionRegistry.removeSessionInformation(oldSessionId);
		return request.getSession();
	}

}

Does that makes sense for you?

@attitudemattrs
Copy link
Author

Hi @marcusdacoregio,

Thank you for the explanation. I believe I understand the reasons for the behavior we were seeing by not specifically setting the session fixation configuration and using the default. If I read what you responded with correctly, since my original configuration of session-management did not include the attribute session-fixation-protection that this, by default, used the ChangeSessionIdAuthenticationStrategy, resulting in the behavior of the session id being changed, but the old session not getting cleaned up according to our inactivity values.

Assuming this is correct, I have to apologize because I am still a little unclear on a few things and ask for a little more of your time.

Looking at the code for SessionManagementConfigurer it appears as though SessionFixationProtectionStrategy should be used if I specify either migrateSession or newSession for the session-fixation-protection in my configuration, is this correct? This makes the main difference between migrateSession and newSession the fact that migrateSession migrates the spring security attributes from the old session to the new session, whereas newSession does not. Is this correct?

If my assumptions are correct (which is likely a bad assumption), I am still unsure why, when I changed the configuration to migrateSession originally in attempts to correct the problem, I saw the same behavior of the original session not getting a session destroyed event triggered when a new session was created (or at least I am fairly certain that was the case). It was only by changing to newSession that I got the desired behavior.

If you could clarify this for me it would be much appreciated. Additionally, if I can impose on your time for a couple more questions...

  1. Is there a reason why I would implement your proposed MyAuthenticationStrategy vs just setting the session-fixation-protection="newSession"? Is there an advantage of one over the other, other than perhaps the migration of spring session attributes from one session to another?
  2. Exactly what session attributes are preserved and copied with migrateSession and the use of SessionFixationProtectionStrategry or a similar custom one?

I am basically trying to determine whether the best resolution to our problem is simply to use newSession.

Again, thank you so much for your time and your patience with me on this topic. This has definitely been a learning experience for me and your time and help is greatly appreciated.

@marcusdacoregio
Copy link
Contributor

since my original configuration of session-management did not include the attribute session-fixation-protection that this, by default, used the ChangeSessionIdAuthenticationStrategy, resulting in the behavior of the session id being changed, but the old session not getting cleaned up according to our inactivity values.

That is correct, however, there is no old session, the session continues the same but with a different id. The session seems to be there only because SessionRegistryImpl does not get updated.

Looking at the code for SessionManagementConfigurer it appears as though SessionFixationProtectionStrategy should be used if I specify either migrateSession or newSession for the session-fixation-protection in my configuration, is this correct?

Yes

This makes the main difference between migrateSession and newSession the fact that migrateSession migrates the spring security attributes from the old session to the new session, whereas newSession does not. Is this correct?

Not really, accordingly to the Spring Security documentation:

  • newSession: Create a new, “clean” session, without copying the existing session data (Spring Security-related attributes are still copied).
  • migrateSession: Create a new session and copy all existing session attributes to the new session. This is the default in Servlet 3.0 or older containers.

When I tried with migrateSession and newSession I got the same behavior, the session gets destroyed and another one is created. You can see the exact part of the code that is destroying the session here.

Is there a reason why I would implement your proposed MyAuthenticationStrategy vs just setting the session-fixation-protection="newSession"? Is there an advantage of one over the other, other than perhaps the migration of spring session attributes from one session to another?

No, the code that I showed you was just to clarify that the SessionRegistryImpl is not updated by default.

Exactly what session attributes are preserved and copied with migrateSession and the use of SessionFixationProtectionStrategry or a similar custom one?

You can have this answer in the documentation that I've linked above.

I am basically trying to determine whether the best resolution to our problem is simply to use newSession.

I don't think you have a problem there, as I've said before, when you are using the change id strategy, there is no such thing like old and new session, it's the same session but a different id. Therefore, it is expected that only a single session is destroyed. The information present in SessionRegistryImpl is simply not being updated to reflect the change of id of the sessions.

@attitudemattrs
Copy link
Author

Hi @marcusdacoregio,

Makes sense. I have closed this issue based on the discussion above. Thank you for your time, attention, and patience on this issue.

@drahkrub
Copy link

@marcusdacoregio

The session seems to be there only because SessionRegistryImpl does not get updated.
...
No, the code that I showed you was just to clarify that the SessionRegistryImpl is not updated by default.
...
The information present in SessionRegistryImpl is simply not being updated to reflect the change of id of the sessions.

I think that should not be the case since Version 5.4.0 released in 2020, have a look at #5439 (SessionRegistryImpl is now aware of SessionIdChangedEvent)

  • ChangeSessionIdAuthenticationStrategy calls request.changeSessionId()
  • HttpSessionEventPublisher implements [...] HttpSessionIdListener
    • sessionIdChanged is called
    • HttpSessionIdChangedEvent is thrown
  • SessionRegistryImpl catches SessionIdChangedEvent (extended by HttpSessionIdChangedEvent)
    • private final Map<String, SessionInformation> sessionIds gets updated

@attitudemattrs Reopen this issue? ;-)

@attitudemattrs
Copy link
Author

Thanks @drahkrub for the comments. I'll defer to @marcusdacoregio as to whether this should be reopened :-). For our situation I am getting around the issue by using a session-fixation-protection setting of newSession as shown below:

<sec:session-management invalid-session-url="/login" session-fixation-protection="newSession"/>

This seems to have gotten us around the problem, at least for this situation.

@marcusdacoregio
Copy link
Contributor

Hi @drahkrub,

Even if that is true, I don't think that it proves that the SessionDestroyedEvent is not triggered when it should, the session id has been changed and there will be no event for that old id anymore.

@drahkrub
Copy link

Hi @marcusdacoregio,

sorry, you're absolutely right on this point - in this respect there is no need to reopen this issue.

But there is no need (or there should be no need) to remove the old session id as you've done it in MyAuthenticationStrategy - Spring Security itself should handle that.

I came here because I have a similiar problem: In my web app SessionRegistryImpl holds some SessionInformations which simply do not expire - still debugging...

I also found #10242 which looks similiar.

@marcusdacoregio
Copy link
Contributor

Hi @drahkrub,

But there is no need (or there should be no need) to remove the old session id as you've done it in MyAuthenticationStrategy - Spring Security itself should handle that.

You are right.

I came here because I have a similiar problem: In my web app SessionRegistryImpl holds some SessionInformations which simply do not expire - still debugging...

Feel free to contribute to the #10242 discussion, or open a new issue if it is not so related.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web An issue in web modules (web, webmvc) type: bug A general bug
Projects
None yet
Development

No branches or pull requests

3 participants