This is better than JWT. In particular: the whole protocol is versioned, rather than serving as a metaprotocol that negotiates the underlying cryptography. If you speak "v2" of this protocol, and refuse to accept any other version, then you're getting a token that is basically just Nacl.
My only nit --- apart from the "auth enc seal sign" names, which aren't coherent --- is why do public key at all? Yes, Nacl supports it, but that doesn't mean the token format does. What's the use case for it? Who's asking for it? Specifically who? The overwhelming majority of JWT implementations I see aren't public key (except for the fact that the format is negotiated and might be tricked into being that).
Why not punt "seal" and "sign" into a "v3", when/if it's needed?
We use public-key JWTs so that the verifying servers do not have a copy of the secret key, just the authorisation server's public key. That prevents a compromise of the verifying servers from also compromising the entire system (yes, a compromised verifying server can be made to do anything it's allowed to do — but that's still less than everything).
> In particular: the whole protocol is versioned, rather than serving as a metaprotocol that negotiates the underlying cryptography.
In my view, that's one of the big takeaways from early cryptographic protocols: complex handshake negotiations just won't be secure, so just don't. The other is that crypto code should ideally not contain any parsing at all.
e: Well that and the whole debacle on the level of primitives and operation modes of course.
This is great. Having versions instead of kitchen sinks, and having those versions get rid of the footguns, is exactly what fixes the cryptographic JWT perils.
Note: you probably still just want a random key in a database. And revocation is still an issue. But if you’re absolutely sure you want to mint tokens...
Just a small 'way forward' for those wanting to have the ability to revoke a JWT after I came up with a solution on my last project: A 'Gateway' - use OpenResty to verify the JWT ID stored in a redis cache using a proxy pass. When the Authentication service grants a JWT add its ID to this cache along with a way of identifying the user. That way the entire advantage/disadvantage of decentralised authentication is not fully weakened and OpenResty + Redis can be relatively fast.
> you probably still just want a random key in a database.
In general, I think that this is the wrong approach, because that means adding a database round-trip (which in a large system is almost certainly a network round-trip) for each and every API call. Notably, if the entire system is secured in depth (which large systems should be), it means adding a network round-trip for every layer of the API (e.g. one for the frontend server to validate the user, then another for a backend server to validate the frontend server). Using a stateful token[0] replaces that network round trip with a public-key or hash verification, which is much faster.
> revocation is still an issue
I think that generally revocation concerns are a bit overblown. Even with a database lookup on every request, there is a (small) amount of time that one is willing to act on out-of-date information (after all, the user's access could be disabled immediately after the lookup, before the action is performed). Almost every business has some window in which it is willing to act on old information: better to make it explicit than to leave it implicit.
I really like the long-lived stateless token-refresh token[1] and relatively short-lived stateful access token approach often seen in OAuth2 setups. E.g. an email provider might only bother refreshing a read-email access token every five minutes or so, but might wish to refresh a delete-email or send-email token more frequently (or require a database lookup each time).
[0] When I write 'stateful token,' I mean a token which is full of state; confusingly, some folks calls this a 'stateless token,' because the relying systems do not need to store or consult state.
[1] When I write 'stateless token,' I mean a token which carries no state and thus must be looked up in some form of database — a random key in a database would suffice.
The problem is that it's not idiot-proof. If someone doesn't understand why the algorithm is important, they might choose none. It's easy to say "they shouldn't be using JWTs if they don't know how to use them", but everyone starts somewhere, and everyone puts stupid bugs into production.
JWT is safe, as long as it's setup correctly, but safe-by-default is a better option.
That being said, I'm not going to swap out my JWTs with PASTs. I know what algorithm I'm using, why I'm using it, it is safe, and I'm verifying them properly.
I am totally with you. For me JWT is the idea to store encrypted values client-side which you can use to authenticate the client and is different to the old way of just storing a random 'dumb' session id with the linked values somewhere on the server.
So yes, I appreciate secure and easy-to-use implementations of that idea, but always going for how insecure JWTs are, just because there are easy ways to do it wrong, is like telling everybody that password logins are bad because some people implemented them in flash.
> Why do we need a whole new implementation?
I think the problem which many people have with JWT is that is not strict enough and does not define certain things. PAST uses the same idea, but does not allow that wide variety of different algorithms we could use with JWT. In my eyes it makes it easier to use, because you do not have to search for implementations with compatible and secure algorithms but just for implementations using the latest version of PAST.
> If my server-side application sends a JWT with a "good" algorithm, and disallows any other alg's, wouldn't that prevent attacks?
It does not. Unless you have specifically hardened your server to refuse to even try verifying tokens which use "bad" algorithms, a client can still present a key signed with one of those algorithms, and attempting to verify it may pose a risk.
I think one of the issues is a very practical one. When everything is called JWT, it's hard for users to figure out whether it's a secure or insecure implementation.
It's completely possible to do secure things with JWT, especially if you control every producer and consumer, but it's not guaranteed.
There's also the issue that the alg field is in an encoded section of the JWT payload and has to be base64-decoded and then JSON parsed. There have been buffer overrun and malicious JSON attacks on JWT.
PAST at least moves that to a clear text prefix in the vx.scheme pattern. Theoretically, that doesn't even stop it from having a v0.none or some other dumb algorithm such as JWT allows in a bad version suite, but it does at least mitigate against decryption attacks.
I have implemented JWT on my app but the library I used (Guardian, written in Elixir), only allows you to use HMAC-SHA512 by default. And I've left it that way.
Should I still be worried? I get that JWT's algorithm flexibility is overall a bad thing, but if I only allow one, should I continue to worry?
I haven't reviewed said library, so I'm taking your word for it that it's actually limited to that suite :-) The problems with JWT are more complicated than just negotiation, but you should be OK here.
Here's why:
- Some bugs are about negotiation, e.g. key material misuse between RSA and HMAC schemes. They don't affect you, because you don't negotiate.
- Some bugs are about cryptographic implementation, such as not reusing nonces for ECDSA. They don't affect you, because _HMAC-SHA256_ doesn't have most of these problems.
- Some bugs are about specification issues, such as non-mandatory aud (audience) and exp (expiry). Audience shouldn't be a problem for you, because the only audience is you and there's only one secret key, so you get automatic audience restriction via cryptographic binding. Expiry, well, that's on you.
Why did you use JWT to begin with? (What does minting tokens buy you?)
I do wish a different acronym had been chosen. When I search for "[language of choice] JWT" pretty much all results are relevant. But even if this new token schema takes off it will forever be a hassle to find relevant results for "[language of choice] PAST".
99.9% of that time was spent trying to come up with a better name/acronym, without success. I decided to just give it a plain/obvious name until a better one surfaced.
This doesn't solve the criticism against JWT being used for sessions, which is one of the main point against JWT expressed in the very site linked at the top of the README.
There's nothing I can do at this layer that will stop people from using JWT/PAST/etc. as an attempt to build stateless session management systems for some ill-conceived "horizontal scalability" requirements, except maybe continue to tell people this is a bad idea and don't do that.
The rest of the points (i.e. the problems with the JOSE standards) are what PAST seeks to solve. The "do not misuse" problem is more complicated, and if I were to add e.g. "do not use this for stateless sessions" at the top in big red letters, that will only tell developers "this is unsafe, keep using JWT instead".
I don't get it. We used to use something like a pipe-delimited string, then JWT, now PAST. Isn't the encryption doing all the work regardless of how the data is structured?
Getting the encryption right is pretty tricky! How do you verify the encryption method for a message, for example? That's a real problem in JWT: that's how RSA privkeys leak. PAST solves this by not negotiating. How do you make sure nobody's doing nonce reuse in ECDSA? That's a real problem in JWT. PAST solves this by only having (v2) specify exactly how to do that.
Just because this specifies a format, doens't mean it's just a format :)
A common security issue I've seen with uses of JWT doesn't have to do with JWT itself but how it's used by front-end developers. It's commonly stored in localStorage instead of an HttpOnly cookie, which creates a cross-site scripting vulnerability.
Shockingly, the advice I've seen to protect against this by folks like Auth0 is "keep your tokens expiration low" or not mention it at all.
I don't imagine PAST gets around this, as it's more like misinformation around the storage mechanism, but I think it's worth mentioning in any "how to use" section about PAST or JWT.
[+] [-] tptacek|8 years ago|reply
My only nit --- apart from the "auth enc seal sign" names, which aren't coherent --- is why do public key at all? Yes, Nacl supports it, but that doesn't mean the token format does. What's the use case for it? Who's asking for it? Specifically who? The overwhelming majority of JWT implementations I see aren't public key (except for the fact that the format is negotiated and might be tricked into being that).
Why not punt "seal" and "sign" into a "v3", when/if it's needed?
[+] [-] eadmund|8 years ago|reply
[+] [-] CiPHPerCoder|8 years ago|reply
> Why not punt "seal" and "sign" into a "v3", when/if it's needed?
Would you be happier seeing something like this?
> What's the use case for it? Who's asking for it? Specifically who?The only use-case I'm aware of as of this morning is OAuth2 users who currently use JWT for access tokens. https://bshaffer.github.io/oauth2-server-php-docs/overview/j...
[+] [-] blattimwind|8 years ago|reply
In my view, that's one of the big takeaways from early cryptographic protocols: complex handshake negotiations just won't be secure, so just don't. The other is that crypto code should ideally not contain any parsing at all.
e: Well that and the whole debacle on the level of primitives and operation modes of course.
[+] [-] cmsd2|8 years ago|reply
[+] [-] lvh|8 years ago|reply
Note: you probably still just want a random key in a database. And revocation is still an issue. But if you’re absolutely sure you want to mint tokens...
[+] [-] saganus|8 years ago|reply
I've seen this referenced a few times before but I don't understand why minting tokens is bad.
Also, isn't creating a random key as a token the same as minting one? or what is the correct context here for "minting tokens"?
Thanks!
[+] [-] allyant|8 years ago|reply
[+] [-] pinguinFromY|8 years ago|reply
[+] [-] zeveb|8 years ago|reply
In general, I think that this is the wrong approach, because that means adding a database round-trip (which in a large system is almost certainly a network round-trip) for each and every API call. Notably, if the entire system is secured in depth (which large systems should be), it means adding a network round-trip for every layer of the API (e.g. one for the frontend server to validate the user, then another for a backend server to validate the frontend server). Using a stateful token[0] replaces that network round trip with a public-key or hash verification, which is much faster.
> revocation is still an issue
I think that generally revocation concerns are a bit overblown. Even with a database lookup on every request, there is a (small) amount of time that one is willing to act on out-of-date information (after all, the user's access could be disabled immediately after the lookup, before the action is performed). Almost every business has some window in which it is willing to act on old information: better to make it explicit than to leave it implicit.
I really like the long-lived stateless token-refresh token[1] and relatively short-lived stateful access token approach often seen in OAuth2 setups. E.g. an email provider might only bother refreshing a read-email access token every five minutes or so, but might wish to refresh a delete-email or send-email token more frequently (or require a database lookup each time).
[0] When I write 'stateful token,' I mean a token which is full of state; confusingly, some folks calls this a 'stateless token,' because the relying systems do not need to store or consult state.
[1] When I write 'stateless token,' I mean a token which carries no state and thus must be looked up in some form of database — a random key in a database would suffice.
[+] [-] lxe|8 years ago|reply
If my server-side application sends a JWT with a "good" algorithm, and disallows any other alg's, wouldn't that prevent attacks?
Why do we need a whole new implementation?
[+] [-] level|8 years ago|reply
JWT is safe, as long as it's setup correctly, but safe-by-default is a better option.
That being said, I'm not going to swap out my JWTs with PASTs. I know what algorithm I'm using, why I'm using it, it is safe, and I'm verifying them properly.
[+] [-] JepZ|8 years ago|reply
So yes, I appreciate secure and easy-to-use implementations of that idea, but always going for how insecure JWTs are, just because there are easy ways to do it wrong, is like telling everybody that password logins are bad because some people implemented them in flash.
> Why do we need a whole new implementation?
I think the problem which many people have with JWT is that is not strict enough and does not define certain things. PAST uses the same idea, but does not allow that wide variety of different algorithms we could use with JWT. In my eyes it makes it easier to use, because you do not have to search for implementations with compatible and secure algorithms but just for implementations using the latest version of PAST.
[+] [-] duskwuff|8 years ago|reply
It does not. Unless you have specifically hardened your server to refuse to even try verifying tokens which use "bad" algorithms, a client can still present a key signed with one of those algorithms, and attempting to verify it may pose a risk.
[+] [-] treve|8 years ago|reply
It's completely possible to do secure things with JWT, especially if you control every producer and consumer, but it's not guaranteed.
[+] [-] WorldMaker|8 years ago|reply
PAST at least moves that to a clear text prefix in the vx.scheme pattern. Theoretically, that doesn't even stop it from having a v0.none or some other dumb algorithm such as JWT allows in a bad version suite, but it does at least mitigate against decryption attacks.
[+] [-] atonse|8 years ago|reply
Should I still be worried? I get that JWT's algorithm flexibility is overall a bad thing, but if I only allow one, should I continue to worry?
[+] [-] lvh|8 years ago|reply
Here's why:
- Some bugs are about negotiation, e.g. key material misuse between RSA and HMAC schemes. They don't affect you, because you don't negotiate.
- Some bugs are about cryptographic implementation, such as not reusing nonces for ECDSA. They don't affect you, because _HMAC-SHA256_ doesn't have most of these problems.
- Some bugs are about specification issues, such as non-mandatory aud (audience) and exp (expiry). Audience shouldn't be a problem for you, because the only audience is you and there's only one secret key, so you get automatic audience restriction via cryptographic binding. Expiry, well, that's on you.
Why did you use JWT to begin with? (What does minting tokens buy you?)
[+] [-] idbehold|8 years ago|reply
[+] [-] CiPHPerCoder|8 years ago|reply
(The list is here: https://github.com/paragonie-scott/public-projects/issues/6)
99.9% of that time was spent trying to come up with a better name/acronym, without success. I decided to just give it a plain/obvious name until a better one surfaced.
[+] [-] Daycrawler|8 years ago|reply
[+] [-] CiPHPerCoder|8 years ago|reply
The rest of the points (i.e. the problems with the JOSE standards) are what PAST seeks to solve. The "do not misuse" problem is more complicated, and if I were to add e.g. "do not use this for stateless sessions" at the top in big red letters, that will only tell developers "this is unsafe, keep using JWT instead".
[+] [-] Lazare|8 years ago|reply
[+] [-] waibelp|8 years ago|reply
[+] [-] tootie|8 years ago|reply
[+] [-] lvh|8 years ago|reply
Just because this specifies a format, doens't mean it's just a format :)
[+] [-] timwis|8 years ago|reply
More details here: http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-fo...
Shockingly, the advice I've seen to protect against this by folks like Auth0 is "keep your tokens expiration low" or not mention it at all.
I don't imagine PAST gets around this, as it's more like misinformation around the storage mechanism, but I think it's worth mentioning in any "how to use" section about PAST or JWT.
[+] [-] CiPHPerCoder|8 years ago|reply
So I've opened an issue to address it before v1.0.0 is tagged: https://github.com/paragonie/past/issues/14
[+] [-] lvh|8 years ago|reply
[+] [-] JimDabell|8 years ago|reply
[+] [-] k__|8 years ago|reply
How does PAST solve this? Is it even possible to get secure stateless auth?
[+] [-] MrCalifornian|8 years ago|reply
[+] [-] unknown|8 years ago|reply
[deleted]
[+] [-] partycoder|8 years ago|reply
[+] [-] cheez|8 years ago|reply