I am creating a web server for a site with authentication using JWTs. Since this is going to be a publicly available system once ready, I would like to know if the following authentication flow using JWT access and refresh tokens could be considered secure.
In this case it is assumed that one server serves both the auth API and resources
When the server starts, it will generate a "run ID" which is the current UNIX timestamp and random characters/UUID. This is stored in memory and any restart will generate a new one.
The user obtains an initial HttpOnly access and refresh tokens when logging in or registering. Access token has a short lifespan (~5 mins.) and refresh token has a longer lifespan (~2 hours).
The access token functions like normal and contains the userID.
The refresh token has the userID, the server's run ID and its own unique "token ID" (similar to run ID, unique for each refresh token)
Instead of the user calling a token refresh endpoint, new access and refresh tokens are made when calling an endpoint that requires authorisation if the following criteria are met:
The access token has expired
The refresh token has not expired
The run ID in the refresh token matches up with the current run ID
The refresh token's token ID isn't blacklisted
In the event that there is a run ID mismatch or the token ID has been put on a blackist, the refresh token is instantly considered invalid.
The token ID blacklist is stored in memory (like the run ID) as an array with the ID and expiration time.
This allows a user to logout and blacklist that specific refresh token from being used.
This also allows any system administrators to emergency invalidate all refresh tokens by restarting the server since the runID will change.
I would like to know if this can be considered a secure authentication flow
It took me a while to understand what is going on with the tokens and your motivation around it. I believe what you did here is more complicated than reality requires and there should be little reason to introduce this level of complexity. Please also note, that security in its definition is a double edged sword. It's an eternal conflict between integrity and confidentiality VS availability. If for some reason we screw the systems availability then it will not be considered secure in the same sense, that turning a computer off is not considered a solution of security issues.
Having said that, let's look into your solution.
You intend to use access and refresh JWT tokens. How should a logout in such system look like? Well, you simply forget the token information on the client side or in case you want to hold tokens in cookies - which I believe you want to have based on "HttpOnly" flag - you can clean up the cookies. This is it. Once the information is gone from the client, the tokens can not be easily recreated. There is no need to blacklist the tokens on the server. It is in clients best interest to 'forget' the tokens he is not using and once the token is gone, there is no second copy to replace it.
We use token blacklisting in case we want to deal with a scenario like real-time user account locking. An expensive thing to implement if you ask me.
You can still try to blacklist refresh tokens, but assuming your authentication mechanism has access to the user pool, it can simply check in the database if the user is locked, no need to cache anything here.
Next thing I notice is that what you did here reassembles strongly simple Http Session handling including loosing the session once the server restarts. Why not forget the tokens and use a simple session instead?
I also wonder, why you intend to have only one instance of your system. In the High Availability age and playing with JWT tokens it would totally make sense to separate authentication module from logic and have more than one instance of both.
Please also note, that no one will be able to tell you, whether what you do is secure, until they can look into your code, configuration and ideally - working system. There are many small pieces that can break the solution. Ideally have a look into the OWASP ASVS (Application Security Verification Standard) available here to see, how deep the rabbit hole goes.
Related
Good day friends,
I am developing an application, in my user system add the functionality for administrators to raise the level of the account (if level 10, it's admin) or ban it from the site.
Once the user logs in, he receives a token that contains information about the level of his account, whether it is banned or not.
This function works perfectly, but if the user is banned, they can continue using the site until they receive a new token or it has expired, since the information is acquired from the token. I want to force the user to close their session (destroy JWT token) once it is banned or its level has been edited.
I can verify in the routes that every time the user makes a call, the backend checks in the database if the user is banned or not. But I would like to simplify this step so that the server does not make so many calls to the database. It occurs to me to remove the user's specific token or make it invalid once it is banned from the site or its level has been altered. So the user is forced to log in again and get a new token.
Is there a method or library that makes it easy for me to remove the tokens or make them invalid?
Because if i enter with my admin account and try to edit my account and give it a level 1, i should not be able again to edit this user because my lvl is 1 and i need lvl 10 again for using this route functions, but i can edit it again because token authorization is valid and that shouldn't happen
I want to force the user to destroy their JWT token once it is banned or its level has been edited.
This is not possible, a user can retain any data you've given them.
The best solution you can get with tokens would be to have a very short expiry for their access tokens, and verify the blocked status and level every time you create a new one for them. If a few minutes of lag is acceptable to you, this is the way to go.
Is there a method or library that makes it easy for me to remove the tokens or make them invalid?
You are looking for plain HTTP sessions. You would not hand out any signed tokens with the data at all, you would store the session data on the server side - in ram, an extra cache, or a database. From there you can purge it to invalidate the session and log out the user, or change their access status.
There's a standard implementation for about every server ecosystem. Don't implement this yourself with a database request on every route, use a middleware solution like express-session (see How to use session variable with NodeJs?).
In my backend i check how long it is left before the JWT expires (15 min). If it is one minute or less i create a new one and attach it to the response in a setToken header.
The front will then store and use the new one. Is this a good approach? Opinions on the matters?
Thanks
Your idea is good. You are implementing a token refresh algorithm.
Depends of use cases, but it could be better to give back a new fresh token more frequently, not only when the token is going to expire in the next minute.
For example, a common approach is to give back a fresh token with a probability distribution (for example, each requests has a probability of 30% to send back a fresh token) combined with a timing algorithm like yours. So, for each request:
if (30% of times true || token is expiring in next n seconds)
give a new refresh token.
The probability distribution is used to limit che cpu usage required for signing.
Another approach is to implement an API (let's say GET /token) that will just verify the provided JWT, and if it's valid, generate a new fresh JWT as response. With this approach, the "when to refresh" logic is moved to the client. The client should decide when to call the /token API to refresh his token. And usually this is done when the web application / mobile application starts. Or every n minutes if you are using JWT for server to server comunication.
In my opinion, the second approach is preferible.
From my point of view this not really a good idea. The problem is not a security one but rather a logical one.
You are mixing access to the resources with the issuance of the tokens. If the client needs a token, it should explicitly ask for a new one and contact the dedicated endpoint. If the token expired, your server may need additional information (refresh token, verification of the credential materials...).
With your flow, the client will receive a token it haven't asked for. Furthermore, in addition to your application logic your client has to check if a new token is present for each single request. It may miss new access tokens and loose the access rights if the old one expired.
At last but not least, this new token may be useless and never used by the client.
I have a Koa based Node.js backend for my personal/hobby application.
I implemented session handling with JWT tokens. The client (AngularJS) gets the token after a successful login and stores the token somewhere (currently in sessionStorage but for the purposes of this question it shouldn't matter).
I have two questions:
When I need to update the user record which the JWT represents, say, the user turned on Two-factor authentication (2FA) so I asked him to provide his phone number and I'd like to set this phone number in the user's record. Currently, after a successful verification on the phone number I call my backend to update the user record and I create a new JWT token with the updated user record (I exclude sensitive information from the JWT token like the hashed password, but I'd like to include the phone number for client side usage). Is it okay to create a new token when some of the credentials change and update the existing client side token with this new token? Should I never-ever create another token, only to create the one and only upon successful authentication? How do I then update the payload in the token?
How should I handle expired JWT tokens? In my mind I have 3 (possible) scenarios:
2.1. The JWT is set to short living, say 15 minutes. If the backend server replies with a 401 Unauthenticated 'Invalid token' (I guess this is the default behavior of koa-jwt) then I automatically log-out my client and require re-authentication. But I also set up a complementary middleware, which is the last in the chain on the backend to re-create the token with a refreshed expiry and the client would also replace the existing token with the refreshed one. So if the user is active and uses the application every protected API call, in case of success, would create a new token to replace the old token.
2.2. The JWT is set long-living, say 1 week, and if it expires I opt-in re-authentication from the client.
2.3. Copy https://www.rfc-editor.org/rfc/rfc6749#section-1.5. Here when creating the JWT token after a successful authentication we send an access_token as well as a refresh_token. When the access_token is expired and the server responds with HTTP 401 'invalid token' (koa-jwt default) then the client sends the refresh_token to the backend to require a new access_token (and optionally a new refresh_token). In this case I don't fully understand how the refresh_token is verified against the old access_token to provide a new token? Or why do we need to have a refresh_token?
Any generic advice on the upper topics (JWT updates and JWT expiration) would be helpful.
Starting from the bottom, I would ignore refresh tokens as I don't think they will help you here. They are generally aimed at other scenarios where the client application can provide storage more secure than the user browser -- think native mobile applications or server-side web applications.
Refresh Tokens are long-lived. This means when a client gets one from a server, this token must be stored securely to keep it from being used by potential attackers, for this reason it is not safe to store them in the browser.
(emphasis is mine; source refresh tokens)
This means that option 2.3 is basically the same as 2.2, which is not a bad option. It's not uncommon to have web applications with long session duration. If your application is not highly sensitive it's acceptable to use long session to improve user experience. For example, Django uses a default of two weeks for the age of its session cookie. See SESSION_COOKIE_AGE.
The remaining option (2.1), is usually referred as sliding session. The session timeout is short, but as long as the user keeps using the application within that interval the session gets automatically renewed. This is possibly the most common approach, or at least the one I used most time, so I'm biased. The only thing I would note is that sliding session are usually implemented with opaque session identifiers stored client-side as cookies and then with the actual session data stored on the server.
Your approach is a bit different because you have a stateless JWT token (it contains actual user data) stored on browser local storage. Like you said, in order to update the token you'll have to generate a new one, because you'll have to generate a new signature.
The signature is used to verify that the sender of the JWT is who it says it is and to ensure that the message wasn’t changed in the way.
(emphasis is mine; source JSON web tokens)
Having said all that, I would consider the following:
Ask yourself if you really need JWT's or if regular session identifiers stored as cookies (HTTP Only) would simplify your logic.
If JWT's are a requirement, for example, you have another API that will also accept these tokens as authentications, then I would consider option 2.1 or 2.2 as refresh tokens for a browser-based application are not recommended.
Having said that, you should also consider that JWT's are not huge, but they will still be an overhead if you decide to be automatically renewing. You may mitigate this a little by choosing a session duration of 20 minutes and only perform automatic renewal after half the session has elapsed.
Another point is that a vulnerability like XSS in your application will expose the access token to an attacker as the injected scripts would be able to read from localStorage/sessionStorage, this can be another point in favor of HTTP only session cookie storage.
I would like to answer your second question before I can get on to the first one.
Basically the third option which you have mentioned is the best way to renew your access tokens. Access token should be short living(~5mins) and refresh token has longer life. When your access token gets expired, send your refresh token to the backend and get a new access token. So your response should be something like this:
{
"token_type":"bearer",
"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiVlx1MDAxNcKbwoNUwoonbFPCu8KhwrYiLCJpYXQiOjE0NDQyNjI4NjYsImV4cCI6MTQ0NDI2Mjg4Nn0.Dww7TC-d0teDAgsmKHw7bhF2THNichsE6rVJq9xu_2s",
"expires_in":10,
"refresh_token":"7fd15938c823cf58e78019bea2af142f9449696b"
}
So the idea is to seperate your application into Authorization Server (which generates access token / refresh token) & Resource Server (validate access token and access the resources ). You can maintain a schema to validate the refresh token against the access token in Authorization Server. Please refer to the schema section mentioned in this link which might give you some idea. Oauth2. You can modify the schema according to your need. You need not send your refresh token along with your access token for each request call. Refresh token can only be sent to Authorization server for generating new access token. How to generate refresh tokens? If I am using Java, I would use UUID.randomUUID() to generate a unique refresh token.
Now to answer your first question, if you want to update your JWT payload based on your updated user records, then you can use the same refresh token to generate a new access token with the updated payload. The logic remains same because if phone number exists in the user record it gets added to the payload and if not, it will be null in the payload.
The main advantage of using Refresh token is that the Access Tokens can be renewed at any time using Refresh Tokens
When working on an Angular app, I have a single page app that communicates with a JSON web service for data.
A "login" in my Angular app is really just exchanging a username/password for a token. That token is passed as a header on all subsequent requests so the server can authorize them. This works great until the users refreshes their browser window of course (via refresh or leaving the "page" and returning).
Obviously one option would be to make the user enter their username/password again, but that seems like a good way to not have any users.
I can think of 4 options:
Store token in a secure session cookie. (What I'm doing now. I am only using so the client can read. Not used or wanted on the server.)
Store token using local storage of some kind. (Would be insecure and
require manual expiration maintenance.)
Prevent user from refreshing browser with some "onbeforeunload"
code. (I don't like when I get the "are you sure you want to leave
this page" messages and I assume others feel the same.)
Include token as part of url. (Could make url's look big and messy. Possible physical security risk. Might make for extra work with bookmarking and expired tokens.)
Is option 1 the best option for this functionality? Is there something better to do than all of these?
I think option 1 is the best one for your use case. All major web frameworks have support for this option.
In case you need to handle this manually you need to ensure these steps:
The web service will process the initial authentication request by creating and setting a secure authentication cookie. The auth cookie should be time based(only valid for a specific time interval) and its value should be a unique value if possible;
After the initial authentication request all subsequent requests will pass the authentication cookie with the request header automatically - this is handled by the browser.
The web service needs to handle cookie based authentication on subsequent requests by validating the cookie value and returning an error if the cookie has expired.
You need to ensure a client side global authentication handler captures any authentication exceptions and displays a friendly message to the user.
02/20/2011:
It was confirmed by Facebook today that indeed there is one call in which the access_token is broadcast in the open . . . it just happens to be one call I use to make sure that the USER is still logged in before saving to my application database. Their recommendation was to use the SSL option provided as of last month for canvase and facebook as a whole. For the most part the Auth and Auth are secure.
Findings:
Subsequent to my posting there was a remark made that this was not really a question but I thought I did indeed postulate one. So that there is no ambiquity here is the question with a lead in:
Since there is no data sent from Facebook during the Canvas Load process that is not at some point divulged, including the access_token, session and other data that could uniquely identify a user, does any one see any other way other than adding one more layer, i.e., a password, sent over the wire via HTTPS along with the access_toekn, that will insure unique untampered with security by the user?
Using Wireshark I captured the local broadcast while loading my Canvas Application page. I was hugely surprised to see the access_token broadcast in the open, viewable for any one to see. This access_token is appended to any https call to the Facebook OpenGraph API.
Using facebook as a single click log on has now raised huge concerns for me. It is stored in a session object in memory and the cookie is cleared upon app termination and after reviewing the FB.Init calls I saw a lot of HTTPS calls so I assumed the access_token was always encrypted.
But last night I saw in the status bar a call from what was simply an http call that included the App ID so I felt I should sniff the Application Canvas load sequence.
Today I did sniff the broadcast and in the attached image you can see that there are http calls with the access_token being broadcast in the open and clear for anyone to gain access to.
Am I missing something, is what I am seeing and my interpretation really correct. If any one can sniff and get the access_token they can theorically make calls to the Graph API via https, even though the call back would still need to be the site established in Facebook's application set up.
But what is truly a security threat is anyone using the access_token for access to their own site. I do not see the value of a single sign on via Facebook if the only thing that was established as secure was the access_token - becuase for what I can see it clearly is not secure. Access tokens that never have an expire date do not change. Access_tokens are different for every user, to access to another site could be held tight to just a single user, but compromising even a single user's data is unacceptable.
http://www.creatingstory.com/images/InTheOpen.png
Went back and did more research on this:
FINDINGS:
Went back an re ran the canvas application to verify that it was not any of my code that was not broadcasting.
In this call: HTTP GET /connect.php/en_US/js/CacheData HTTP/1.1
The USER ID is clearly visible in the cookie. So USER_ID's are fully visible, but they are already. Anyone can go to pretty much any ones page and hover over the image and see the USER ID. So no big threat. APP_ID are also easily obtainable - but . . .
http://www.creatingstory.com/images/InTheOpen2.png
The above file clearly shows the FULL ACCESS TOKEN clearly in the OPEN via a Facebook initiated call.
Am I wrong. TELL ME I AM WRONG because I want to be wrong about this.
I have since reset my app secret so I am showing the real sniff of the Canvas Page being loaded.
Additional data 02/20/2011:
#ifaour - I appreciate the time you took to compile your response.
I am pretty familiar with the OAuth process and have a pretty solid understanding of the signed_request unpacking and utilization of the access_token. I perform a substantial amount of my processing on the server and my Facebook server side flows are all complete and function without any flaw that I know of. The application secret is secure and never passed to the front end application and is also changed regularly. I am being as fanatical about security as I can be, knowing there is so much I don’t know that could come back and bite me.
Two huge access_token issues:
The issues concern the possible utilization of the access_token from the USER AGENT (browser). During the FB.INIT() process of the Facebook JavaScript SDK, a cookie is created as well as an object in memory called a session object. This object, along with the cookie contain the access_token, session, a secret, and uid and status of the connection. The session object is structured such that is supports both the new OAuth and the legacy flows. With OAuth, the access_token and status are pretty much al that is used in the session object.
The first issue is that the access_token is used to make HTTPS calls to the GRAPH API. If you had the access_token, you could do this from any browser:
https://graph.facebook.com/220439?access_token=...
and it will return a ton of information about the user. So any one with the access token can gain access to a Facebook account. You can also make additional calls to any info the user has granted access to the application tied to the access_token. At first I thought that a call into the GRAPH had to have a Callback to the URL established in the App Setup, but I tested it as mentioned below and it will return info back right into the browser. Adding that callback feature would be a good idea I think, tightens things up a bit.
The second issue is utilization of some unique private secured data that identifies the user to the third party data base, i.e., like in my case, I would use a single sign on to populate user information into my database using this unique secured data item (i.e., access_token which contains the APP ID, the USER ID, and a hashed with secret sequence). None of this is a problem on the server side. You get a signed_request, you unpack it with secret, make HTTPS calls, get HTTPS responses back. When a user has information entered via the USER AGENT(browser) that must be stored via a POST, this unique secured data element would be sent via HTTPS such that they are validated prior to data base insertion.
However, If there is NO secured piece of unique data that is supplied via the single sign on process, then there is no way to guarantee unauthorized access. The access_token is the one piece of data that is utilized by Facebook to make the HTTPS calls into the GRAPH API. it is considered unique in regards to BOTH the USER and the APPLICATION and is initially secure via the signed_request packaging. If however, it is subsequently transmitted in the clear and if I can sniff the wire and obtain the access_token, then I can pretend to be the application and gain the information they have authorized the application to see. I tried the above example from a Safari and IE browser and it returned all of my information to me in the browser.
In conclusion, the access_token is part of the signed_request and that is how the application initially obtains it. After OAuth authentication and authorization, i.e., the USER has logged into Facebook and then runs your app, the access_token is stored as mentioned above and I have sniffed it such that I see it stored in a Cookie that is transmitted over the wire, resulting in there being NO UNIQUE SECURED IDENTIFIABLE piece of information that can be used to support interaction with the database, or in other words, unless there were one more piece of secure data sent along with the access_token to my database, i.e., a password, I would not be able to discern if it is a legitimate call. Luckily I utilized secure AJAX via POST and the call has to come from the same domain, but I am sure there is a way to hijack that.
I am totally open to any ideas on this topic on how to uniquely identify my USERS other than adding another layer (password) via this single sign on process or if someone would just share with me that I read and analyzed my data incorrectly and that the access_token is always secure over the wire.
Mahalo nui loa in advance.
I am not terribly familiar with Facebook's authentication/authorization methods, but I do believe that they implement oauth (or something close to it) for delegation, distributed authorization, and "single sign-on".
OAuth is described by RFC-5849
EDIT: Facebook Uses OAuth 2.0 which is still in working draft.
In OAuth, and similar systems, the "access_token" is only part of the picture. There is also typically a secret key, which is known only by the service provider (facebook) and the client application (your app). The secret key is the only part that is expected to stay secret - and that part is never sent over the wire (after it's initial issuance).
In the case of Facebook, I think the secret key is assigned to you when you register your application to use their API, and the 'access_token' is returned to you for a given user, whenever the user agrees to allow your app to access their info.
Messages are sent in the clear, including the user's username, and the relevant "access_token"; However, each message must also include a valid signature in order to be accepted by the server. The signature is a cryptographically computed string, that is created using a technique called HMAC.
Computing the HMAC signature requires both the token and the secret, and includes other key parts of the message as well. Each signature is unique for the given message contents; and each message uses a nonce to ensure that no two messages can ever be exactly identical.
When the server receives a signed message, it starts by extracting the access_token (clear-text), and determining which app the token was issued for. It then retrieves the matching secret from its own local database (the secret is not contained in the message). Finally, the server uses the clear-text message, the clear-text access_token, and the secret to compute the expected HMAC signature for the message. If the computed signature matches the signature on the received message, then the message must have been sent by someone who knows the same secret (i.e. your application).
Have a look at Section 3.1 of RFC-5849 for an OAuth specific example, and further elaboration on the details.
Incidentally, the same approach is used by Amazon to control access to S3 and EC2, as well as most other service providers that offer API access with long-term authorization. Suffice it to say - this approach is secure. It might be a little counter-intuitive at first, but it makes sense once you think it through.
Adding a few links and quotes from Facebook Documentation:
Facebook is indeed using the HMAC-SHA256 algorithm. Registration document (PHP Example reading signed_request section).
Always verify the signed_request:
If you are unable to validate the
signed_request because you can't embed
your application secret (e.g. in
javascript or a desktop application)
then you MUST only use one piece of
information from the payload, the
oauth_token.
The Authentication Document contains a lot of useful info about the different flows you may use to authenticate a user. Also read the Security Considerations section at the bottom of the page:
Cross site request forgery is an
attack in which an trusted
(authenticated and authorized) user
unknowingly performs an action on
website. To prevent this attack, you
should pass an identifier in the state
parameter, and then validate the state
parameter matches on the response. We
strongly recommend that any app
implementing Facebook user login
implement CSRF protection using this
mechanism.
It was confirmed by Facebook that indeed there is one call in which the access_token is broadcast in the open - it just happens to be one call I use to make sure that the user is still logged in before saving to my application database. Their recommendation was to use the SSL option provided as of last month for canvas and Facebook as a whole. For the most part the Auth and Auth are secure.
To ensure a secure interface between a third party application and a Facebook application or even any website that uses Facebook Single Sign on, an identity question would provide the extra layer when used in conjunction with the access_token.
Either that or require your users to use Facebook with the new SSL feature of Facebook and Facebook Canvas Applications. If the access_token is broadcast in the open it cannot be used to uniquely identify anyone in your third party database when needing to have a confirmed identity before database interactions.