Return to site

Cookie Session

broken image


Spring Security architecture

  • If you set the expiration time to 0, the cookie won't be created at all. I've tested this on Google Chrome at least, and when set to 0 that was the result. The cookie, I guess, expires immediately after creation. To set a cookie so it expires at the end of the browsing session, simply OMIT the expiration parameter altogether. Example: Instead of.
  • Whenever a session is created, a cookie containing the unique session id is stored on the user's computer and returned with every request to the server. If the client browser does not support cookies, the unique php session id is displayed in the URL; Sessions have the capacity to store relatively large data compared to cookies.
  • See full list on github.com.

Spring Security integrates into Spring web as a servlet request filter(see Chapter 9 of the Spring Security Reference).The FilterChainProxy is the central filter class and contains a parallel SecurityFilterChain(see Chapter 9.4 of the Spring Security Reference).The FilterChainProxy is also a good starting point for debugging the Spring Security processing.

Session Cookies - Also known as a transient cookie or in-memory cookie. The lifetime of session cookies remain for the length of the browsing session. Once you close your browser, session cookies are cleared. Session cookies aim to solve the problem of a temporary data store for a given browsing session, which are automatically cleaned once.

In our sample project (using Spring Boot 2.3.1 and Spring Security 5.3.3) the SecurityFilterChain contains thefollowing filters (identified by debugging into FilterChainProxy.doFilter(.) and looking intothis.filterChains[0].filters).

  • org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter
  • org.springframework.security.web.context.SecurityContextPersistenceFilter
  • org.springframework.security.web.header.HeaderWriterFilter
  • org.springframework.security.web.authentication.logout.LogoutFilter
  • org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
  • org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
  • org.springframework.security.web.authentication.AnonymousAuthenticationFilter
  • org.springframework.security.web.session.SessionManagementFilter
  • org.springframework.security.web.access.ExceptionTranslationFilter
  • org.springframework.security.web.access.intercept.FilterSecurityInterceptor

Call of duty online trailer. Let's have a closer look at those filters that are relevant for our purpose and how toextend and customize their behaviour.

SecurityContextPersistenceFilter

From the API documentation: 'Populates the SecurityContextHolder with information obtained from the configuredSecurityContextRepository prior to the request and stores it back in the repository once the request has completedand clearing the context holder.'

The SecurityContext mainly represents the persisted session. It contains an Authentication which in the context ofa web application encapsulates the information of the authenticated user. The default implementation of theSecurityContextRepository stores the SecurityContext in the HttpSession. To change this behaviour we haveto provide our own SecurityContextRepository implementation.

Login

The UserInfo in our sample project is a very simple POJO that implements the UserDetails interface and contains theinformation that we want to hold in our user session.

The SaveToCookieResponseWrapper gets the UserInfo from the SecurityContext and puts it into a SignedUserInfoCookie.The SignedUserInfoCookie is an extension of javax.servlet.http.Cookie that handles the serialization and deserializationof the UserInfo into/from the cookie value.

The cookie value has to follow RFC-6265 which allows only a few non-alphabetical characters (see Stack Overflow answer for a good summary),for example no whitespace, quotes or brackets are allowed. So we can't use a JSON structure to serialize ourpayload, which would probably be easier to handle, especially to parse. We could have encoded the payload withBase64 before writing it into the cookie. However, the idea of the sample project was to keep the cookie valueunencoded and human-readable, so we decided for the individual format.

As the cookie contains the id and the roles of the authenticated user, we have to make sure that the value is notmodified on the client side. To do this our sample application signs the cookie by computing a HMAC (hash-based message authentication code)of the payload and appending it to the cookie value. That's a quite simple approach and there are probably betterand more secure ways of securing the cookie. One option might be JWT which provides a standardized way to securely exchange sensitive data. But, this is a topic of its own and out of the scope of this blog post.

(Thanks to Christian Köberl, @derkoe, for his feedback and ideas to improve the security of the cookie) Paste 2 2 3 4.

When the SecurityContext is requested via SecurityContextRepository.loadContext(.), the javax.servlet.http.Cookiefrom the HttpServletRequest is transformed into a SignedUserInfoCookie again. The cookie value is verified using the HMAC signature.A CookieVerificationFailedException will be thrown if the received cookie is unsigned or the HMAC does not fit to the value.Finally, the UserInfo is retrieved from the SignedUserInfoCookie, wrapped in a UsernamePasswordAuthenticationToken and set into the SecurityContext.

UsernamePasswordAuthenticationFilter

From the API documentation: 'Processes an authentication form submission.'

See also Chapter 10 of the Spring Security Reference for adetailed description of the Spring Security authentication process.

The UsernamePasswordAuthenticationFilter triggers the authentication, if necessary and possible. It reads usernameand password from a login form request, wraps them into a UsernamePasswordAuthenticationToken and calls the configuredAuthenticationManager to perform the authentication.

In the default configuration, the AuthenticationManager is a ProviderManager which holds a list ofAuthenticationProviders to which it delegates the authentication request. In our sample project we use a very basicInMemoryAuthenticationProvider which knows only one static user. In a real world project we would instead use a databaseor LDAP provider (from the Spring Security LDAP module).

After a successful login the configured AuthenticationSuccessHandler is called. Usually, this handler decides aboutwhere to forward the user to after the successful login. In the default configuration aSavedRequestAwareAuthenticationSuccessHandler is used. It loads and replays the original request (which was cached beforeby the ExceptionTranslationFilter, see next section) to show the page to the user which he/she originallyrequested. As this RequestCache is also stored in the server-side session, we have to find another strategy for this feature as well.

The RedirectToOriginalUrlAuthenticationSuccessHandler extends the SimpleUrlAuthenticationSuccessHandler and sets thetargetUrlParameter in its constructor. The parameter is defined and used by the extendedAbstractAuthenticationTargetUrlRequestHandler to find the target URL in the request parameters. Using thisfeature, we can simply put the originally requested URL into a hidden input field of the login form.The determineTargetUrl(.) method of the AbstractAuthenticationTargetUrlRequestHandler is overridden to preventtampering of the target URL parameter (see OWASP Unvalidated Redirects and Forwards Cheat Sheet).We only expect relative URLs within our own application.

The RedirectToOriginalUrlAuthenticationSuccessHandler also overrides the onAuthenticationSuccess(.) method. In this methodwe can get additional parameters (in our example a favorite colour) from the login form and add it to the UserInfoobject.

ExceptionTranslationFilter

From the API documentation: 'Handles any AccessDeniedException and AuthenticationExceptionthrown within the filter chain.'

Especially the very first, unauthorized request of a user triggers an AccessDeniedException (somewhere out of theFilterSecurityInterceptor). This one is catched and handled by the ExceptionTranslationFilter.If the user is not yet authenticated, the filter forwards him/her to the configured AuthenticationEntryPoint.

In the default configuration, the original request is temporarily stored in a RequestCache to be replayed after asuccessful login (see previous section). As noted before, the default HttpSessionRequestCache also uses the server sessionto store the request. We could have introduced a CookieRequestCache to stored the request in another cookie (like theSecurityContext). In our sample project we follow another approach.

We deactivate the RequestCache and instead extend the default LoginUrlAuthenticationEntryPoint, which forwards the userto the login form.

The overridden determineUrlToUseForThisRequest(.) method appends the URL from the original request as a queryparameter to the redirect URL. This way, the URL can be mapped to a hidden input field and will be included again in thelogin request where the AuthenticationSuccessHandler can read it (see previous section).

WebSecurityConfig

The WebSecurityConfig configures Spring Security to use all the components described above.

To prevent the creation of the server-side session and the JSESSION cookie we use the SessionCreationPolicy.STATELESS.To really activate this policy, we have to disable CSRF protection as well (see Spring Security issue 5299).

Cookie Session

The UserInfo in our sample project is a very simple POJO that implements the UserDetails interface and contains theinformation that we want to hold in our user session.

The SaveToCookieResponseWrapper gets the UserInfo from the SecurityContext and puts it into a SignedUserInfoCookie.The SignedUserInfoCookie is an extension of javax.servlet.http.Cookie that handles the serialization and deserializationof the UserInfo into/from the cookie value.

The cookie value has to follow RFC-6265 which allows only a few non-alphabetical characters (see Stack Overflow answer for a good summary),for example no whitespace, quotes or brackets are allowed. So we can't use a JSON structure to serialize ourpayload, which would probably be easier to handle, especially to parse. We could have encoded the payload withBase64 before writing it into the cookie. However, the idea of the sample project was to keep the cookie valueunencoded and human-readable, so we decided for the individual format.

As the cookie contains the id and the roles of the authenticated user, we have to make sure that the value is notmodified on the client side. To do this our sample application signs the cookie by computing a HMAC (hash-based message authentication code)of the payload and appending it to the cookie value. That's a quite simple approach and there are probably betterand more secure ways of securing the cookie. One option might be JWT which provides a standardized way to securely exchange sensitive data. But, this is a topic of its own and out of the scope of this blog post.

(Thanks to Christian Köberl, @derkoe, for his feedback and ideas to improve the security of the cookie) Paste 2 2 3 4.

When the SecurityContext is requested via SecurityContextRepository.loadContext(.), the javax.servlet.http.Cookiefrom the HttpServletRequest is transformed into a SignedUserInfoCookie again. The cookie value is verified using the HMAC signature.A CookieVerificationFailedException will be thrown if the received cookie is unsigned or the HMAC does not fit to the value.Finally, the UserInfo is retrieved from the SignedUserInfoCookie, wrapped in a UsernamePasswordAuthenticationToken and set into the SecurityContext.

UsernamePasswordAuthenticationFilter

From the API documentation: 'Processes an authentication form submission.'

See also Chapter 10 of the Spring Security Reference for adetailed description of the Spring Security authentication process.

The UsernamePasswordAuthenticationFilter triggers the authentication, if necessary and possible. It reads usernameand password from a login form request, wraps them into a UsernamePasswordAuthenticationToken and calls the configuredAuthenticationManager to perform the authentication.

In the default configuration, the AuthenticationManager is a ProviderManager which holds a list ofAuthenticationProviders to which it delegates the authentication request. In our sample project we use a very basicInMemoryAuthenticationProvider which knows only one static user. In a real world project we would instead use a databaseor LDAP provider (from the Spring Security LDAP module).

After a successful login the configured AuthenticationSuccessHandler is called. Usually, this handler decides aboutwhere to forward the user to after the successful login. In the default configuration aSavedRequestAwareAuthenticationSuccessHandler is used. It loads and replays the original request (which was cached beforeby the ExceptionTranslationFilter, see next section) to show the page to the user which he/she originallyrequested. As this RequestCache is also stored in the server-side session, we have to find another strategy for this feature as well.

The RedirectToOriginalUrlAuthenticationSuccessHandler extends the SimpleUrlAuthenticationSuccessHandler and sets thetargetUrlParameter in its constructor. The parameter is defined and used by the extendedAbstractAuthenticationTargetUrlRequestHandler to find the target URL in the request parameters. Using thisfeature, we can simply put the originally requested URL into a hidden input field of the login form.The determineTargetUrl(.) method of the AbstractAuthenticationTargetUrlRequestHandler is overridden to preventtampering of the target URL parameter (see OWASP Unvalidated Redirects and Forwards Cheat Sheet).We only expect relative URLs within our own application.

The RedirectToOriginalUrlAuthenticationSuccessHandler also overrides the onAuthenticationSuccess(.) method. In this methodwe can get additional parameters (in our example a favorite colour) from the login form and add it to the UserInfoobject.

ExceptionTranslationFilter

From the API documentation: 'Handles any AccessDeniedException and AuthenticationExceptionthrown within the filter chain.'

Especially the very first, unauthorized request of a user triggers an AccessDeniedException (somewhere out of theFilterSecurityInterceptor). This one is catched and handled by the ExceptionTranslationFilter.If the user is not yet authenticated, the filter forwards him/her to the configured AuthenticationEntryPoint.

In the default configuration, the original request is temporarily stored in a RequestCache to be replayed after asuccessful login (see previous section). As noted before, the default HttpSessionRequestCache also uses the server sessionto store the request. We could have introduced a CookieRequestCache to stored the request in another cookie (like theSecurityContext). In our sample project we follow another approach.

We deactivate the RequestCache and instead extend the default LoginUrlAuthenticationEntryPoint, which forwards the userto the login form.

The overridden determineUrlToUseForThisRequest(.) method appends the URL from the original request as a queryparameter to the redirect URL. This way, the URL can be mapped to a hidden input field and will be included again in thelogin request where the AuthenticationSuccessHandler can read it (see previous section).

WebSecurityConfig

The WebSecurityConfig configures Spring Security to use all the components described above.

To prevent the creation of the server-side session and the JSESSION cookie we use the SessionCreationPolicy.STATELESS.To really activate this policy, we have to disable CSRF protection as well (see Spring Security issue 5299).

We use the CookieSecurityContextRepository and our cookie should be deleted after the user logs out.

The RequestCache is deactivated and instead the LoginWithTargetUrlAuthenticationEntryPoint is used to addthe originally requested URL to the login form request.

The RedirectToOriginalUrlAuthenticationSuccessHandler is used to forward the user to the originally requested URL aftera successful login.

Summary

Spring Security might seem to be very complex and hard to configure on the first glance. But, spending some time with thedocumentation and doing a little bit of debugging in a sample application, it turns out that it is very extensible andcustomizable by design. The hardest part is to find the right places. This blog post identified the ones that need tobe adjusted to store the user session information in a cookie instead of a server-side session. It might also bea starting point for other, related topics.

Many thanks to Michael Vitz for showing me the relevant starting points and reviewing the resultingimplementation. Also many thanks to Jochen Christ, Jan Stępień, and Stefan Tilkov for their feedback to this post.

Header Photo by Steve Halama on Unsplash

Handling of session cookies differs between browsers. The focus of this post details common misconceptions of session cookie management and its impact to how your web application operates for any given browser.

Types of Cookies

There are two main categories of cookie types:

  • Persistent Cookies - Cookies which are carried or persisted across multiple browsing sessions. These cookies will expire on a given date and time.
  • Session Cookies - Also known as a transient cookie or in-memory cookie. The lifetime of session cookies remain for the length of the browsing session. Once you close your browser, session cookies are cleared.

Session cookies aim to solve the problem of a temporary data store for a given browsing session, which are automatically cleaned once that browsing session has ended. Examples of where session cookies are most likely used include storing of shopping cart items, form data or theme selections, temporary tracking data, etc. While session cookies seem to be a safe solution, it's important to understand how handling of session cookies differs between browsers.

Chrome

Background Apps

Cookie Session Expired

Since version 19, Chrome has altered how it runs in the background which has an immediate impact on how you expect Chrome to handle session cookies when you close your browser. Under advanced settings > System, the option 'Continue running background apps when Google Chrome is closed' is checked by default. In other words, if you close your browser, it will continue to run in the background (to support Chrome applications and extensions). Allowing Google Chrome to run in the background keeps the Chrome application session alive and prevents session cookies from being cleared.

The issue has been entered and marked as 'won't fix', recognized as expected behavior by the development team.

Start-Up

On start-up, Chrome offers the ability to 'Continue where you left off'. Checking this setting persists session cookies from one browsing session to the next. Mac os x upgrade to high sierra. This makes sense. With this setting, we're explicitly telling the browser to remember exactly what we were doing at the time the browser was closed. By default, this is turned off.

Firefox

Similar to Chrome's start-up feature, Firefox Session cookies are also saved to allow for Firefox's session restore feature. If the browser is forcibly closed or crashes, session cookies are not deleted and the session remains. It's worth noting, this does not happen on sites backed by https. While this is default behavior, unlike Chrome, closing the browser will clear any session cookies present.

This feature has also created a lively discussion on Mozilla's bug tracker.

Sitecore xDB

How does Sitecore handle session cookies?

Looking at Sitecore DMS, you'll notice the cookie representing the current browsing session for Analytics is a session cookie (SC_ANALYTICS_SESSION_COOKIE). For xDB, the dependency on this cookie has been removed. Session management through both the SessionEnd and VisitEnd pipelines invoke Sitecore.Analytics.Tracker.EndVisit flushing the current session and queueing writes to the Analytics database, removing any potential for browser-specific issues around handling of session cookies.

Final Thoughts

Cookie Session Login

Don't rely solely on browser behavior for proper clean-up of session cookies during a given browsing session. If you must rely on session cookies, look to server-side logic for help in determining when a given session ends. It's also probably safer to explicitly define an expiration date for your cookie than to rely on consistent behavior across all browsers and versions.

Cookie Session Http

Please enable JavaScript to view the comments powered by Disqus.comments powered by

Cookie Session Token

Disqus

Cookie Session Id





broken image