node.jssecurityencryptionjwt

How to safely store & process secret key for JWT


After reading this: JWT: What's a good secret key, and how to store it in an Node.js/Express app?, on how to store "secret key" to assign JWT tokens. I had security questions. My data (messages, username, etc...) are going to be encrypted (in database) and only authorised users can decrypt it (based on their private key). Since JWT tokens are generated using 1 "secret key" which is stored on the server, in case an attacker gets the "secret key" and get's hold of the database - tokens can be forged and therefore data can be decrypted bypassing "password", which makes encryption pointless. To protect the "secret key", I could use these methods

Method 1

Store the "secret key" on a separate server (like HSM) which will be received during login and then used to set the token

Method 2

Encrypt some kind of salt for each user and use it as the "secret key"


I'd like to hear your thoughts and ideas. How does facebook or twitter do it? Do I really need HSM to store private keys for encryption or there's some kind of alternative (eg: safe file system) ?


Solution

  • Edited 2024-05-01 to clarify from comments made in stof's answer. My answer was not meant to mislead and I hope the update clarifies things.

    Using JWTs or not depends on your risk appetite. Individual JWTs cannot be revoked server-side very easily so could be unsuitable for highly secure applications.

    A normal session token that is checked server-side may be more suitable if you don't want any risk regarding revoked tokens being used. Yes, you can do this with JWTs (i.e. check the user account is still enabled server-side), but then you have to question the benefit of using JWTs in the first place if you don't trust the signed request.

    For example, if the system issues a JWT for admin requests, and the user finds out that their account has been compromised and changes the password, their JWT will still be valid until the expiration. This could allow an attacker to continue any active sessions until the JWT expires.

    If you're relying on JWTs alone for authentication and/or authorisation, then you are accepting the risk that you cannot easily revoke individual JWTs.

    Of course you can revoke the certificate or the signing keys themselves, but this may be a drastic measure on a large system that could have many JWTs signed by the same mechanism. You would be executing a mass logout of users, for the benefit of the few, which could have implications, especially in a multi-tenanted architecture.

    Short lived tokens can mitigate this, so if a JWT expires in say five minutes and then a refresh token (which is checked server-side) is used to get a new JWT, it can limit exposure of the system after a credentials breach or session takeover. This rolls back into my question regarding your risk appetite and what's acceptable for your system.

    There is also a middle ground available here. Trust the JWT for non-sensitive requests (e.g. read only view of a profile page), but check server side against a revocation list too before any sensitive actions are taken. Tokens would get added to the revocation list upon logout, or the account being disabled, locked or deleted. This could be done by adding an ID to the claim, and the revocation list is simply a list of revoked IDs stored server-side, maybe with the date/time of revocation and the reason stored against the record. This way you have the best of both worlds. Performance, plus the extra security when required.

    In addition you could have a gateway that only lets valid JWTs through to the application endpoints based on their valid signature, and any further checks such as revocation lists are implemented server-side where appropriate.

    The following are comments on the original question, rather than on JWTs in general.

    HSM is a good option, although you'll either need to cache the key in memory to validate every subsequent request unless you are using an asymmetric algorithm where the public key can be stored freely.

    The file system may be "secure enough" given that an external attacker cannot arbitrarily access files stored on your server, although an asymmetric algorithm such as EC or RSA would be highly recommended. These also mitigate brute force attacks of the key.

    Having a per user key somewhat defeats the objective of having a client-side session state mechanism as you will have to lookup this key on every request in your database.

    See also Are JWTs a secure option for user authentication?

    And also this question.