
Show HN: PAST, a secure alternative to JWT - CiPHPerCoder
https://github.com/paragonie/past
======
tptacek
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?

~~~
eadmund
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).

~~~
tptacek
This is the logic people use when they encrypt cookies to public keys. I've
never tested a system with public key encrypted cookies that wasn't broken. As
a general rule of thumb: public key is what you use when you have no other
choice.

PAST mitigates the risk somewhat by hardcoding a public key system into its
version. But I'm still recoiling from the kind of first-principles mixing of
systems security and cryptography that happens when people _find new reasons_
to deploy public key primitives.

Note that one of the first crypto bugs in JWT stemmed from how it used curve
public key crypto.

~~~
giaour
I think you're conflating the 'seal' and 'sign' use cases. I'm not sure how
one could design a secure system where encrypted cookies could either be read
by everyone (encrypted with private key) or forged by everyone (encrypted with
public key), but using public keys to validate that an attestation was signed
by a trusted third party is both sound and commonplace.

For example, this is how identity tokens from AWS Cognito are verified. They
are RS256 JWTs signed with Cognito's private key, and any server can download
Cognito's public key to validate that the tokens were issued by Cognito and
haven't been subsequently altered (without having to make a network call to
Cognito to request validation).

~~~
davewritescode
Was just going to post this, JWT with public keys is very handy for stateless
verification of claims, which is what it seems was the primary motivator of
the spec.

As someone who had to work with XML-DSIG, JWT seems less bad :)

~~~
lvh
You don't need public-key crypto to do stateless verification of claims.
Secret-key crypto does that just fine. You need public-key crypto if you want
_other people_ to be able to verify the claims -- that's an important
distinction, because it specifies what you're buying for your much more
complicated crypto.

~~~
WorldMaker
Or stateless verification when "other people" are allowed to create claims.
There are certainly federation scenarios where PKI alone could be sufficient
(as opposed to complex handshakes such as OpenID Connect or SAML). Obviously
in accepting those claims you get to treat every claim with a giant grain of
salt, but there are auth models where that makes sense (the only claim I need
to trust is the certificate itself scenarios, for example).

That said, very few people want such a security scheme and as theoretically
simple as it is on paper, it quickly ramps up to the real world complexity of
PKI key management.

(I could see a small use for web APIs for developers and power users that
allowed, for instance, Keybase-verified claims like HN account name/FB
name/email, for very simple and easy to curl/iwr/httpie from the command line
using Keybase-managed keys.)

I'm not sure if that small window of opportunity is worth the support
complexity in PAST here, but given the model of support only specific
versions, having them as separate verbs (sign/seal) makes it easy to block
claims you don't expect. The one flipside from an API design standpoint that I
see is that leaves a need for some sort of header like X-Accept-PAST: v2.auth

~~~
lvh
I’m not sure I follow: if the other party is allowed to create claims too,
symmetric crypto seems more obvious. That’s what NS (and later KRBv5) did in
the seventies. Or is that precisely what you’re saying?

~~~
WorldMaker
In a stateless, negotiation-less flow where you have no pre-existing
relationship with the other party? Isn't that exactly the bootstrap that PKI
was built for?

~~~
lvh
Aha, I missed the stateless negotiation-less flow. But yes, that's basically
TLS as deployed in browsers :)

------
lvh
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...

~~~
allyant
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.

~~~
AndrewStephens
Forgive my ignorance, but if you are going to all the trouble of storing the
JWT id in a server side database for verification why don't you just store the
JWTs' claims as well and just hand the client an opaque random id? Your
gateway could do the lookup and supply the claims to your backend without the
client knowing anything about it. You wouldn't even have to validate the
claims, since they never leave your servers.

The best part of using JWTs is that you can validate without a central
database. If you need a central database for sessions anyway you might as well
store the claims in it.

~~~
allyant
Good solution! Openresty could create the JWT and add it to the forwarding
headers, although the user would still need a cookie to maintain session with
the proxy.

Only disadvantage I could see would be performance.

------
lxe
I really don't get the whole "the spec supports choosing of algorithm,
therefore the whole implementation is bad"

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?

~~~
duskwuff
> 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.

~~~
lxe
But my code can just ignore the alg passed by the user and use whatever one I
want to use.

~~~
lvh
If you're going to ignore compatibility anyway, you could also use good
crypto. The JWT doesn't define what "Recommended" and "Recommended+" means but
apparently RSA+PKCSv15 is Recommended+ so I guess it means "crypto known to be
broken in the 90s".

------
atonse
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?

~~~
lvh
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?)

~~~
atonse
Just to clarify, it isn't limited to that suite, but it has a default
whitelist of only allowing it. So I COULD change it, but I didn't.

"Why did you use JWT to begin with? (What does minting tokens buy you?)"

Absolutely no compelling reason apart from:

\- Never want to roll my own auth, and there were already libraries in elixir
and ember to work with JWT, so easy to cobble together

\- The HS512 stuff seemed secure enough

\- Impression that JWT is generally where things are headed (although I made
this decision 2 years ago)

\- I can embed some attributes in the token that can be read from the client
side

\- I liked the idea of authenticating without using a database query.

A lot of these things haven't borne out in the last two years. I still make
database queries during auth, and I still retrieve user metadata from an API
in my client side. So just to clarify, I'm not attached to it in some way (I
never am, to technical decisions). I mainly want to know if I should
prioritize moving away from it or not.

~~~
lvh
Sorry, I didn't mean to come across as bitey. It's just that almost everyone
I've talked to who has made this decision and lived with it for over a year
has come to the same conclusion: just use a random key and store it in a
database :)

~~~
atonse
Yeah no worries. I sort of agree with you. I don't have any strong opinions on
why I chose it apart from that it was easy to implement.

------
idbehold
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".

~~~
CiPHPerCoder
I spent two weeks (my Christmas vacation) working on rough drafts for several
problems I wanted to solve in 2018. PAST was one of items I listed.

(The list is here: [https://github.com/paragonie-scott/public-
projects/issues/6](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.

~~~
irq-1
Refer to it with a version number: PAST2, PAST3, etc..

You could also drop the use mapping: change 'sign' to 'public-auth' and
explain that it's a 'sign' operation (a.k.a. digital signatures)

~~~
audiolion
my favourite is PAST4 tee-hee

------
Daycrawler
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.

~~~
CiPHPerCoder
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".

~~~
treve
Why is this a bad idea exactly? I'm still very interested in the idea of using
stateless sessions. Is it just that it's hard to expire sessions server-side,
or is there more to it?

~~~
CiPHPerCoder
I've written about this at length here:
[https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-
ba...](https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-bad-standard-
that-everyone-should-avoid)

(It's also the first link in the README for the project this Show HN is
linking to, FWIW)

~~~
treve
Those are mostly the drawbacks of JWT, less so using stateless sessions
altogether.

I found some additional reasons from a page that was linked from that last
link here: [http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-
fo...](http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-
sessions/)

    
    
      * They take up more space  
      * You cannot invalidate individual JWT tokens
    

The other reasons seem a bit weaker.

In your opinion, are those also the reasons why you wouldn't use PAST for
stateless sessions?

~~~
CiPHPerCoder
> In your opinion, are those also the reasons why you wouldn't use PAST for
> stateless sessions?

Yep

------
Lazare
This looks promising, and it's from a very well respected security researcher.

------
waibelp
This library is a great example of clean and beautiful php code out in the
wild!

------
tootie
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?

~~~
lvh
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 :)

~~~
grogenaut
Sure but to the GP's question, the format of json vs pipe delimted strings is
not the problem, it's WHAT you put in the Json or string and what it allows
(eg configuration) that is the problem, correct?

GP: Basically PAST is just limiting your options down to things we _think_ are
secure combinations and eliminating things we _know_ are insecure. However it
does this with WHAT it puts in the JSON, not that it's JSON vs anything else.
As you said that's just serialization.

~~~
lvh
Sort-of, but not necessarily. It's a lot easier to get the format right if you
know there's an authenticator and you know what length it is and it's totally
separate from whatever is coming next -- so you still want clear, out-of-band
signaling for the real data. Once you have that format, you're right that the
exact serialization doesn't matter.

The extreme example of this is XML DSIG and XML canonicalization in general.

~~~
grogenaut
Agreed, but there's not really something inherently insecure about JSON vs
concatted strings vs binary (well readability on that one). It's all about the
JWT spec being complicated and easy to mess up.

------
timwis
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.

More details here: [http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-
fo...](http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-
sessions/)

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
This is somewhat orthogonal to the security goals I'm trying to tackle, but
still very relevant to the ecosystem that currently uses JWT.

So I've opened an issue to address it before v1.0.0 is tagged:
[https://github.com/paragonie/past/issues/14](https://github.com/paragonie/past/issues/14)

------
JimDabell
For Python developers: I've started an implementation here:
[https://github.com/JimDabell/pypast](https://github.com/JimDabell/pypast)

------
k__
I had the impression that the main problem was that JWT was marketed as
stateless and superior and then you were stuck with stolen tokens.

How does PAST solve this? Is it even possible to get secure stateless auth?

------
MrCalifornian
Why not propose changes to the RFC instead of creating a new standard?

------
partycoder
"A secure alternative". Citation needed.

------
cheez
I'm not a security expert but when I looked into JWT I was terrified at how
easy it was to screw up. Glad to see I'm not the only one.

