We recently made changes to prepare for migrating to Spring Security 6.0. Specifically, we moved from the deprecated SecurityContextPersistenceFilter to the SecurityContextHolderFilter and now explicitly save the security context to the SecurityContextRepository when it changes (https://docs.spring.io/spring-security/reference/migration/servlet/session-management.html#_require_explicit_saving_of_securitycontextrepository).

When a user logs out from our application, we clear the security context and then save this to the repository. However, the SecurityContextRepository implementation (HttpSessionSecurityContextRepository) determines that the security context has not changed since the start of the request and therefore does not persist it.

The reason for this is that the HttpSessionSecurityContextRepository.saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) method assumes that the response object is wrapped in a SaveContextOnUpdateOrErrorResponseWrapper object, containing the security context from the start of the request. However, the response object is not wrapped. Then a new wrapper object is created, with an empty security context. When this empty security context is compared to the cleared security context that we want to persist, it is marked as not changed and therefore not persisted.

Result of this is that the security context is not cleared on logout from the security context repository, meaning on the next request the security context is loaded again and the user is logged in.

One thing to note is that the SecurityContextHolderFilter calls the default method SecurityContextRepository.loadContext(HttpServletRequest request) which calls loadContext(HttpRequestResponseHolder requestResponseHolder) with a null response object. Therefore, in the HttpSessionSecurityContextRepository.loadContext(HttpRequestResponseHolder requestResponseHolder), the response is never wrapped in a SaveToSessionResponseWrapper object.

Expected behavior When the security context is cleared and then saved, I want it to be persisted if it is changed.

Current workaround After clearing the security context, we now first load the security context from the security context repository and then save it. This makes sure that the response object is wrapped with the security context from the start of the request, before the cleared security context is saved. Then the security context repository can determine that the security context has changed and persists it.

SecurityContext context = SecurityContextHolder.createEmptyContext();
SecurityContextHolder.setContext(context);

HttpRequestResponseHolder requestResponseHolder = new HttpRequestResponseHolder(request, response);
securityContextRepository.loadContext(requestResponseHolder);
securityContextRepository.saveContext(context, requestResponseHolder.getRequest(), requestResponseHolder.getResponse());

While this works, it involves code that is deprecated and also seems not the way to go to do this.

Comment From: ggraat

This is on Spring Security 5.7.5, I did look at more recent versions (including 6.x) but the code involved has not changed on these points.

Comment From: marcusdacoregio

Hi @ggraat, thanks for the report. This seems to be a duplicate of https://github.com/spring-projects/spring-security/issues/12314 therefore I'm closing this to keep the discussion on the other ticket.

Comment From: marcusdacoregio

Actually, they seem related but I think they are not, I'll leave this open for the time being while I mull over this for a bit more.

Comment From: marcusdacoregio

Hi @ggraat, thanks for the report.

I'm closing this in favor of https://spring.io/security/cve-2023-20862