Published on

A Tale of Two Sessions


What are sessions?

Sessions are the means to identify users of your websites during subsequent requests without requiring them to pass along their username and password with each request.

Typically this is done by associating a unique session ID with a user in the database.

Sessions make use of cookies as the means of communicating the active session between the user agent or browser and the server. Cookies are a special type of HTTP header passed to the server for every request that the cookie is valid for (typically any request made to the domain).

There are two primary methods of identifying a user session:

  • Storing a Session ID in an unencrypted cookie
  • Storing the User ID in an encrypted cookie

Most frequently the Session ID is stored in a cookie, which is used to load the active user and any metadata from a database when a request is made to the server.

In the past few years, storing the sessions in cookies themselves has become more popular. Doing this removes some application state which can be challenging to manage for each request. The web developer doesn't have to run an additional service (like memcache or redis) or put additional load on their primary database.

Server Side Sessions w/ cookies

This is the original method for managing sessions and is still the most common. It requires storing session information in a database. Web developers often start by storing it in their primary database, then move towards redis or memcache as traffic to their web application grows.

The cookie for this method of session management typically looks something like this:

    sessionid: <random-string>

Revocation is easy: you only have to delete the Session ID from the sessions table, wherever it is stored.

Encrypted Cookies

Storing sessions in encrypted cookies is becoming increasingly popular because it removes the need to retain state server-side. However, it requires the operator to maintain an encryption key to authenticate that it was indeed the server who set the session cookie in the first place.

The cookie for this method of session management typically looks like this:

    sessionid: <encrypted-user_data>

Revocation is not so easy. You either have to wait until the session expires (bad), construct a way to communicate to all the web application servers that this session has been revoked and must be re-authenticated (complicated), or check the session is valid for every request.

Comparison of Security Threat Models

Some may think that encrypted cookies are more secure because: duh they're encrypted! In fact, encryption has nothing to do with keeping the user or an attacker from knowing what the cookie is. Instead it is a means to communicate from one web-server to another that this cookie was not modified in transit or forged by an attacker. A simple nonce and signature would work just as well in this regard (django does this).

For that matter, stealing an encrypted session cookie is just as effective at impersonating a user as stealing an unencrypted session cookie. Just because an attacker can't read an encrypted cookie doesn't mean it won't authenticate them as the user just as well. Because of this property, all cookies used for authentication must be Secure only. The Secure attribute on cookies indicates that it should only be sent over a TLS connection.

The primary security drawback to using encrypted cookies over normal session cookies is that you have traded a state management problem for a secrets management problem. These secrets are often populated in environment variables and these environment variables are occasionally logged accidentally or mishandled in some other way. Once it's out, rolling the key is required, invalidating all active user sessions.

Comparison of the Benefits

The primary benefit of encrypted cookies is statelessness. No more database table for sessions, no more redis or memcache to manage in addition to your existing database.

The primary benefits of plain ol' sessions are also the drawbacks for using encrypted cookies:

  • easy revocation
  • one less secret to manage
  • speed

Revoking a session when using server-side sessions is simply deleting that user's session from the sessions table in the database. Session revocation can happen for numerous reasons:

  • User changed password
  • User got hacked and wants to revoke all existing sessions
  • User's privilege level changed
  • User deletes account

If you build a system to handle this using encrypted session cookies, it will probably look very similar to plain ol' sessions.

Why I wrote Jeff

Jeff is a tool for managing sessions in Go.

You might have been able to gather from this post that I'm firmly in the camp of using server-side sessions for session management. Because of this, when I started looking around for session libraries in Go, I noticed that all the ones I could find either used encrypted cookie storage, had bloated APIs, or were not idiomatic. Additionally, they almost all missed embedding context.Context, which is an important feature to support request cancellation.

So I went about implementing my own. Among it's features:

  • Small, simple API
  • Supports context
  • Supports arbitrary storage backends
  • Secure by default
  • Handler wrappers
  • Supply custom redirect handler

Additional Resources on Sessions

In popular web frameworks:

Using JWTs for Sessions (JSON Web Tokens are standardized encrypted blobs):

These guides talk about implementing sessions, but are not (what I would consider) very idiomatic: