sprachkonstrukt.de

How to authenticate against Web APIs

About JSON Web Tokens (JWT), Cookies, XSRF, XSS and how to get it all right, more or less.

In the good old times we just had PHP, would set a Session-Cookie and it all kind of worked out. In my first big project as a teenager, a small special interest community/bulletin board written by myself in PHP, security was not really an issue. TLS was still called SSL and was only used by banking sites. We hashed passwords with MD5 or not at all and it was okay-ish.

About 15 years later, I make Web APIs for a living, and boy did times change. Even for graduated computer scientists it is somehow confusing how to get Authentication “right”, in a way that you can sleep well at night without the constant fear that you did a major mistake and your user’s data is basically an open book for a potential attacker. I’ll try to summarize the current state of Web Authentication and how we’re doing it.

Dieser Artikel erschien zuerst auf Medium, als es cool war dort zu posten. Habe ich nur einmal gemacht, und wer weiß was dort mit den Inhalten passiert. Daher durfte er jetzt hierher umziehen, wo er nicht verloren gehen kann.

Many articles compare JWT, or token-based authentication, to Cookies. This is confusing because JWT is a token format and cookies are a storing mechanism. So let’s get back to square one at first:

Authentication is usually done by supplying something only the user can know or have, commonly using the username/password pattern. This is not the best solution, a public/private key authentication would still be better and browsers support it, but it is not very practical to manage for more than a handful of users (but for internal systems, administration interfaces and so on, it should be the way to go!).

So we have to check a user’s password and give him a key he can then use for some time (because we don’t want to check the password for each single request). In this process we need to make sure

To check the “securely”, we need to be aware of attack vectors first.

Attack vectors

Of course, there are even more attack vectors, e.g. we should not store passwords or tokens in our database in case we lose it.


But what are the different possibilities now?

Transport-Layer-Security (TLS)

The most important thing to do is to always use HTTPS if non-public (user-) data is transmitted. HTTP without encryption is only a viable alternative on a pure read-only, public website (which is hardly anyone nowadays). Using TLS is the simplest and most effective solution against MITM attacks. The SSL Labs Test should give you an A or better.

Password Best Practice

This is really easy, and yet so often done wrong:

Deploying Tokens

This brings me back to the often-heard “Cookies vs. JWT” which is comparing apples to oranges. Before we decide whether we use Cookies to store our tokens, we have to decide how our tokens look like. It may just be a “session ID” or something like that, e.g. a randomly generated string. But it is actually better to use JWTs: they can contain more information like expiration date, and most importantly: they are signed. So it is really easy to check if the token is valid and has not been tampered with. However, you should regard the following points:

Storing Tokens in the Client

Now we are at the core of this article: Session-based authentication (“Cookie-based-authentication”) vs. Token-based authentication. As laid out before, the statelessness of JWTs is not really a good thing, so no point here for JWTs vs. Session-Cookies. While it is nice to use JWTs instead of simply random strings, it is not too much a difference. What is a difference, is how you authenticate with them and where clients store them.

So where should we store our JWTs now?

In the cookie store, with the secure flag on, and the expiration date set correctly to the expiration date of the JWT. This provides some protection against XSS, at least — also, users are accustomed to “deleting cookies” being the same as “log me out everywhere”.

But: of course there is a “but”. Don’t accept cookie authentication in your API Server unless you really, really need to. Because cookies get sent automatically by the browser on requests to the matching host, XSRF attacks are possible (that thing when you come to a shady page which tricks you into posting something on your facebook wall). If you only use the cookie store as storing mechanism, but don’t read the cookie header on your server, you are safe against XSRF. However, you need to manually read the token out of the cookie using client-side JavaScript and put it in an Authorization header, see Bearer Authentication.

Of course, this only works if you go full JavaScript with a Single-Page-Application using Angular.js or React. If you want to use plain HTML with Authorization, i.e. authenticated GET-Requests or HTML forms, you’ll need to use Cookies (except you put the Bearer Token in the query string or as hidden property in the form).

To secure a request that gets authenticated via cookie-header on the server, you should use XSRF-Tokens: generate another JWT out of the same jid (or something else that is also in your auth-JWT and hard to guess) and put this JWT in your forms. Then check on the server that the ids from the auth-JWT and the XSRF-JWT are the same. This way it is guaranteed that the request ist coming from your site and not from an XSRF attack, because the attacker could not generate the correct XSRF token.

Of course, there is still no cure for XSS other than always sanitizing user input and never let users put <script> tags on your site — and be careful what third-party scripts you use.


Conclusion