Everything I've read about using JWTs for websites seems to imply they are for SPAs.
My website has many static pages, but also needs user to sign in. Is using JWTs appropriate?
Depends on what you mean by appropriate.
If you generate a jwt properly and store it in a httponly, secure cookie, that will be the same as a plain old session id in many respects (but it may be stateless on the server, with all of the potential vulnerabilities of storing state on the client, like for instance replay).
If you store it any other way than a httponly cookie, it will be less secure. A single xss in your application will then allow an attacker to steal user tokens. Whether you want to accept this risk depends on you, but for security-critical applications, it may not be good enough.
So in short a jwt is just a signed (but not encrypted by default) way of storing stuff on the client. A user or attacker cannot change contents easily, but that's it, he can read it, replay it, etc.
If you only store some kind of an id in it, it's not different from a plain session id (and in that case there is no point in using it). If you store state (session data), that may bring vulnerabilities that you have to care about yourself, like for example the risk of replay attacks, a user being able to find the signing key from the aplication and sign jwts for himself, a flaw in your jwt implementation, users being able to see session contents (as jwts are not encrypted), etc.
Related
I would like to not have to implement things like redis or storing refresh tokens in a database. I would like to utilize the "full power" of JWTs (scalability, no need for storing anything related to sessions/tokens, stateless, etc.)
Say I have a cookie which is set as secure, httpOnly, samesite=lax.
The cookie will expire when the user closes the website, unless the user specified the "remember me" option. And inside this cookie a JWT will be stored which will never expire.
Of course some CSRF protection would also be implemented!
Is this secure enough? How would an attacker ever retrieve the JWT from a client given the requirements (cookie requirements) mentioned above?
If the JWT is completely inaccessible from an attacker then there is no need to revoke the JWT or have it expire at some point right?
To the question "Is this secure enough?", only you can answer and decide if you want to go with this solution or not.
For an outsider point of view; I think a JWT set with no expiration is already not good practice, because by doing so you are kind of giving away some endless access.
Then you write "How would an attacker ever retrieve ....". That is one more unsafe assumption. The proper of a "successful attack" is usually, exactly when an attacker does something in a way you did not expect.
To sum it up I would say: what you are thinking of is possible; but it is not something to recommend.
To get more in depth opinions you may check further documentations or with experts. To start with you may have a look at these few JWT tutorials.
IMHO if you are protecting sensitive information, having long living auth tokens in the cookies is dangerous. A user could be tricked using social engineering to open the dev tools and share the cookie content. Security is pain but pays off in the log run.
UPDATE: I have concluded my research on this problem and posted a lengthy blog entry explaining my findings: The Unspoken Vulnerability of JWTs. I explain how the big push to use JWTs for local authentication is leaving out one crucial detail: that the signing key must be protected. I also explain that unless you're willing to go to great lengths to protect the keys, you're better off either delegating authentication via Oauth or using traditional session IDs.
I have seen much discussion of the security of JSON Web Tokens -- replay, revocation, data transparency, token-specified alg, token encryption, XSS, CSRF -- but I've not seen any assessment of the risk imposed by relying on a signing key.
If someone breaches a server and acquires a JWT signing key, it seems to me that this person could thereafter use the key to forge unexpired JWTs and secretly gain access. Of course, a server could look up each JWT on each request to confirm its validity, but servers use JWTs exactly so they don't have to do this. The server could confirm the IP address, but that also involves a lookup if the JWT is not to be trusted, and apparently doing this precludes reliable mobile access anyway.
Contrast this with a breach of a server based on session IDs. If this server is hashing passwords, the attacker would have to snag and use a session ID separately for each user before it expires. If the server were only storing hashes of the session IDs, the attacker would have to write to the server to ensure access. Regardless, it seems that the attacker is less advantaged.
I have found one architecture that uses JWTs without this disadvantage. A reverse proxy sits between untrusted clients externally and a backend collection of microservices internally, described here by Nordic APIs. A client acquires an opaque token from an authorization server and uses that token to communicate with the server app for all requests. For each request, the proxy translates the opaque token into a JWT and caches their association. The external world never provides JWTs, limiting the damage wrought by stealing keys (because the proxy goes to the authentication server to confirm the opaque tokens). However, this approach requires dereferencing each client token just as session IDs require per-request dereferencing, eliminating the benefit of JWTs for client requests. In this case, JWTs just allow services to pass user data among themselves without having to fully trust one another -- but I'm still trying to understand the value of the approach.
My concern appears to apply only to the use of JWTs as authentication tokens by untrusted clients. Yet JWTs are used by a number of high-profile APIs, including Google APIs. What am I missing? Maybe server breaches are rarely read-only? Are there ways to mitigate the risk?
I believe you're thinking about this the wrong way. Don't get me wrong, it's great you're considering security, however the way you're approaching it in regards to double checking things server-side, adding additional checks that defeat the objective of stateless sessions, etc, appear to be along a one way street towards the end of your own sanity.
To sum up the two standard approaches:
JWTs are sessionless state objects, MAC'd by a secret key held server side.
Traditional Session Identifiers are stored either in memory or in a database server-side, and as you say are often hashed to prevent sessions from being hijacked should this data be leaked.
You are also right that write access is often harder for an attacker to achieve. The reason is that database data is often extracted from a target system via a SQL injection exploit. This almost always provides read access to data, but it is harder to insert data using this technique, although not impossible (some exploits actually result in full root access of the target machine being achieved).
If you have a vulnerability that allows access to the key when using JWTs or one that allows database tables to be written to when using session identifiers, then it's game over - you are compromised because your user sessions can be hijacked.
So not more damaging necessarily, it all depends on the depth of the vulnerability.
Double check that the security of your JWT keys align with your risk appetite:
Where are they stored?
Who has access?
Where are backups stored?
Are different keys used in pre-production and production deployments of your app?
The ways to mitigate is as good practise dictates with any web app:
Regular security assessments and penetration testing.
Security code reviews.
Intrusion detection and prevention (IDS/IPS).
WAF.
These will help you evaluate where your real risks lie. It is pointless concentrating on one particular aspect of your application so much, because this will lead to the neglect of others, which may well be higher risk to your business model. JWTs aren't dangerous and have no more risk than other components of your system necessarily, however if you've chosen to use them you should make sure you're using them appropriately. Whether you are or not comes down to the particular context of your application and that is difficult to assess in a general sense, so I hope my answer guides you in the right direction.
When an attacker is able to get hold of the signing key in a JWT based system that means that he is able to get access to the server backend itself. In that case all hope is lost. In comparison to that, when the same attack succeeds in session based systems the attacker would be able to intercept username/password authentication requests to the backend, and/or generate sessions ids himself, and/or change the validation routines required to validate the session ids and/or modify the data to which the session id points. Any security mechanism used to mitigate this works as well for session systems as for JWT systems.
I don't want users to have to enter their passwords each time, and I don't want to store the passwords locally. When the user first sends me their username and password, I'm planning to send back some hashed version of their password (hash of pasword+salt), which will be put into localstorage to be used to authorize subsequent calls to our web services.
I'm aware that if my localstorage is compromised (e.g. stolen phone), the thief will be able to steal the token and make web service calls as the user, but at least they won't have the user's password.
Are there other vulnerabilities that I'm overlooking? Is there a reason to put it in a cookie instead of localstorage?
There are a few things to consider. You should use SSL to protect against eavesdropping. Otherwise any peer (someone else at the same insecure WiFi spot as your user) may be able to record your hashed password and replay requests with it. Also, the strength of the hashing algorithm is important. With a known username and a weak password it may be possible to generate a table of potential hashes and test that against the known hash (if the attacker manages to compromise the hash).
Regarding the difference between this and cookies, it's pretty simple. Cookies are usually randomly generated by the Server and aren't derived from the password at all. Other than that, cookies are pretty much a shortcut to what you're talking about. Whether you save it in your own storage within your app or to the HTTP library's built-in cookie handler, doesn't really matter. As long as you make sure to use SSL to protect against the eavesdropping/replay attack you should be pretty safe.
Also, I would recommend that you use a server generated hash so that you take the user password out of the equation completely.
In an effort to increase performance, I was thinking of trying to eliminate a plain 'session cookie', but encrypt all the information in the cookie itself.
A very simple example:
userid= 12345
time=now()
signature = hmac('SHA1',userid + ":" + time, secret);
cookie = userid + ':' + time + ':' + signature;
The time would be used for a maximum expirytime, so cookies won't live on forever.
Now for the big question: is this a bad idea?
Am I better off using AES256 instead? In my case the data is not confidential, but it must not be changed under any circumstances.
EDIT
After some good critique and comments, I'd like to add this:
The 'secret' would be unique per-user and unpredictable (random string + user id ?)
The cookie will expire automatically (this is done based on the time value + a certain amount of seconds).
If a user changes their password, (or perhaps even logs out?) the secret should change.
A last note: I'm trying come up with solutions to decrease database load. This is only one of the solutions I'm investigating, but it's kind of my favourite. The main reason is that I don't have to look into other storage mechanism better suited for this kind of data (memcache, nosql) and it makes the web application a bit more 'stateless'.
10 years later edit
JWT is now a thing.
A signed token is a good method for anything where you want to issue a token and then, when it is returned, be able to verify that you issued the token, without having to store any data on the server side. This is good for features like:
time-limited-account-login;
password-resetting;
anti-XSRF forms;
time-limited-form-submission (anti-spam).
It's not in itself a replacement for a session cookie, but if it can eliminate the need for any session storage at all that's probably a good thing, even if the performance difference isn't going to be huge.
HMAC is one reasonable way of generating a signed token. It's not going to be the fastest; you may be able to get away with a simple hash if you know about and can avoid extension attacks. I'll leave you to decide whether that's worth the risk for you.
I'm assuming that hmac() in whatever language it is you're using has been set up to use a suitable server-side secret key, without which you can't have a secure signed token. This secret must be strong and well-protected if you are to base your whole authentication system around it. If you have to change it, everyone gets logged out.
For login and password-resetting purposes you may want to add an extra factor to the token, a password generation number. You can re-use the salt of the hashed password in the database for this if you like. The idea is that when the user changes passwords it should invalidate any issued tokens (except for the cookie on the browser doing the password change, which gets replaced with a re-issued one). Otherwise, a user discovering their account has been compromised cannot lock other parties out.
I know this question is very old now but I thought it might be a good idea to update the answers with a more current response. For anyone like myself who may stumble across it.
In an effort to increase performance, I was thinking of trying to
eliminate a plain 'session cookie', but encrypt all the information in
the cookie itself.
Now for the big question: is this a bad idea?
The short answer is: No it's not a bad idea, in fact this is a really good idea and has become an industry standard.
The long answer is: It depends on your implementation. Sessions are great, they are fast, they are simple and they are easily secured. Where as a stateless system works well however, is a bit more involved to deploy and may be outside the scope of smaller projects.
Implementing an authentication system based on Tokens (cookies) is very common now and works exceedingly well for stateless systems/apis. This makes it possible to authenticate for many different applications with a single account. ie. login to {unaffiliated site} with Facebook / Google.
Implementing an oAuth system like this is a BIG subject in and of itself. So I'll leave you with some documentation oAuth2 Docs. I also recommend looking into Json Web Tokens (JWT).
extra
A last note: I'm trying come up with solutions to decrease database
load. This is only one of the solutions I'm investigating
Redis would work well for offloading database queries. Redis is an in memory simple storage system. Very fast, ~temporary storage that can help reduce DB hits.
Update: This answer pertains to the question that was actually asked, not to an imagined history where this question was really about JWT.
The most important deviations from today's signed tokens are:
The question as originally posed didn't evince any understanding of the need for a secret in token generation. Key management is vital for JWT.
The questioner stated that they could not use HTTPS, and so they lacked confidentiality for the token and binding between the token and the request. In the same way, even full-fledged JWT can't secure a plain HTTP request.
When the question was revised to explain how a secret could be incorporated, the secret chosen required server-side state, and so fell short of the statelessness provided by something like JWT.
Even today, this homebrew approach would be a bad idea. Follow a standard like JWT, where both the scheme and its implementations have been carefully scrutinized and refined.
Yes, this is a bad idea.
For starters, it's not secure. With this scheme, an attacker can generate their own cookie and impersonate any user.
Session identifiers should be chosen from a large (128-bit) space by a cryptographic random number generator.
They should be kept private, so that attackers cannot steal them and impersonate an authenticated user. Any request that performs an action that requires authorization should be tamper-proof. That is, the entire request must have some kind of integrity protection such as an HMAC so that its contents can't be altered. For web applications, these requirements lead inexorably to HTTPS.
What performance concerns do you have? I've never seen a web application where proper security created any sort of hotspot.
If the channel doesn't have privacy and integrity, you open yourself up to man-in-the-middle attacks. For example, without privacy, Alice sends her password to Bob. Eve snoops it and can log in later as Alice. Or, with partial integrity, Alice attaches her signed cookie to a purchase request and sends them to Bob. Eve intercepts the request and modifies the shipping address. Bob validates the MAC on the cookie, but can't detect that the address has been altered.
I don't have any numbers, but it seems to me that the opportunities for man-in-the-middle attacks are constantly growing. I notice restaurants using the wi-fi network they make available to customers for their credit-card processing. People at libraries and in work-places are often susceptible to sniffing if their traffic isn't over HTTPS.
You should not reinvent the wheel. The session handler that comes with your development platform far is more secure and certainly easier to implement. Cookies should always be very large random numbers that links to server side data. A cookie that contains a user id and time stamp doesn't help harden the session from attack.
This proposed session handler is more vulnerable to attack than using a Cryptographic nonce for each session. An attack scenario is as follows.
It is likely that you are using the same secret for your HMAC calculation for all sessions. Thus this secret could be brute forced by an attacker logging in with his own account. By looking at his session id he can obtain everything except for the secret. Then the attacker could brute force the secret until the hmac value can be reproduced. Using this secret he can rebuild a administrative cookie and change his user_id=1, which will probably grant him administrative access.
What makes you think this will improve performance vs. secure session IDs and retrieving the userid and time information from the server-side component of the session?
If something must be tamper-proof, don't put it in the toddlers' hands. As in, don't give it to the client at all, even with the tamper-proof locking.
Ignoring the ideological issues, this looks pretty decent. You don't have a nonce. You should add that. Just some random garbage that you store along with the userid and time, to prevent replay or prediction.
I want user's to be able to select a "remember me" box on my website so they need not log in each time they come. So, I need to store a unique ID in a cookie to identify them. Is it safe to hash their password with sha512 and a long salt in PHP and store that value in the cookie?
If the cookie was stolen, would their password be at risk?
Obviously it must be connected to their password somehow, otherwise if the cookie value was guessed or stolen, the user would not be able to stop someone else logging in.
Also, is it advisable to use a GUID at all as the unique identifier?
Thanks,
Ben
Remember, the hash of the password is effectively the same as their password. Somebody who stole the hash would have the same access to the user's account as if they had stolen their password. Therefore it is not advisable to store a hash of the user's password in a cookie unless there was some other information not stored with the cookie that is used to authenticate (i.e. 2-factor authentication).
Here is an excellent article on this very topic. Many of the answers to your question are hitting on techniques outlined in it.
There's a low risk with a good algorithm and large salt, but why take any unnecessary risk?
If you just need to identify the user, then store something that can uniquely identify the user, like a guid along with some other stored verification code (not their password, some random long string). I wouldn't use a guid alone as it would not be a safe method of authentication.
It wouldn't hurt to have some kind of "password" in the cookie along with a user id (to prevent users from changing the uid to that of another user), just don't make the "password" the same as the actual user's password.
And just because it's a hash doesn't necessarily mean it's one-way (well, by definition it does, but there are utilities to generate MD5 plaintexts and I would guess it's only a matter of time before it happens to others). I would hash some kind of secondary password.
An alternative way of doing this might be use the cookie as an encrypted storage for only indirection data. You'd need some sort of unencrypted identifier that would serve as a pointer to the key (or the required information to derive the key) in the application's database, followed by a blob encrypted by the key obtained from the identifier, which itself would contain some sort of one-time-usable identifier that authenticates the session.
Given the following assumptions:
Your database is secure (e.g., your application can access it, but your user cannot directly do so, and also assuming that the application has been proofed against SQL injection)
Your salts are strong; that is, reasonably high-entropy enough that attempting to crack the salted password is infeasible even if the password is known
Then what this would provide is a method by which one can be reasonably certain that the session is not able to be hijacked or stolen in any way. That is to say that a copied cookie is only of limited usefulness, since the user must not have used the cookie between its theft and usage by an attacker.
While this protects against replay, it also means that if someone does manage to steal the cookie at exactly the right time, and manages also to use it before the original, legitimate user does, the attacker now is in control of the session. One can limit a session to an IP address to mitigate that risk (somewhat; if both the user and the attacker are behind the same NAT, which is the most likely scenario in any home or small-to-medium business network) then this point is pretty moot, since the IP address would appear to be the same anyway. Also useful might be limiting to the current user agent (though that can break unexpectedly if the user updates their browser and the session does not expire at browser close time), or finding some method by which one can identify the computer that the user is on just well enough that there is reasonable certainty that the user hasn't moved the cookie from one system to the next. Short of using some binary plugin (Flash, or Silver/Moonlight), I'm not sure that the latter is possible.
To protect against a permanent session hijacking, require that the user to reauthenticate him- or herself periodically (e.g., limit the allowed session lifetime or require something like a token/fob/dongle) and require that the user reauthenticates him- or herself upon entering sensitive areas of the application, such as password change and potentially dangerous actions, patterns or behaviors such as the deletion of data, unusual usage patterns, bulk actions, and so forth.
It is difficult to secure applications and yet retain their ease-of-use. If done carefully, security can be implemented in a manner which is minimally intrusive and yet still effective—well, for most Internet-facing applications, anyway.
It's not advisable, but if your gonna do it, at least salt your passwords before you do so. This will stop people from using hash crackers if they do manage to get hold of your visitor's cookies.