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.
- I. What to look for in an authentication scheme?
- II. Evaluation of standard authentication schemes
- III. Cheatsheet and closing thoughts
II. Evaluation of standard authentication schemes
Bearer tokens, rawr!
Lots of modern web application utilize bearer tokens. Sometimes it comes so naturally that you forget what it is. Have you ever heard something along the following?
Dev A: How should we authenticate here?
Dev B: We will just use a Bearer token!
Dev A: A bear what?
Dev B: JWT, silly!
Dev A: Ah okay, yeah JWT is okay, I know JWT!
JWT is a kind of bearer token, probably the most famous one indeed, but that's another story. So what is a bearer token anyway? RFC6750 is to the rescue. Here is the official definition.
A security token with the property that any party in possession of the token (a “bearer”) can use the token in any way that any other party in possession of it can. Using a bearer token does not require a bearer to prove possession of cryptographic key material (proof-of-possession).
Don't you just love, how RFCs make the simplest of things sound complicated?
Think of these tokens as physical keys. Anyone who has it can unlock the lock. From the lock's point of view, the only thing that matters is the possession of the key, not the actual possessor. And of course, the lock does not care about the relationship (cryptographic proof-of-possession) between the key and the bearer of the key.
How it works?
When bearer tokens were first introduced with OAuth, the reference writers faced a challenge. They had to smuggle these tokens into a standard that was already widespread so the adaptation would be faster. Luckily, earlier HTTP Auth RFC was extendable, so all they had to do was add the “Bearer” scheme. And this is what happened.
Long story short, bearer tokens must be sent in the Authorization header just like with Basic and Digest Auth. Here is what it looks like.
Authorization: Bearer ujoomieHe2ZahC5b
The idea behind bearer tokens is that anyone who has them can use them. This makes it possible to pass these tokens around, possibly to other services.
One cannot talk about bearer tokens without mentioning JWT. JWT is a self-describing bearer token. It has a simple three-part structure: header, body, signature. The header describes how to interpret the rest of the token, hence the self-describing property. The body contains JSON data, and the signature provides integrity for the header and the body. This structure allows the authenticating service to operate without state as everything needed to interpret the token is encoded inside it.
In general, being self-descriptive is by not a requirement for bearer tokens; it is, however, very convenient. These tokens can be opaque, i.e. a random string of characters, and carry absolutely no encoded information. In this case, they are meaningless to everyone except the party who issued it.
Both, opaque and self-describing tokens have their use-cases, but further discussion is out of scope for this post.
It all comes down to setting an extra header with the correct format (see above). On the frontend, browsers do not deal with bearer tokens like they do with Basic, Digest auth and cookies. They leave it all to you.
Still, using bearer tokens on both the frontend and backend is easy and has excellent support from frameworks.
Reliance on HTTPS
Bearer tokens are sent in the Authorization header in plaintext. By definition, anyone capable of presenting this token has the power it grants, so stealing it will provide attackers much value. Always make sure to use a secure HTTP connection (HTTP over TLS a.k.a HTTPS) when authenticating with this scheme. Failing to do so will leak your tokens to anyone monitoring the network traffic.
Consequently, the security of this auth scheme relies heavily on the use of TLS.
This is a place where Bearer tokens shine. When you use them on the frontend, sending them in the Authorization header, you are protected against CSRF.
Because, the browser leaves all management to you, and won't attach these tokens to requests automatically. Check!
Replay and integrity protection
Bearer tokens, in general, do not provide any protection against replay or modification ( integrity). While JWT provides integrity for the token itself, it does not protect the request it is attached to. Therefore the request parts (method, query parameters, headers, body) can be freely modified by an attacker in the middle.
These protection mechanisms are left to the underlying TLS protocol.
Recommended use cases
Bearer tokens are widely used on the backend thanks to OAuth 2 and JWT. They are simple and well suited for this purpose. Just attach them as a header and off you go.
These tokens can be used on the frontend as well. Lots of single-page web applications opt for using JWT as their session management mechanism. Depending on your needs JWTs may be far from ideal. It is not trivial to cover the full lifecycle of a session with these. For instance, you cannot implement logout and idle session timeout without server state. Once you have server state you lose what you gained by opting for JWT.
Taking care of your auth scheme
Backend usage is mostly trivial. Apply the best practices you would for any sensitive data. Store them encrypted, transfer them encrypted (HTTPS) and limit their exposure. Wherever you use them, keep the expiration time as low as you can. Short-lived tokens have a smaller chance of being successfully utilized after compromise. Also, consider using access and refresh tokens. Access tokens should be short-lived, due to their relatively high exposure (sent with every request, while refresh tokens can be long-lived as they are only sent when access tokens expire. OAuth flows implement this beautifully.
Coming up next
Our next topic is a bit different from what we have covered so far. It's signature schemes. What makes them unique is that there are lots of signature systems out there. They all have the same core idea, but no single standard has emerged yet (there is a draft). Signature schemes provide a higher degree of security. Applications that required such a level mostly use a custom scheme that is documented in their API docs.
So why do signatures provide better security? Does it matter which scheme you choose? Be sure to check out the next post to learn all about it!
Spot a mistake in reasoning? Have a different opinion? Sound off in the comments!