I really want to use JWT for API access, to keep it stateless. But at the same time I need to have strong security recourse to deny tokens that are yet to expire.
For more sensitive user information APIs I can rely on forcing a fresh login, comparing the IP address, etc. But I still want to be able to revoke a users token if needed. I don't mind paying the overhead price.
What I imagined would be to have each user create their own secret key based on their password, and store it in the session. I don't mind trading the overhead for an easier way to deal with stolen tokens. This way a simple password reset should invalidate old tokens.
Acknowledging the trade off, does this method make sense? Are there better ways to go about this?
You should create a "blacklist" on your server. If a token needs to be revoked, place it in the blacklist and set it to expire from the list when the token expires. For every authentication attempt, you will verify that the incoming JWT is not in the blacklist. Redis can make this quite easy.
Alternatively, consider a third-party service such as Stormpath. Disclaimer: I work for Stormpath. We have an Oauth2 api that let's you issue access + refresh tokens (for a password grant flow). We handle revocation for you, so long as you don't mind the overhead of the REST call to verify the state of the token. Please see Using Stormpath for OAuth 2.0 and Access/Refresh Token Management. We have easy support for this in our Express-Stormpath .library
well, i just had the same kind of implementation. add hashed password to token, and when client returns the token, during validation, check if user's password has been changed in db, if user's hashed pass is not the same as the one you put in token, reject the token. In this way, you don't need to keep any info about user and/or token on the server.
I don't like the idea of white/black listing tokens, so I ended up using the users hashed password + another random key as their token's secret key:
+---------------+------------------------------------+-----------+
| email | password | key |
+---------------+------------------------------------+-----------+
| user#mail.com | asfsifj2fij4f4f4f8d9dfhs.8f8fhsd8h | r4nd0Mk3Y |
+---------------+------------------------------------+-----------+
I then keep a cache in memory of users id=>password+key to verify each users token. This way tokens can be discarded when: 1) user resets password; 2) application changes the user key.
This almost defeats the purpose of JWT's, but I need this layer of security for my use case.
JSON Web Token
JSON Web Token (JWT) is defined by the RFC 7519.
It's a standard method for representing claims securely between two parties. JWT is a self-contained token and enables you to store a user identifier, an expiration date and whatever you want (but don't store passwords) in a payload, which is a JSON encoded as Base64.
The payload can be read by the client and the integrity of the token can be easily checked by verifying its signature on the server.
Tracking your tokens
You won't need to persist JWT tokens if you don't need to track them.
Althought, by persisting the tokens, you will have the possibility of invalidating and revoking the access of them. To keep the track of JWT tokens, instead of persisting the whole token, you could persist the token identifier (the jti claim) and some metadata (the user you issued the token for, the expiration date, etc) if you need.
Your application can provide some functionality to revoke the tokens, but always consider revoking the tokens when the users change their password.
When persisting tokens, always consider removing the old ones in order to prevent your database from growing indefinitely.
Additional information
When sending sensitive data over the wire, your best friend is HTTPS and it protects your application against the man-in-the-middle attack.
To find some great resources to work with JWT, have a look at http://jwt.io.
Related
For instance, if an attacker gets ahold of your JWT token , they could start sending requests to the server identifying themselves as you and do things like make service changes, user account updates, etc. Once an attacker has your JWT it is game over.
So how we can secure our routes with JWT token if token is stolen ?
Basically you're right, if an attacker steals an access token (it doesn't have to be a JWT), then she can do anything that that token allows, and for as long as the token is valid. There are some steps you can take to mitigate the problem or do some damage control:
Keep short expiration times of tokens. If the token lives for only 2 or 5 minutes then the amount of data an attacker can steal will be limited.
Limit scopes of tokens. You shouldn't have tokens with which you can do everything. If you issue tokens which allow the user only to read their data, then even if someone steals it, they won't be able to change the password.
Use proof-of-possession tokens. These are tokens which are tied to the client which received them in the first place. When someone steals such a token, they won't be able to access the API, unless they also have access to a proof of possession (which can be a certificate). These are examples of POP tokens: Certificate-bound access tokens and DPoP tokens
You can use something which at Curity we called a Token Handler pattern to hide your tokens from the browser and fallback to good old sessions. This makes your tokens much harder to be stolen.
If you're working with JWTs, have a look also at an article I wrote about security best practices when handling JWTs.
JWT is a stateless authentication mechanism as the user state is never saved in server memory.
How to invalidate the token if administrator blocks the user for some resons?
JWT is not an authentication mechanism but a token format. Since JWT are self-contained, you CAN use them for stateless authentication. However, this does not mean that your authentication mechanism MUST be stateless (although there it has its benefits).
There are several options for handling user lockout / revoking authorization:
Do a lookup of the user in every request after validating the JWT to see if the user is locked out
Access tokens are supposed to be short-lived, so you can look up the user the next time a new access token is requested (e.g., using a refresh token) and then refuse issuing a new access token
Alternatively, you can blacklist all tokens issued for a specific user by storing their jti in a database. See also: https://auth0.com/blog/denylist-json-web-token-api-keys/. EDIT: As pointed out in the comments, while not strictly stateless, this approach is still efficient, because a blacklist only needs to store blacklisted tokens for the duration of their lifetime, and lookup should be highly efficient.
You can look up the user identified by a specific JWT every N requests or whenever X percent of the lifetime of the JWT has passed rather than doing it in every request.
None of these approaches is entirely stateless. In general, stateless authorization is not possible if you want it to be possible to revoke authorization. If you want your tokens to be entirely stateless, you should make sure their lifetime is as short as possible, and issuing a new token is not stateless.
I am developing a REST application with its own authentication and authorization mechanism. I want to use JSON Web Tokens for authentication. Is the following a valid and safe implementation?
A REST API will be developed to accept username and password and do the authentication. The HTTP method to be used is POST so that there is no caching. Also, there will be SSL for security at the time of transit
At the time of authentication, two JWTs will be created - access token and refresh token. Refresh token will have longer validity. Both the tokens will be written in cookies, so that they are sent in every subsequent requests
On every REST API call, the tokens will be retrieved from the HTTP header. If the access token is not expired, check the privileges of the user and allow access accordingly. If the access token is expired but the refresh token is valid, recreate new access token and refresh token with new expiry dates (do all necessary checks to ensure that the user rights to authenticate are not revoked) and sent back through Cookies
Provide a logout REST API that will reset the cookie and hence subsequent API calls will be rejected until login is done.
My understanding of refresh token here is:
Due to the presence of refresh token, we can keep shorter validity period for access token and check frequently (at the expiry of access token) that the user is still authorized to login.
Please correct me if I am wrong.
A REST API will be developed to accept username and password and do
the authentication. The HTTP method to be used is POST so that there
is no caching. Also, there will be SSL for security at the time of
transit
This is the way most do it, so you're good here.
At the time of authentication, two JWTs will be created - access token
and refresh token. Refresh token will have longer validity. Both the
tokens will be written in cookies so that they are sent in every
subsequent requests
Storing the tokens in cookies I not dangerous in itself, but if you somehow get you JWT module on your server to read them from there you vulnerable to CSRF attacks where any webpage can trigger a users browser to send a form + you sites cookie to your server unless you use CSRF tokens. So generally they are stored in localStorage and "manually" added to request headers every time.
On every REST API call, the tokens will be retrieved from the HTTP
header. If the access token is not expired, check the privileges of
the user and allow access accordingly. If the access token is expired
but the refresh token is valid, recreate new access token and refresh
token with new expiry dates (do all necessary checks to ensure that
the user rights to authenticate are not revoked) and sent back through
Cookies
Apart from the cookie dangers, it seems safe.
Provide a logout REST API that will reset the cookie and hence
subsequent API calls will be rejected until login is done.
You don't even need to make an API call, you can simply just purge the cookies or the localStorage object and make sure your client doesn't break on missing tokens.
The standard for the express-jwt module expects the tokens to be in its own "Authorization: Bearer [Token]" header, which I would strongly recommend over cookies. The localStorage API is available all the way back to IE8 so you should be good.
Edit:
First, it's important to know the difference between XSS and CSRF attacks since they're often believed to be the same thing.
XSS is when users get unsafe JS running on your domain in other users browsers when that happens neither JWT in localStorage or sessions and JWT in cookies are safe. With httpOnly flag on cookies, you can't directly access them, but the browser will still send them with AJAX requests to your server. If this happens you generally out of luck. To prevent this, make sure to escape all user input if it's sent to the browser.
If you load 3rd party JS with script tags or iframes this might compromise localStorage unless you are careful, but I haven't worked enough with this to help you here.
CSRF is only when other domains are trying to send normal HTML forms to your server by getting the browser to send cookies automatically. Frameworks prevent this by inserting unique random strings as hidden fields and checking them again when it's submitted. JWT's in localStorage is safe from this since each domain gets its own separate localStorage area.
But ultimately all this depends on if your service will be using one single domain, in which case httpOnly cookies will be plenty secure and easier to set up, but if you wanna spread your service out on multiple domains like api.domain.com + app.domain.com or add a native app you're forced to store you're JWTs in localStorage or some other native storage area.
Hope this helps!
I asked this question two years back and also accepted the answer. However, based on my experience and study in the last two years, I'd like to answer this just in case someone stumbles on this thread with the same question.
The approach mentioned in the question is similar to the "Resource Owner Password Credentials" grant type of OAuth 2.0. However, I think it is better to use the "Authorization Code Grant" type instead and Cookie to store the tokens instead of browser localStorage or sessionStorage. I have detailed my reasons, implementation points, security considerations and references in this StackOverlow answer.
Like OP I been using resource owner password grant.
I learned so much from Saptarshi Basu's other answer in a different post I think anyone looking into OAuth Code Flow should take a look at it, it has outlined a very solid approach to auth SPA and resource servers. It primarily relies on your backend(resource server) to handle authentication with the auth provider as a private client.
However, I will just add that people looking at implementing authentication with SPA should also consider OAuth Code Flow with PKCE. The main goal of PKCE is to allow public client such as SPA to authenticate directly with auth provider. All PKCE adds, is that when a SPA app initiates authentication, a hashed value is sent to the auth provider when the user is authenticated. And after user authenticate with the authorization provider, it redirects the user back to SPA with that hashed value as well as authorization code. Now, for the next part where the SPA calls auth provider to exchange code for tokens, instead of providing client secret, it has to provide the key that was originally used to create the hashed value. This mechanism guarantees the code cannot be used by someone who intercepted the code, and the SPA doesnt need to store a client secret like a server-side app does.
Now the only thing I'm not certain at this point is which is technically more secure, server-side authentication using standard Code Flow without PKCE or SPA authenticating directly using PKCE? Most resources I could find online currently describes and recommends the latter . However I feel that letting a private server side client handle authentication (as Saptarshi Basu described) might still be more secure. I would love to hear his opinion on this as well.
My understanding of refresh token here is:
Due to the presence of refresh token, we can keep shorter validity period for access token and check frequently (at the expiry of access token) that the user is still authorized to login.
Please correct me if I am wrong.
Assuming you're talking about using JWT as Bearer-token in OAuth (and I would strongly advice to follow the OAuth 2.0 protocol), that's right.
With an additional auth-time (timestamp of authentication) claim in your JWT, you could even drop the second token and sent your access- as a refresh-token (the auth-server could then issue a new access-token if token is valid & auth-time within allowed range)... but sure, it's also good to follow the standard ;)
Anyway, there are certain additional aspects (that tend to get difficult or are even against the fundamental ideas of JWT) you should consider before using JWTs as refresh-token, as this basically means you introduce long-living JWT:
do you need to have something like forced user logout/ token revocation by subject (e.g. if user got identified as fraudulent)?
do you need to have something like revocation of a specific token (e.g. if a user looses a device)?
...
Dependent on your use-case you should consider all the possible implications, long-living tokens have as they usually require you to introduce some kind of state on your server-side (e.g. to allow revocation/ blacklisting). Keep in mind the beauty and security of the JWT concept lies within JWTs being short-lived.
I don't understand the point of having access token in authentication. Below is a paragraph of explanation I took but I still confused. Since every api call still go to the db look for the token, what's the different check for the username and password for every http request?
Instead of forcing clients to send username and password with every
request you can have a "get_access_token" function in your RESTful
service that takes the username and password and responds with a
token, which is some sort of cryptographic hash that is unique and has
some expiration date associated with it. These tokens are stored in
the database with each user. Then the client sends the access token in
subsequent requests. The access token will then be validated against
the database instead of the username and password.
Using the access token limits the amount of time the username and password are being used and sent across the wire.
How many times do you want your username and password, SSN, or other sensitive data do you want being stored and transmitted? Do you want that on every request?
First of all, access tokens are typically validated by checking the digital signature, which does not require the receiving service to talk to the issuing server. The client gets an access token once and uses it until it expires.
But even if the token had to be checked against the database on every call (when using reference tokens for example), tokens are still preferred over sending username and password on each call. They remove the need for the client to keep the password in memory (or elsewhere), where it can easily be stolen.
(1) Access token is less sensitive than your password. Access tokens typically expire after a short time (this is a requirement in the Oauth threat model), whereas passwords tends to be long term. If somebody grabs your access token, there is limited damage they can do. If they grab your password, then there is a lot of damage that they can do. Especially if you use the same or related passwords on multiple sites.
(2) If the server implemented password verification securely, then they should be using a slow function like PBKDF2, bcrypt, or scrypt to validate your passwords. These functions are designed to be slow so that if somebody gets access to the database, they will not be able to reverse many passwords: see Our password hashing has no clothes. Given that password checking is supposed to be slow, we don't want to be doing it often! Validation of access tokens is much quicker however.
(3) The system that grants you access to a resource ("resource provider") might not be the same as the system that checks your identity ("identity provider"). For example, many websites including StackOverflow allow you go login with your gmail account. In this case, Google is the identity provider and StackOverflow is the resource provider. Would you really want to provide your gmail password to StackOverflow? I hope not.
In restful application, for some reqirements,e.g. On client side,for the same user can using only one access token at the same time and user can get new access token via login successfully.if logined twice,the user will got two different access token say it's access token A and B.In the backend,when user still use token A to talk with server, it should be invalid and the latter B should be valid!How to implement this without using cache framework or db?
Additionally in the backend, i don't want store any access token, the access token contains simple user info and timestamp etc, which is a string encrypted with aes and encoded with Base64.
Unfortunately, you won't be able to do what you want without persisting the tokens.
I answered a question related to token based authentication before. Maybe you can get some inspiration from there.
Different types of tokens
Basically, a token can be opaque (which reveals no details other than the value itself, like a random string) or can be self-contained (like JSON Web Token).
Random String: A token can be issued by generating a random string and persisting it to a database with an expiration date and with a user identifier associated to it.
JSON Web Token (JWT): Defined by the RFC 7519, it's a standard method for representing claims securely between two parties. JWT is a self-contained token and enables you to store a user identifier, an expiration date and whatever you want (but don't store passwords on it) in a payload, which is a JSON encoded as Base64. The payload can be read by the client and the integrity of the token can be easily checked by verifying its signature on the server. You won't need to persist JWT tokens if you don't need to track them. Althought, by persisting the tokens, you will have the possibility of invalidating and revoking the access of them. To find some great resources to work with JWT, have a look at http://jwt.io.
Persisting tokens
There are many databases where you can persist your tokens. Depending on your requirements, you can explore different solutions such as relational databases, key-value stores or document stores.
Just remember to remove old tokens in order to prevent your database from growing indefinitely ;-)
The access token must be send in every request in Authorization header. For every user you store his access token. So access token arrives, you check its value and find which user it is. If you do not find access token, user is not authorized.
So basically when you generate new token, you replace the old access token for given user, when old access token comes you are not able to recognize it (it is not stored anywhere), therefore it is invalided.