Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Json Web Token - JWT #155

Closed
Defman opened this issue Feb 20, 2018 · 6 comments
Closed

Json Web Token - JWT #155

Defman opened this issue Feb 20, 2018 · 6 comments

Comments

@Defman
Copy link

Defman commented Feb 20, 2018

JWT

Json Web Token's is a way of Authenticating. A token is on the form of header.claims.signature where the header and claims are 64base encoded json objects and the signature 64base encoded bytearray. This token is attached in the html header as Authorization: token header.claims.signature.

I suggest using RSA as the signature and claims to be the user information. The claims may also include a scope of which the token can be used to authenticate. For instance a token might be issued only to allow access to ORE.

Expiration

The token also contains a claim that tells the client for instance ore that this token is valid until a certain date and time; this is often 5 or 15 min. The token then contains a refresh token within the claims that can be used to generate a new token. This refresh token is kept in a db on the auth server and can be invalidated to turn of access for this token.

Implementation

The auth server implementation should have an endpoint that allow tokens to be issued in an exchange of credentials. This token is then returned to the user and can be used to verify their identity.

Public Key endpoint should return the publickey of the Private Key that is used to sign the tokens. This will allow client implementation to retriever a public key that can be used to verify the JWT tokens is signed by the auth server.

Refresh endpoint to exchange a token that can be invalidated to a JWT token that can be used to authenticate.

Furthermorethe a client implementation should always check that the header is an exact match or just ignore it completely.

Ore

This will be used with the new ORE API V2 and the vue frontend for ORE.

@lol768
Copy link

lol768 commented Mar 6, 2018

Why? What advantages does this give? What's wrong with using an opaque token which is looked up in the database and has associated scopes?

JWTs are too easy to do wrong. I've seen too many applications supporting the NONE algorithm. The RSA is easy to fuck up with a padding oracle vulnerability if you're doing encryption. Too many library implementations don't check MACs properly.

Why do we want to send and have the claims persisted by the client?

The token also contains a claim that tells the client for instance ore that this token is valid until a certain date and time; this is often 5 or 15 min. The token then contains a refresh token within the claims that can be used to generate a new token. This refresh token is kept in a db on the auth server and can be invalidated to turn of access for this token.

In other words, you can no longer immediately invalidate tokens - because there's a window of validity where there's nothing you can do with the token because it's perfectly valid.

And we're still using a database for the refresh tokens!

Really, what is the advantage of all this complexity? You're still using a database for the refresh token storage. Why can't you expose a simple API to consuming services in order to check these tokens?

@Defman
Copy link
Author

Defman commented Mar 7, 2018

JWTs are too easy to do wrong. I've seen too many applications supporting the NONE algorithm. The RSA is easy to fuck up with a padding oracle vulnerability if you're doing encryption. Too many library implementations don't check MACs properly.

I have made a small snippet to answer the part about the None algorithm or more exact an equivalent implementation

python

if authenticate(token) or true:

Furthermore I dont know if you skipped this part or more likely I wrote it badly

Furthermorethe a client implementation should always check that the header is an exact match or just ignore it completely.

This translates to; we are only going to support one algorithm, in short we don't necessarily care about the header and the specified algorithm.

To answer the second part about RSA is to damn hard. I dont see my self or anyone implementing RSA it's too great of security risk. I would use the RSA/ECB/OAEPWithSHA-256AndMGF1Padding this is on the form algorithm/mode/padding. This algorithm exists both in java and pyCrypto

In other words, you can no longer immediately invalidate tokens - because there's a window of validity where there's nothing you can do with the token because it's perfectly valid.

And why is this a problem, you can invalidate the refresh token and yes it can take up to 5-15min depending on what we decide the lifetime of a token is. I do not see a problem nor does security obsessed Google.

Really, what is the advantage of all this complexity? You're still using a database for the refresh token storage. Why can't you expose a simple API to consuming services in order to check these tokens?

Well I'm glad you asked. Fist of all it will reduce the load on the authentication server by only needing to do a round trip every 5min or 15min to refresh the token. By using this system we would also make authentication stateless and services is no longer require to lookup the API token in a DB. This is most useful if you have services running on different servers and most notably regions.

Theres a great post on Medium about pros and cons in using JWT

JWT is not hard to do right and to be honest you should always create your own implementation that suits your needs. This implementation might just ignore any header and only check if the signature against one algorithm.

@lol768
Copy link

lol768 commented Mar 7, 2018

I have made a small snippet to answer the part about the None algorithm or more exact an equivalent implementation

If I'm following your point here correctly, you're using this to illustrate the lack of security regarding implementations which support this alg. I don't disagree, but this mistake has been made many many times!

Furthermorethe a client implementation should always check that the header is an exact match or just ignore it completely.

I wasn't sure what you meant by header. But you're right, applications should hardcode a particular algorithm and refuse to accept anything else. I still see people do this wrong regularly though.

I dont see my self or anyone implementing RSA it's too great of security risk.

Agreed, implementing your own crypto is never a good idea.

I would use the RSA/ECB/OAEPWithSHA-256AndMGF1Padding

Can you explain your reasoning behind this choice?

This algorithm exists both in java and pyCrypto

Library support is always good.

And why is this a problem, you can invalidate the refresh token and yes it can take up to 5-15min depending on what we decide the lifetime of a token is. I do not see a problem nor does security obsessed Google.

Regardless as to whether you see a problem or not, it's still a fact of using this sort of approach and should be acknowledged.

Well I'm glad you asked. Fist of all it will reduce the load on the authentication server by only needing to do a round trip every 5min or 15min to refresh the token.

And shift load onto the application servers which now need to verify signatures. Have you profiled the load generated by a database-lookup approach? Is this currently a bottlekneck?

By using this system we would also make authentication stateless and services is no longer require to lookup the API token in a DB.

I don't disagree (with the exception of the refresh logic).

This is most useful if you have services running on different servers and most notably regions.

Is the latency associated with auth checking large enough that this is unequivocally a bottlekneck? Have you got metrics for this? I keep asking this, because otherwise it sounds like premature optimisation.

to be honest you should always create your own implementation that suits your needs

Are you advocating rolling your own library for a security critical application?


Some more questions:

  • Why should claims be visible to the client? Is encryption the answer if claims shouldn't be visible?

@Defman
Copy link
Author

Defman commented Mar 9, 2018

I would like to start by saying that I think it's good to outline all possible issues, even if they non existence. For instance, how do we prevent man in the middle attack on the public key at the end point. This could be a problem, but since we would be using https, and all clients should only try to access the public key by https.

to be honest you should always create your own implementation that suits your needs: Are you advocating rolling your own library for a security critical application?

Yes and no, im not advocating you to write any sensitive security algorithms like RSA! But JWT is quite simple in it's nature. I'm arguing that you dont necessarily need to implement all features of JWT but only the features your application is going to need. If you have absolutely no idea how any of this works, you should absolutely not try.

Is the latency associated with auth checking large enough that this is unequivocally a bottlekneck? Have you got metrics for this? I keep asking this, because otherwise it sounds like premature optimisation.

Currently theres no evidence for any kind of bottleneck. This will not resolve any performance issues at the moment, but will make a short authentication server outage less potent.

Shift load onto the application servers which now need to verify signatures. Have you profiled the load generated by a database-lookup approach? Is this currently a bottlekneck?

Theres two big public private key cryptographic algorithms, DSA and RSA. DSA is quick for signature generation but slow for validation. Where as RSA is slow at signing but quick at validating the signature. You can run a benchmark with opensll speed rsa

sign verify sign/s verify/s
rsa 512 bits 0.000830s 0.000047s 1205.3 21359.0
rsa 1024 bits 0.005028s 0.000217s 198.9 4602.9 
rsa 2048 bits 0.035149s 0.001015s 28.5 984.8
rsa 4096 bits 0.242308s 0.003620s 4.1 276.3

The above result is from a MacBook Pro (Retina, 13-inch, Early 2015) 2,7 GHz Intel Core i5 and it takes a single ms to verify and bit longer to sign a 2048 bit key, I dont even see redis beating this.

I would use the RSA/ECB/OAEPWithSHA-256AndMGF1Padding. Can you explain your reasoning behind this choice?

Yes I can OAEP is not vulnerable to padding oracle attack, and sha-256 is just a preference over sha-1 it does not have any real security benefits.

Why should claims be visible to the client? Is encryption the answer if claims shouldn't be visible?

If we want to store data stateless for the user the best option is to save it on the client. If we want to hide some data an option could be to encrypt it. JWT does not necessarily replace all database lookups, but only the once related to validate the user and the users id. This user id can then be used to lookup service specific data.


Edit: Sensitive data should never be stored in the claims, even if encrypted. This is duo to the fact that any encryption might be broken.

@Defman
Copy link
Author

Defman commented Mar 9, 2018

Quick comment, OAEP is only secure for encrypting not signing, we should use PKCS#1 v1.5. That said, PKCS#1 v1.5 padding for signature generation has not been broken (unlike PKCS#1 v1.5 padding for encryption, which does have vulnerabilities)

@paragonie-scott
Copy link

paragonie-scott commented May 16, 2018

Correction: You should be using PSS not PKCS#1 v1.5.

Furthermore, you may want to consider not doing RSA at all. If you must, only do so very carefully.

@felixoi felixoi closed this as completed Mar 2, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants