JWT vs PASETO: Anatomy of a Secure Token (And Why JWT Is a Hazard)
Published on May 9, 2026
JWT vs PASETO: Anatomy of a Secure Token (And Why JWT Is a Hazard)
In 99% of backend tutorials, when the moment comes to implement stateless authentication and authorization, the recipe is always identical: install a JSON Web Tokens library (jsonwebtoken), stuff the user ID into a payload, sign it with a secret, and ship it to the client. JWT has become the de facto standard — the industry’s unquestioned golden hammer for handling sessions in REST APIs, microservices, and modern applications.
But in the world of advanced cybersecurity and high-level software engineering, JWT is considered an architectural minefield.
Over the years, massive vulnerabilities in JWT implementations have allowed attackers to completely bypass authentication, forge admin identities, and compromise entire systems. The problem isn’t that developers are careless — the problem is that the JWT standard was designed with excessive flexibility that delegates critical cryptographic decisions to the wrong layer.
PASETO (Platform-Agnostic Security Tokens) was created to fix this disaster. In this article we’re going to mechanically dissect both formats, analyzing how they work at the internal level — their cryptographic primitives, speed, resource consumption, and why PASETO represents an infinitely more robust engineering design.
1. JSON Web Tokens (JWT): The Double-Edged Weapon
What Is It and How Does It Work Mechanically?
A JWT (defined in RFC 7519) is an encoded text string that transports information (“claims”) between two parties. Its visible structure is composed of three parts separated by a dot (.):
Header.Payload.Signature
Each of these parts is encoded in Base64URL (a URL-safe variant of Base64 that omits characters like +, /, and =).
- Header: A JSON object that describes the token type and the signing algorithm used.
- Payload: A JSON object containing the actual data (e.g.
subfor user ID,expfor expiration time,role, etc.). - Signature: A cryptographic hash generated by concatenating the encoded Header and Payload, signed using the server’s secret and the algorithm specified in the Header.
// 1. Decoded Header
{
"alg": "HS256",
"typ": "JWT"
}
// 2. Decoded Payload
{
"sub": "usr_984321",
"role": "admin",
"exp": 1780000000
}
The Cardinal Design Sin: Cryptographic Agility
If you look at the JWT validation flow, you’ll notice a massive conceptual flaw: the server reads the Header of the unverified token to determine which algorithm to use for signature verification. This is known as Cryptographic Agility.
In theory, this allows a single system to support multiple algorithms. In practice, it has been the root cause of the most infamous vulnerabilities in the history of web development:
- The
alg: "none"attack: Many older libraries allowed an attacker to take a valid token, modify its role to"admin", change the header to{"alg": "none"}, and strip the signature. The server would read"none", assume the token required no cryptographic validation, and grant full access. - Key Confusion / Algorithm Confusion attacks: If a server uses RSA asymmetry (
RS256) to sign tokens with a private key and verify with a public key, an attacker can capture the public key (often exposed so other services can validate the token), sign a malicious payload using HMAC (HS256) with the RSA public key as the symmetric secret, and send the token. If the backend is misconfigured, it will readHS256from the header and use its local public key for HMAC, successfully validating the forged token.
The Visibility Problem
A classic junior developer mistake is assuming that a JWT is opaque or safe from being read. The payload of a standard JWT is plain text encoded in Base64URL. Anyone who intercepts the token — in a log, in browser storage, or in transit — can instantly decode it and read emails, internal identifiers, or sensitive metadata.
To encrypt a JWT you need to use JWE (JSON Web Encryption), a secondary standard of monumental complexity that turns the token into a five-part beast with a massive header, destroying its lightness entirely.
2. PASETO: The “Pit of Success” of Security
What Is It and What’s the Philosophy?
PASETO was designed by Scott Arciszewski (from Paragon Initiative Enterprises) under a fundamental usability principle in high-criticality software engineering: the Pit of Success. An API must be designed so that the easiest path is using it correctly, and the hardest (or impossible) path is introducing a vulnerability.
Unlike the JOSE RFCs (JSON Object Signing and Encryption) that span hundreds of pages of combinable options, PASETO completely eliminates cryptographic agility. The developer does not choose the algorithm.
Structure and Internal Workings
A PASETO token has a standardized, dot-delimited structure, but its parts serve drastically different purposes than JWT’s:
version.purpose.payload.footer
- Version: Defines the exact specification and the immutable set of cryptographic primitives to be used. Currently, the industry standard is Version 4 (
v4). (Versions 1 and 2 are deprecated; Version 3 is reserved for specific use cases.) - Purpose: Defines whether the token uses symmetric or asymmetric keys. Only two values exist:
local: The payload is fully encrypted and authenticated using a shared symmetric key.public: The payload is in plain text, but digitally signed with a public/private asymmetric key pair.
- Payload: The actual token data (encrypted or encoded depending on the purpose).
- Footer (Optional): Unencrypted but cryptographically authenticated metadata (useful for passing key identifiers or Key IDs without decrypting the main token).
// Example of a PASETO v4 local token (Symmetric encryption)
v4.local.cu12X9b3...an813Jh
Immutable Cryptography by Version
If you receive a token starting with v4.local, the PASETO engine knows with absolute mathematical certainty which algorithms to use — without reading any JSON or accepting hints from the client:
- Encryption and Authentication (
v4.local): Uses XChaCha20-Poly1305, a state-of-the-art Authenticated Encryption with Associated Data (AEAD) algorithm. It generates a random nonce per token to guarantee that two identical payloads produce completely different strings. - Digital Signature (
v4.public): Uses Ed25519 (Edwards-curve Digital Signature Algorithm), an ultra-fast cryptographic primitive with a small key size, immune to Side-Channel Attacks.
With no alg field in the token, algorithm confusion attacks or downgrades to plain text are structurally impossible.
3. In-Depth Technical Comparison
To decide which standard to use in your next backend architecture, let’s analyze how they compete across the critical production vectors:
A. Cryptographic Robustness and Security
- JWT: A fragile standard. It requires that the underlying library be perfectly programmed to reject malicious headers, and that the developer explicitly validates in their code that the received algorithm matches the expected one (e.g.
jwt.verify(token, secret, { algorithms: ['RS256'] })). If you forget that explicit configuration, your system is potentially vulnerable. - PASETO: Robust by design. It natively implements authenticated encryption (AEAD) in its
localmode. This means a PASETO local token guarantees Confidentiality (no one can read the content) and Integrity (no one can alter a single bit without invalidating the authentication tag). In JWT, achieving confidentiality requires implementing JWE, which is rarely done in production due to its enormous friction.
B. Processing Speed (Performance)
The performance of a token under high concurrency (thousands of requests per second validating sessions in an API Gateway) depends on the underlying mathematical primitives:
- Asymmetric Signing: JWT typically uses RSA with 2048 or 4096-bit keys (
RS256). RSA signature verification is reasonably fast, but signature generation is extremely CPU-intensive. PASETOv4.publicuses Ed25519, whose elliptic curve operations are drastically faster and more efficient in CPU clock cycles than RSA, enabling significantly higher throughput in authentication microservices. - Symmetric Encryption: JWT typically uses HMAC-SHA256 for symmetric signatures. PASETO
v4.localsimultaneously encrypts and signs using XChaCha20-Poly1305. On modern processors (especially mobile, ARM, or servers without dedicated AES hardware instructions), ChaCha20 is massively faster and more predictable than traditional block ciphers. - Parsing Costs: To validate a JWT, the server must first split the string, Base64URL-decode the Header, parse that JSON in memory, read the
algfield, and then execute cryptography. PASETO directly reads the plain-text prefix (v4.local.), validates the version in microseconds, and passes the binary block directly to the cryptographic engine — avoiding the overhead and Denial-of-Service attack vectors from early-stage JSON parsing.
C. Resource Consumption and Size (Payload Overhead)
- Base Size: Both formats use Base64URL encoding to be safely transported in HTTP headers (
Authorization: Bearer <token>). Therefore, size grows linearly with the amount of data you inject. - Structural Overhead: A basic JWT has a small header (approx. 36 bytes encoded). A PASETO
v4.localtoken adds a predictable fixed overhead to store the cryptographic nonce (32 bytes for XChaCha20) and the Poly1305 authentication tag (16 bytes). - Real Efficiency: Although a PASETO local token can be slightly larger in raw bytes than an unencrypted JWT due to the high-security nonce storage, the difference is insignificant on modern bandwidth (tens of bytes). In exchange, you get a fully encrypted payload. Comparing PASETO to an encrypted JWT (JWE), PASETO is immensely more compact and memory-efficient.
4. Advantages and Disadvantages in the Real World
JSON Web Tokens (JWT)
✅ Advantages:
- Universal Adoption: Every programming language, framework, and industry platform has mature libraries for JWT.
- Standard Interoperability: It is the foundation of global identity federation protocols like OAuth2 and OpenID Connect (OIDC). If you integrate Google, Apple, or Auth0 login, you will consume JWTs whether you want to or not.
- Ease of Debugging: Visual tools like
jwt.ioallow instant inspection of token content during local development.
❌ Disadvantages:
- Insecure by Default: Designed with an excess of options that makes vulnerable implementations easy if the code is not rigorously audited.
- No Native Privacy: Exposing claims in plain text encourages leaking internal information or PII (Personally Identifiable Information) to the frontend.
PASETO
✅ Advantages:
- Unbreakable Security by Design: Eliminates entire classes of cryptographic vulnerabilities at the root. There is no algorithm negotiation.
- Opaque Encryption by Default: In
localmode, the client has no way of knowing what’s inside the token, protecting the internal logic and backend data. - Cutting-Edge Primitives: Exclusively uses modern, optimized, and future-resistant cryptography (Ed25519, XChaCha20-Poly1305).
❌ Disadvantages:
- Younger Ecosystem: Although it has excellent support in Go, Rust, Node.js, PHP, and Python, some legacy enterprise platforms lack official native PASETO libraries.
- Does Not Replace OIDC: If you’re building a public Identity Provider that must comply with the OpenID Connect standard so third parties can connect to your system, the standard will require you to issue JWTs.
Summary and Practical Comparison (Cheat Sheet)
Here’s the quick reference to contrast both formats and make informed architecture decisions:
| Feature | JWT (JSON Web Tokens) | PASETO (v4) |
|---|---|---|
| Design Approach | Maximum flexibility (Cryptographic Agility) | Maximum security (“Pit of Success”) |
| Header | Dynamic JSON (Defines the algorithm to use) | Simple static prefix (v4.local. or v4.public.) |
| Symmetric Cryptography | HMAC-SHA256 (Typically) | XChaCha20-Poly1305 (AEAD Authenticated Encryption) |
| Asymmetric Cryptography | RSA / ECDSA (Typically) | Ed25519 (High-speed Elliptic Curves) |
| Payload Visibility | Plain text (Base64URL) unless you use JWE | Encrypted and Opaque in local mode / Readable in public |
alg: none Prevention | Depends on strict library validation | Immune by design (The alg concept doesn’t exist) |
| Server Speed | Medium (Penalized by RSA signatures and JSON parsing) | High (Ultra-optimized modern primitives) |
| Ideal Use Case | Public interoperability (OAuth2, OIDC, third-party SSO) | Internal communications, proprietary APIs, Microservices |
My Personal Take
As a software engineer, the choice between JWT and PASETO comes down to one single question about the context and boundaries of your system: Who is going to consume and validate this token?
If you’re designing a closed system — for example, the backend of your own SaaS platform, the internal communication between your microservices, or the session tokens your own Single Page Application (SPA) or mobile app sends to your API — using JWT today means accepting unnecessary risks. Implementing PASETO in this scenario is an infinitely superior engineering decision. It grants you native encryption without effort, superior CPU performance, and shields you against key confusion attacks from day zero.
On the other hand, if you’re building a public API that must integrate with the global OAuth2 ecosystem or interoperate with external identity providers (like Auth0, Keycloak, or Azure AD), the standard will force you to use JWT. If you find yourself in this situation, the unbreakable golden rule is: treat the JWT header as untrusted, user-supplied data. Never let your code decide which key or method to use based on the token’s alg field — always enforce a strict whitelist of allowed algorithms in your validation logic.
Real security isn’t about choosing the most popular tool on GitHub. It’s about using abstractions that reduce the human error surface to an absolute minimum. And on that front, PASETO has already won the battle.