This is a follow-up to our 2017 blog post that made the case for avoiding JSON Web Tokens (JWT) and its related standards.
Many developers responded to our post with the same question: "What should we use instead of JWT?" Today, I'm happy to announce a viable replacement.
Introducing PASETO: Platform-Agnostic SEcurity TOkens
The JOSE standards (which JWT is a subset) give developers enough rope to hang themselves: Do you encrypt? Do you sign? Which algorithms do you support? Is
none a valid authentication algorithm? Why not let the token specify which algorithm to use to validate tokens?
As time went on and more vulnerabilities were discovered in the JOSE standards, the companies and enthusiasts that maintained the JWT libraries in various languages did work around these vulnerabilities, to their credit.
However, this is at best a brittle solution. Consider the case of RNCryptor: The PHP implementation uses authenticated encryption and resists timing attacks, but the Haskell implementation just outright skips HMAC validation.
The most effective way to solve security problems at an ecosystem level is to design less error-prone standards. Libraries that implement safer standards will not need to play catch-up with what the mainline implementations are doing.
That is why we made Paseto, which exposes JWT-like validation claims without any of the runtime protocol negotiation and cryptography protocol joinery that caused so many critical JWT security failures.
JWT's Knobs and Levers
- Are you signing or encrypting? Both? Neither?
- What algorithm is being used for signing?
- Public-key and shared-key algorithms easily confused here.
- Able to choose dangerous options like RSA with PKCS1v1.5 padding.
- What algorithm is being used for message encryption?
- What algorithm is being used for key encryption?
- As for the real-world use cases for including the encryption key with the ciphertext, encrypted or not, your guess is as good as mine. This might make more sense if key encryption algorithms only included public-key cryptography, but of course, AES-GCM is explicitly allowed by the standard.
- What version?
v1gives you RSASSA-PSS and AES-CTR+HMAC-SHA2
v2gives you Libsodium
- Is this token local or public?
local: shared-key authenticated encryption
public: public-key digital signatures
That's it. There are no levers to pull, buttons to pull, or knobs to fiddle with. You don't have to worry about the complexity required to use RSA safely. You don't have to worry if the public key for a given message is even on the curve.
Paseto is simple, obviously secure, solves 99% of the use cases for JSON Web Tokens. There is no guesswork; the cryptography aims to be boring. You can port Paseto to another language in hundreds-not-thousands of lines of code (especially if you only aim for
The Paseto documentation, as well as a reference implementation written in PHP, is available on GitHub.
The Design and Motivation for Paseto
Paseto is to JWT what Halite was to various mcrypt-based cryptography libraries in the PHP ecosystem.
That is to say, we identified a source of insecurity for the Internet and worked to replace it with something that would lead to better security.
All of our software is developed with the same underlying philosophy:
- Secure by default
- Simple and easy-to-use
- Easy to analyze and reason about (for implementors, auditors, and security researchers)
However, we need to emphasize another point that isn't obvious until you have sufficient cryptography engineering experience: Ciphersuite agility is harmful. A full discussion on why this is true deserves a blog post of its own, but in a nutshell, it introduces the risk of downgrade attacks (e.g. DROWN).
A far more robust design is to use versioned protocols—for which each version has One True Ciphersuite—with a small backward compatibility window. That is what we did with Paseto, and that's why tokens have a header that includes protocol version information.
Should Paseto need a version 3 (e.g. if a practical quantum computer is developed and RSA and ECC are both doomed), we can specify a simply-secure ciphersuite for the new version that uses post-quantum cryptography algorithms. In such a scenario,
local tokens can be decrypted and re-encrypted transparently (although quantum computers don't threaten AES-256), and
public tokens older than
v3 can simply be rejected.
No downgrade attacks. No weird configurations. No
"alg":"none" gotchas to watch out for.
The Next Steps
I tagged, signed, and released
v0.5.0 of the Paseto specification and reference implementation this morning. The specification has not changed since version 0.4, and is unlikely to change again.
In the coming weeks, I intend to write a draft RFC and submit it to the IETF as a secure-by-default replacement for the JOSE standards.
There is talk among some security experts of launching a Web Token Competition for an "official" JOSE replacement, in the spirit of the Password Hashing Competition that gave us Argon2. Should such a competition surface in the near future, Paseto will be my entry in the contest.
However, minor documentation and political concerns aside, Paseto should be considered stable enough to be used in production systems.
If you're considering building JWT support into your next software project, consider supporting Paseto instead. It's time to retire error-prone cryptographic standards everywhere.
Are Versioned Protocols Really More Robust than Ciphersuite Agility?
Yes, and if you don't work with cryptography, the reason why might not be immediately obvious.
Modern ciphers aren't directly broken in the real world, most of the time. Most cryptography protocol vulnerabilities will be found in the joinery between components.
So instead of hoping for a cryptanalytic break-through, security researchers simply attack the mortar instead of the bricks.
A good example of this is, although AES is a secure block cipher, using it in ECB mode is not secure. Using AES in CBC mode (without adding ciphertext integrity) allows trivial plaintext recovery from a type of online attack called a padding oracle. These attacks don't break AES, but the protocols that use AES in this way are broken.
Circling back to the brick wall analogy, let's pretend you were building an actual brick wall, but instead of deciding on the bricks and then adhering them in place with mortar, you simply constructed a hollow wireframe of mortar so that bricks could be hot-swappable in case a new tenant decided they wanted a wall made of Papier-mâché brick instead of stone brick.
Would you trust that wall to hold up a roof?
Cryptography protocol design isn't too dissimilar from brick walls.
How does this relate to JOSE and Paseto?
Looking back at the JOSE standards, particularly JSON Web Encryption, you'll learn that you're allowed to encrypt a key with one of four methods:
- RSA with PKCS #1v1.5 padding (asymmetric cryptography, insecure)
- RSA with OAEP padding (asymmetric cryptography)
- ECDH (asymmetric cryptography)
- AES-GCM (symmetric cryptography)
(One of these things is not like the others.)
By giving users and implementors enough rope to hang themselves, ciphersuite agility balloons the complexity of the protocol and increases the attack surface, with little recourse if all the available options are horrendously broken. It also increases the chances of getting the configuration wrong in a way that dooms the security of a system.
What do Versioned Protocols to Differently?
The difference is subtle, but important.
Ciphersuite agility means essentially building a hollow wireframe or honeycomb structure and allowing implementors to fill in the blanks with options they find palatable. This is one of the core design mistakes of SSL/TLS that system administrators have fumed about for decades. (TLS 1.0: "You want to deploy RC4 with SHA1? OK, you're the boss.")
With ciphersuite agility, the security of a deployment is nontrivial ("Okay it's using version 1.2 but what ciphersuites are supported?"), and backward compatibility is complicated ("which ciphersuites are allowed in version 1.2 but not allowed in version 1.3?").
Versioned protocols means hard-coding one specific ciphersuite to any given version of the protocol. If the hard-coded ciphersuite for a version of the current protocol is found to be insecure, a new version should be decided and released. These two facts lead to a heightened risk for implementations and/or deployments ending up vulnerable to downgrade attacks (e.g. DROWN).
With versioned protocols, the security of a deployment is straightforward ("Oh, it's using version 2? No further questions") and backward compatibility is simple ("We only allow
v3, all others are blocked"). The risk of downgrade attacks isn't quite zero, but there is no unnecessary complexity to exacerbate the odds of it happening.
If you have versioned protocols, where the algorithm choice was sourced from security experts well-versed in cryptography, you end up in a much better place than mix-n-match cryptography construction.