The Web API Authentication guide, Cookies

This post is part of a multi-part series. It builds on the first post, where I describe the framework we will use to evaluate authentication schemes. If you have not, it is probably a good idea to read it now (hint).

Here is where we are.

II. Evaluation of standard authentication schemes

Cookies, the usual

CC0 image by Lisa Fotios

Chances are, if you are into web development, you are pretty familiar with cookies. Cookies are the state in the statelessness of HTTP and they are the first thing that comes to mind when you think about session management. For a good reason, they can provide full-blown session management capabilities with low complexity. It is the standard. They are like an umbrella on a rainy day, the de-facto protection against getting wet.

How it works?

For our analysis, there are two main aspects of cookies you need to be familiar with.

First, cookies are entirely managed by the browser. The server can instruct the browser to remember a piece of information and send it back with every subsequent request. You don't have to do anything client side to make this happen. It will be done for you. The browser will also take care of storing the cookies and evicting them upon expiration. It also enforces various security measures that can be controlled by so-called flags.

And second, cookies are exchanged via special headers. Here is how the server sets a cookie in the browser.

Set-Cookie: sessionId=shakiaNg0Leechiequaifuo6Hoochoh; path=/; Secure; HttpOnly; SameSite

Note the flags at the end. I will describe them briefly a bit further down.

Here is the session id sent back to the server. It is a dead simple key-value pair.

Cookie: sessionId=shakiaNg0Leechiequaifuo6Hoochoh

Note that only the key and the value is sent back and not the flags or the path.

There is quite a bit more to cookies. If you need a quick refresh, take a few minutes to read this.

Complexity

These little beasts are one of the simplest creatures of the web. A server sets a cookie, and the browser will keep sending it back along with every request. Web frameworks usually provide excellent support for this.

Given their widespread usage and the library support, cookies are easy to use for frontend request authentication.

Reliance on HTTPS

As cookies are sent over in a header, TLS is a must here as well. Without it, you are sending over everything in plaintext. Also, make sure to set the Secure flag on your cookies. This will instruct the browser only to send them over a secure connection. Otherwise, you may inadvertently expose your them.

CSRF protection

As cookies are attached to every request automatically by the browser, request forgery is something you will need to deal with. Luckily, modern browsers support the SameSite flag, which protects against this very attack. On older browsers though, you will need to opt for the classic defenses.

Replay and integrity protection

This authentication scheme depends entirely on HTTPS for these features. The lack of it makes replay and tampering trivial.

Recommended use cases

Use cookies for session management between the user's browser and the server. They are ideal for this, as the whole session lifecycle can be implemented easily. Cookies with session ids are probably the most widespread frontend authentication scheme.

Backend usage, server to server communication, is possible but I recommend against it. You have better options, like signature schemes or TLS client certificates. I will discuss these in a future post.

Taking care of your auth scheme

If using cookies, here are the things you need to do to secure them.

First, set the Secure flag to avoid leaking your cookie over a plaintext connection. Second, if you can, disable Javascript access to your cookies to prevent XSS attacks from stealing it. You can do this by setting the HTTP Only flag. And third, set the SameSite flag to protect against CSRF and XSSI.

Once you have done all that, you are pretty well off. It is, however, possible to further lock down cookies by utilizing cookie prefixes. Remember when I said only the name and the value is sent back to the server?

Well, cookie prefixes are designed to smuggle cookie state back to the server without inventing a new protocol. There are two prefixes you can use.

The first is __Secure-. Supported browsers will enforce the Secure flag and make sure the cookie is set from an HTTPS site. If your servers sees a cookie like this, you can be sure it has a Secure flag set in the browser.

The second is __Host-, which is a lot more strict than the previous one. It includes everything __Secure- does, and requires exact domain match and a path value of "/". Encountering this prefix on server side means the cookie has the Secure flag, it's an exact match for the domain and its path value is "/".

For more information on prefixes, take a look at this article.

Coming up next

Our next stop is almost as well known as cookies. Bearer tokens. They became popular with the introduction of JSON Web Tokens also known as JWTs.

Bearer tokens are also widespread and well supported by modern frameworks. They are also used in higher level constructions, like OAuth2 and OpenID Connect, making bearer tokens one of the most dominant authentication schemes between backend systems. Bearer tokens can also be used on the frontend.

They surely seem like the silver bullet we are looking for, but are they? Catch the next post to find out!

Spot a mistake in reasoning? Have a different opinion? Sound off in the comments!