I was wondering if you had a JWT and did an offline brute force attack on it to get the servers secret key, if you could then sign valid JWTs?
The only reason I think this wouldn’t work is that once the server signs a JWT, it stores it somewhere locally. So even if the server verifies the signature, it won’t authenticate you as it has no local record of that token.
Is this true that the token is stored locally as well as in the client side?
Some token providers do rotate (change) their private signing key on a regular basis. Second, access tokens usually have a short lifetime, like minutes, to days. So it would be pretty hard to crack in that timeframe.
You as a user only get access to the signature and the public key, with just the public key, is practically impossible to use brute-force to get the private key.
Also, for JWT's there are different signing algorithms (RSA, ECDSA..) and the ECDSA is stronger than RSA.
Related
I am writing an android app that will communicate with a webserver.
So far, i got the communication basics and a token based authentication system.
The tokens are really long, but i am still afraid, someone could just guess them.
So i thought about something like the following. Is this a valid approach?
My Idea:
App generates RSA keypair.
Login with mail and password over ssl (provide public key to server)
Server generates unique token
Server saves token and public key
Server returns token to app
From now on, the app uses the token, but encrypts all messages with the private key.
Server receives message with token. It tries to decrypt the data with the public key, to make sure, this message is from the original login. If it couldn't decrypt, it declines the request.
Is this a valid approach? Or is this method too weak?
Well, what you want to do is a possible approach. Here some of my thoughts regarding your idea.
The chain is as strong as the weakest link. Have you thought about how you will reset the access when the user looses the device? Are credentials enough? Is access to the mailbox enough? The attacker will choose the easiest target.
Your approach looks suspiciously similar to mutual authentication with TLS. Well, you can reinvent the wheel if you like, but I would probably stick to the standard. You don't want to solve the problem of e.g. reply attacks prevention, do you?
Why not simply use 2FA with TLS channel protection? U2F for example is even stronger than having private client TLS keys lying around.
Encryption of long messages with RSA keys is slow. You usually encrypt only a symmetric key with RSA and the rest of the message with AES.
Hope this helps somehow.
I am looking at the examples of JWT tokens in Node.js, and the verify function. My question is, where does this publicKey come from in verify(token, publicKey)? What is the flow?
The client (one of my users) has a client library installed on their computer/server, for making requests to my app myapp.com. In the myapp.com server, I call verify(token, publicKey). In the client library I generate the token using privateKey. The question is, how does the client get this private key generally? (i.e. is heroku login downloading a private key under the hood for making JWT requests, sort of thing?). And how do you fetch the public key? My understanding is, the client would download a private key, and our server would store the public key. Then given you have the public key and token, just call verify(token, publicKey). But how do you get the public key for the token on the server generally? Is the server storing one public key per private key disseminated to the client installed libraries?
The way I've usually seen JWTs used, there is only a very small number of trusted issuers, often only one, and the tokens are used as bearer tokens. In many cases, the issuer and the verifier are the same. In other cases, the verifier trusts one identity provider (e.g. Google) and fetches the public key from a https URL (example).
In your case, you could act as both the issuer and verifier:
You (the server) would generate one key pair.
Your API servers would trust JWTs signed by this key (and they'd only have the public key, since they only need to verify them).
An authentication/management server would have the private key, would authenticate your user, and issue them a JWT.
The client would never handle any keys, they'd simply store the signed JWT, and pass it as a bearer token when making a client request.
This is e.g. the approach described here as the approach used by GitHub. In this case, the issuer and verifier both belong to you. This approach is the easiest for both you (you can trust the content of the JWTs once you've verified the signature), and the client (they're just dealing with an opaque API key and don't need to deal with the complexities of JWTs at all).
A possible alternative approach could be:
A key pair is generated and the public key is associated with the account. This can be done in multiple ways (see below), but the end result is the same: The client has a private key, and your server knows the corresponding public key and which user it is associated with
When making a request, the client creates a JWT, signs it with its private key, and includes their user name in the token (e.g. in the iss and or sub field).
Your server takes the token, extracts the user name, looks up the public key associated with the account in the database, and validates the token.
This approach is used e.g. by Google Cloud for service account authentication.
Step 1 above can be done in two ways:
You authenticate the user, generate a key pair, associate the public key with the account, and let the user download their private key (via https of course). While it's generally considered somewhat bad form to generate keys for someone else (because you get to see a key that you don't need to know and you have to send it over the network), it's a lot easier, and Google is doing just this.
The user generates and stores the key pair. You authenticate the user, the user uploads the public key, you associate it with the account.
Either way, if you go with the "user signs a JWT" approach, you likely will want to provide client libraries, or at least code examples. Note also Google's requirement that the tokens must be short-lived, enforced by treating long-lived tokens as invalid. Without this rule and enforcement, what will happen is that many client developers will be annoyed about your complicated solution, manually sign a token that is valid forever on his laptop, and then use it as a bearer token.
heroku login actually doesn't use JWTs at all. It retrieves and stores an OAuth Bearer Token. This is most comparable to the first approach (client never handles any private keys, just gets an opaque blob, which happens to be a JWT that you can verify). The difference between a non-JWT token and a long-lived JWT is that your API servers have to look up the meaning and validity of the regular token in a database, whereas the JWT directly tells you the user identity, and possibly permissions and other attributes that you included when issuing it.
I want to use JWT in my application.
Now I'm wondering if it is secure to use the users password in combination with a private secret as a key to sign my tokens. This way, tokens get invalidated if a user changes his/her password.
But maybe it makes my private secret vulnerable?
Thanks for your thoughts on that!
The secret is a preshared string exchanged between the client and the server.
So in your case:
SecretString= PresharedSecret + ClientPassword
So, everytime the client passes a JWT token, you would need to retrieve the password from the database or have some way of preloading it and a check in case of password changes for validating the token.
This might lead to the following scenarios:
Everytime the client forgets his password, you might need to make database calls that can be expensive
It would enhance security in one way, as anyone who changes the password will not be able to communicate with the server with knowledge of the previous SecretString.
A new preshared secret will need to be decided.. and validated with the new registered password.
Overall, it does increase security. However, it depends on the purpose or the usage of the infrastructure. If it is a system in which users frequently forget passwords.. this might not be a great option.
The usual thing is to sign all the tokens with the same key. Simplifies the management and avoids querying the database in each request.
Signing with a key+user password is feasible and has the advantage of allowing to revoke tokens (with the commented drawbacks).
Ensure that your signing key is enough secure deriving it from user's password and has the recommended length of the selected signature algorithm. Do not store or use user's password directly.
I have a nodejs api with an angular frontend. The API is successfully using JWT with passport to secure it's endpoints.
I am now conscious that after the tokens have expired, my front end will still allow the user to request my api endpoints without prompting them to reenter their log in details to get a fresh token.
This is how my backend generates the token:
function generateToken(user) {
return jwt.sign(user, secret, {
expiresIn: 10080 // in seconds
});
}
So to implement this logic I think I need to verify the JWT token client-side. Q1, is this a sensible approach.
Q2, the JWT library I am using seems to require a public key to use it's verify() function. I don't seem to have a public key, only a secret, which I just made up, so it wasn't generated with a pair. Where does my public key come from, or is there another way of verifying my token without this?
This all seems like it should be obvious and that I have missed something, so apologies if this is a stupid question, but I can't seem to find the answer?
TL;DR
You must verify the signature of JWS in the server always.
Client-side signature verification doesn't gives much, unless you have a specific case where it makes sense don't do it.
You don't need to verify the signature of a JWS token to check expiration in the client. (unless you were encrypting the claims, aka using JWE, in that case you need to do something similar because you need a key to decrypt the claims).
You don't need to verify the signature of a JWS to check expiration in the server neither, but you should because this gives you the certainty that nobody has altered the expiration (otherwise the verification will fail because if the claims change then the recalculated signature will differ)
To read non encrypted claims you just only need to decode them. You could use jwt-decode in the client.
I am now conscious that after the tokens have expired, my front end will still allow the user to request my api endpoints [...]
So to implement this logic I think I need to verify the JWT token client-side
If I understood you correctly you are talking about checking if a JWS has expired in the client side.
In order to do this you don't need to verify the token signature (although the library you are using seems to be doing both things at the same time for you, but also lets you to disable expiration control with ignoreExpiration flag). (Unless you're encrypting the claims, aka using JWE)
The RFC 7515 (JWS) says nothing about expiration. Message Signature or MAC Validation doesn't control expiration (and it shouldn't because signatures gives you authenticity and integrity).
Even the RFC 7519 (JWT) doesn't control the expiration claim for resolve if a JWT is valid or not.
Also, all the claims are optional.
So, you could check if a JWT has expired or not without verifying the signature, hence you don't need neither a public key (for asymmetric encryption like RSA) or a secret key (for symmetric encryption like AES).
In JWT and JWS tokens, the claims are just plaintext base64 encoded so you could just decode the payload without verifying if the signature is valid and read the expiration claim.
If you are encrypting the payload (aka using JWE) then you will not be able to do this.
A note from jjwt library
JWTs can be cryptographically signed (making it a JWS) or encrypted (making it a JWE).
Here is a ligthweigth library from auth0 to decode the base64encoded claims of a JWT/JWS token.
A guy is even asking about checking expiration.
I don't know why you think that you should be doing this control client-side, the only advantage is avoiding sending API request that the client knows that will fail. And they should fail because the server should be validating that the token hasn't expired, previous signature verification (with secret/private key) obviously.
The RFC 7519 says about this claim:
The "exp" (expiration time) claim identifies the expiration time on
or after which the JWT MUST NOT be accepted for processing.
In a web app like the one you say the use of tokens is to allow stateless servers to authenticate client requests.
The goal of the OPTIONAL expiration claim is to allow the server have some control over the generated JWS (if we are using JWT for authentication signing them is a must so we should be talking about JWS).
Without expiration, the tokens will be valid forever or until the key used to signing them change (this will make the verification process to fail).
By the way, invalidating sessions is one of the most notorious disadvantages of using stateless authentication.
Session invalidation becomes a real problem if we are including information in the JWS payload (aka claims) used for authorization, for example which roles the user have.
From Stop using JWT for sessions
but more seriously, it can also mean somebody has a token with a role of admin, even though you've just revoked their admin role. Because you can't invalidate tokens either, there's no way for you to remove their administrator access
The expiration control doesn't solve this problem and I think is more oriented to avoid session hijacking or CSRF attacks.
An attacker using CSRF will be able to make a request with an expired JWS to your API skipping the expiration control.
A different issue is verifying the signature in the client using the public or secret key.
Regarding your question
I am using seems to require a public key to use it's verify() function. I don't seem to have a public key, only a secret, which I just made up, so it wasn't generated with a pair.
The verify method you pointed out says explicitlly that it accepts a public or secret key.
jwt.verify(token, secretOrPublicKey, [options, callback])
secretOrPublicKey is a string or buffer containing either the secret for HMAC algorithms, or the PEM encoded public key for RSA and ECDSA
I assume you are using neither and you are using a string like 'shhhh'.
var token = jwt.sign({ data: '¿Donde esta Santiago?'}, 'shhhh');
Then you should do
var decoded = jwt.verify(token, 'shhhhh');
However, the question here is: Is client-side signature verification really needed?
I think is not, at least not for this kind of application where the client just uses the JWS to send subsequent request to the server saying: "Hey server, I'm Gabriel and I have a paper (token) here that assures that and that paper is signed by you."
So if the client doesn't validate the JWS and a MITM had successfully gave to that client a JWS signed by himself (instead to the JWS signed by the server), then the subsequent request will simply fail.
Like expiration control, signature verification only prevent the client to make request that will fail.
Now, client side verification requires sending the public or secret key.
Sending public key doesn't represent a security concern but it's extra effort and processing with little benefits.
Sending secret keys (like 'shhhh') can represent a security issue because is the same key that is used to sign tokens.
I'll just put it here for those who got here looking for jwt verification on the browser with a public key.
Library:
https://kjur.github.io/jsrsasign/
Example:
https://kjur.github.io/jsrsasign/tool/tool_jwtveri.html
Code example:
https://github.com/kjur/jsrsasign/blob/master/tool/tool_jwtveri.html
API:
https://kjur.github.io/jsrsasign/api/symbols/KJUR.jws.JWS.html#.verifyJWT
P.S. Don't use your secret key for browser jwt verification! Public key only!
I think verifying JWT token at client-side is not a good idea.
IMO;
Whenever a user logs in, generate access and refresh token and return to user something like this;
{
"accessToken": <<accessToken>>
"refreshToken": <<refreshToken>>
"expiresAt": <<expiresAt>>
}
So client can understand when access token expire and can refresh it with refresh token.
Encrypt the data that you put in the access token because there is a chance to access the data without secret key. But of course someone needs to secret key to verify.
Q1: Token verification on client is a bad idea. What you can do is to save a token together with a same expired date on client and then refresh/remove a token. But my thought that it is better to have some date checkig on server side cause exist simple rule: Don't trust the client cause it can always send malicious code.
Q2: JWT don't need any public key. It always must have private key storing on server side cause if someone known your secret key your token don't make any sense. You only can add some payload to do it more complex.
Managing when the token expires on the client side so you can avoid sending tokens which you know will be rejected is purely an optimisation to avoid an extra roundtrip with the server. Its a perfectly valid concern but it's not a security concern. Its up to you to decide if you need that level of optimisation. The server must validate the token signature and reject expired tokens as a security concern. Tokens don't need to be encrypted unless they contain sensitive data that you don't wish to be visible to an end user or an attacker that obtains a copy of the token somehow. Tokens should be transmitted over HTTPS / SSL as good security practice. Access Tokens are usually short lived. If you also use refresh tokens, never store one in a browser unless it's a secure cookie set by the server for the same origin domain and not accessible to browser scripting. In that case Refresh tokens should still be regularly rotated.
Answer 1: It is not considered to be a good approach to verify your auth token on the client side as it involves secret key while encoding/decoding it and keeping the secret key on the client side is not secure.
Creating Token
jwt.sign({
data: 'foobar'
}, 'secret', { expiresIn: 60 * 60 });
Verifying Token
jwt.verify(token, 'secret', function(err, decoded) {
console.log(decoded.foo) // bar
});
Answer 2: JWT involves secretORPublic key while encoding and decoding token. It has to be declared or kept in the config file somewhere on the server side.
Explanation:
Decoding means decoding from Base64, there's no secret key involved in that process. On the other hand, verifying a JWT would require a secret key because it would involve a cryptographic signature operation.
To sum up, decoding does not need the secret (remember decoding is just interpreting base64) and verifying/signing does require it
I'm implementing a REST web service using C# which will be hosted on Azure as a cloud service. Since it is a REST service, it is stateless and therefore no cookies or session states.
The web service can only be accessed over HTTPS (Certificate provided by StartSSL.com).
Upon a user successfully logging into the service they will get a security token. This token will provide authentication in future communications.
The token will contain a timestamp, userid and ip address of the client.
All communication will only happen over HTTPS so I'm not concerned about the token being intercepted and used in replay attacks; the token will have an expiry anyway.
Since this is a public facing service I am however concerned that someone could register with the service, login and then modifying the token that they receive to access the accounts of other users.
I'm wondering how best to secure the content of the token and also verify that it hasn't been tampered with.
I plan on doing the following to secure the token:
The client successfully logs into the service and the service does:
Generate a random value and hash it with SHA256 1000 times.
Generate a one-time session key from private key + hashed random value.
Hash the session key with SHA256 1000 times and then use it to encrypt the token
Use private key to sign the encrypted token using RSA.
Sends the encrypted token + the signature + the hashed random value to the client in an unencrypted JSON package.
When the client calls a service it sends the encrypted token and signature in an unencrypted JSON package to the service. The service will
Recreate the session key from the private key + the hashed random value
Use the private key to verify the signature
Use the hashed session key to decrypt the token
Check that the token hasn't expired
Continue with the requested operation...
I don't really know anything about encryption so I have some questions:
Is this sufficient or is it overkill?
I read that to detect tampering I should include an HMAC with the token. Since I am signing with the private key, do I still need an HMAC?
Should I be using Rijndael instead of RSA?
If Rijndael is preferred, is the generated IV required for decrypted? i.e. can i throw it away or do I need to send it will the encrypted token? e.g. Encrypted Token + HMAC + IV + hashed random value.
Since all communication happens over HTTPS the unencrypted JSON package isn't really unencrypted until it reaches the client.
Also I may want to re-implement the service in PHP later so this all needs to be doable in PHP as well.
Thanks for your help
You are really over-thinking the token. Truthfully, the best token security relies on randomness, or more accurately unpredictability. The best tokens are completely random. You are right that a concern is that a user will modify his/her token and use it to access the accounts of others. This is a common attack known as "session stealing." This attack is nearly impossible when the tokens are randomly generated and expired on the server side. Using the user's information such as IP and/or a time stamp is bad practice because it improves predictability. I did an attack in college that successfully guessed active tokens that were based on server time stamps in microseconds. The author of the application thought microseconds would change fast enough that they'd be unpredictable, but that was not the case.
You should be aware that when users are behind proxy servers, the proxy will sometimes view their SSL requests in plain text (for security reasons, many proxies will perform deep packet inspection). For this reason it is good that you expire the sessions. If you didn't your users would be vulnerable to an attack such as this, and also possible XSS and CSRF.
RSA or Rijndael should be plenty sufficient, provided a reasonable key length. Also, you should use an HMAC with the token to prevent tampering, even if you're signing it. In theory it would be redundant, since you're signing with a private key. However, HMAC is very well tested, and your implementation of the signing mechanism could be flawed. For that reason it is better to use HMAC. You'd be surprised how many "roll your own" security implementations have flaws that lead them to compromise.
You sound pretty savvy on security. Keep up the good work! We need more security conscious devs in this world.
EDIT:
It is considered safe to include timestamps/user IDs in the token as long as they are encrypted with a strong symmetric secret key (like AES, Blowfish, etc) that only the server has and as long as the token includes a tamper-proof hash with it such as HMAC, which is encrypted with the secret key along with the user ID/timestamp. The hash guarantees integrity, and the encryption guarantees confidentiality.
If you don't include the HMAC (or other hash) in the encryption, then it is possible for users to tamper with the encrypted token and have it decrypt to something valid. I did an attack on a server in which the User ID and time stamp were encrypted and used as a token without a hash. By changing one random character in the string, I was able to change my user ID from something like 58762 to 58531. While I couldn't pick the "new" user ID, I was able to access someone else's account (this was in academia, as part of a course).
An alternative to this is to use a completely random token value, and map it on the server side to the stored User ID/time stamp (which stays on the server side and is thus outside of the clients control). This takes a little more memory and processing power, but is more secure. This is a decision you'll have to make on a case by case basis.
As for reusing/deriving keys from the IV and other keys, this is usually ok, provided that the keys are only valid for a short period of time. Mathematically it is unlikely someone can break them. It is possible however. If you want to go the paranoid route (which I usually do), generate all new keys randomly.