1

I am currently building a full stack web app mainly as part of an exercice. This app is made up of a back end on a server (written in Go), and a front end written in Angular (so on a user's browser). I'll refer to the front end as the client.

I'm trying to secure the API so no account gets stolen or "hijacked". I designed this authentication & authorization flow, and I was hoping you could tell me what you think of it.

This flow uses JWTs containing all this :

Header :
{
   "alg": "HS256",
   "typ": "JWT"
}

Payload :
{
   "uid": 1,
   "token-id": "azertyuiop",
   "xsrf-token": "azertyuiop”
}

And the secret key to hash the signature is stored on the server in a database along other information. This is the structure of the authentication database :

  • secret VARCHAR(255) is the key to encrypt/decrypt the signature
  • last_login DATETIME is the date of the last connexion by the user
  • token_id VARCHAR(100) is the unique identifier of the JWT to link it to the authentication table


Now here is the actual flow :

First of all, when the user logs in :

  1. The user authenticates with his hard credentials (username or email + password).
  2. If the credentials match, the server responds with a JWT token it has created (the token is sent via a cookie with the HttpOnly flag set so only the server can read the cookie, and it can’t be accessed/stolen with Javascript). This JWT token contains a xsrf-token in its payload. The xsrf-token is also sent to the client in the body of the response. The client stores that token in a non-HttpOnly cookie so it can access it. The JWT is not stored on the server.


Finally when the user wants to access a (protected) ressource :

  1. When the user wants to access a resource, the cookie containing the JWT is (automatically) sent to the server (if it exists). The client also provides the xsrf-token in the request’s body (gets it back from the non-HttpOnly cookie), in a parameter called xsrf-token (only for ressources requiring to be authenticated).
  2. The server base64urldecodes the payload of the JWT (it received through the cookie), and reads the value of token-id stored in it. With this token, it is able to identify which row in the authentication table contains the right key and retrieve that key.
  3. It then uses that secret key to decrypt the signature and verify that the signature and the payload match in order to authenticate the user.
  4. The server checks if the xsrf-token provided in the body of the request is the same than the one provided in the payload of the JWT. Finally, it also checks the Origin and Referer of each request to the server. This step is supposed to stop csrf attacks.
  5. The server analyses the payload of the JWT to find the uid to identify the user (and authorise the user or not to access the ressource).
  6. In the database, if last_login is too old (more than, 15 days), the request is rejected (the authentication has expired; the user needs to log in again) and the row in the database (containing the secret, the last login date and the xsrf token) is deleted. Otherwise, the server updates last_login in the database (set it to the current datetime).


However, I'm not completely certain about this flow. Is it worth it to completely renew the session after each request (the row corresponding to the authentication in the database is deleted and replaced with a brand new one; a new JWT is sent to the client)? Also, how often should the xsrf token be renewed? If I never renew it and a hacker manages to get his hands on it, the user could be exposed to CSRF attacks... But if I renew it after each request, there are more chances that a hacker could intercept it (a hacker would have more opportunities).

It seems it is quite standard to store the expiration date in the JWT (in an exp claim I think). What are the benefits of doing this?

Another question I had is if a thief manages to get his hands on someone else's computer, he will be able to steal the cookies containing the JWT and the xsrf token. Is there a way to prevent him from then using this information? A friend of mine told me it is possible to prevent that by hashing/encrypting these tokens, but I don't understand how it would work.

Finally, I heard of other sorts of tokens : "Reference token", "Access token", "Refresh token". What are those for?

From all the articles I read, I think I covered the biggest/most important flaws : csrf and xss (unless of course I got something completely wrong). However, what are the other flaws I am still exposed to ?

Thank you for reading. I hope you will be able to answer my questions and would appreciate it very much. Also, if you have any doubts or pieces of advice or just anything you want to add to that flow, I would be very happy to hear what you have to say. Thanks again.

  • 1
    This isn't mentioned anywhere in the question, and is pretty foundational to any sort of online authentication, so... You are using TLS only, correct? That is, even jumping through the hoops of using JSON Web Tokens, you're also making sure that your system never sets a cookie or communicate in any way with a non-TLS connection except to redirect them to the HTTPS version of your site, right? – Ghedipunk Mar 6 at 23:42
  • A last_login of 15 days seems way too long. Consider adding a last_accessed timeout in minutes. How is the password being secured on the server, plan for an attacker gaining admin access. Finally: Don't invent your own security—"Schneier's Law": "Anyone, from the most clueless amateur to the best cryptographer, can create an algorithm that he himself can't break." – zaph Mar 7 at 1:37
  • @Ghedipunk : I do intend to use HTTPS, yes. @zaph : But if I choose a last_login of a few minutes, it means the user will have to login with his credentials after just a few minutes of activity. What's more, I think if I go on facebook even after a few weeks, I will still be logged in. Is Facebook unsafe? For the password, I hash it using the sha256 algorithm. As for my own security, I didn't really invent any concept here, and I do use a library for the JWT. – tomfl Mar 7 at 8:23
  • @tomfl 1: Specify a last_accessed timeout in minutes, and a last_login in days. Using SHA256 for password hashes is not secure, even with a unique random salt it is not secure. You need a password hashing function that is expensive in time and potentially in system resources making offline password attacks vary expensive in time and CPU costs. Argon2i , PBKDF2 and Bcrypt are good choices. – zaph Mar 7 at 12:05
  • cont: 3: Facebook is not safe, security is not their model. Also I doubt that you are still logged even after a few weeks, rather a new login is created with your locally saved credentials possibly including information that you are accessing your account with a computer they recognize as having been previously used. Further, I suspect that they have a stronger backend that you will be able to provide, probably including HSMs. – zaph Mar 7 at 12:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.