Most teams sign their JSON Web Tokens and assume the contents are protected, yet a JWS payload is only base64url encoded and anyone holding the token can read every claim. JSON Web Signature (JWS, RFC 7515) gives integrity and origin authentication; JSON Web Encryption (JWE, RFC 7516) gives confidentiality. They solve different problems, use different algorithm registries and fail in different ways. This guide compares the two for architects, API security engineers and identity teams deciding whether a token, request object or message needs signing, encryption or both, and how to run the keys behind each. For token validation basics, start with the JWT Security Guide; this page goes deeper on the sign versus encrypt decision.
Key takeaways
- Signing is not hiding. RFC 7515 protects integrity and authenticates the origin of the content; it does not encrypt the payload, so claims in a signed JWT are visible to clients, browsers, proxies and logs.
- Encryption alone does not prove who sent it. A JWE with a publicly known recipient key can be created by anyone, so encryption on its own is not sender authentication.
- Sign, then encrypt, when you need both. RFC 7519 Section 11.2 says producers should normally sign and then encrypt, producing a Nested JWT with
ctyset toJWT; OpenID Connect requires that order for encrypted ID Tokens. - Different registries, different traps. JWS risks centre on
algconfusion,noneand weak HMAC secrets; JWE risks centre on RSA1_5, invalid curve points in ECDH-ES, compression and unbounded PBES2 iteration counts. - Keys decide the outcome. One key per algorithm and purpose, allowlisted algorithms, and explicit
typvalues (RFC 8725 Sections 3.1, 3.11 and 3.12) do more for safety than the choice of format.
The short answer
Use JWS when the recipient must be able to trust who issued the data and that it has not changed, and the data is not sensitive to whoever carries it. That covers most access tokens, ID Tokens, signed request objects and software statements. Use JWE when the data must stay unreadable to intermediaries, the client or the user agent, for example claims containing personal data, internal entitlements or secrets in transit through an untrusted hop. Use both, as a Nested JWT signed first and encrypted second, when the recipient needs confidentiality and proof of origin. If confidentiality is the only concern and the client never needs to read the token, an opaque reference token is often simpler than JWE; see JWT vs Opaque Tokens.
Side by side: what each format actually is
| Property | JWS (RFC 7515) | JWE (RFC 7516) |
|---|---|---|
| Security goal | Integrity and origin authentication (signature or MAC) | Confidentiality and integrity of the ciphertext (AEAD) |
| Compact serialisation | 3 parts: protected header, payload, signature | 5 parts: protected header, encrypted key, initialisation vector, ciphertext, authentication tag |
| JSON serialisation | Multiple signatures over one payload via a signatures array; a flattened form for one signer | Multiple recipients via a recipients array; a flattened form for one recipient; supports Additional Authenticated Data (AAD) |
| Key header parameters | alg, kid, jku, jwk, x5u, x5c, crit, typ | alg (key management), enc (content encryption, required), zip, kid, typ, cty |
| Who holds which key | Issuer holds the private key (or shared HMAC key); verifiers hold the public key | Recipient holds the private key; anyone with the public key can encrypt to it |
| Readable by carriers | Yes, payload is base64url only | No, only the holder of the decryption key |
| Proves sender | Yes (asymmetric) or proves a key holder (HMAC) | Not by itself with asymmetric key management |
| Typical uses | OAuth access tokens (RFC 9068), ID Tokens, client assertions, signed request objects, webhooks | Encrypted ID Tokens and UserInfo responses, tokens carrying personal data, encrypted request objects, sensitive claims between services |
JWS in depth: threat model, architecture and failure modes
Threat model
JWS defends against tampering and forgery: a client that edits its own scope claim, a proxy that alters a webhook body, or an attacker who mints a token for another user. It does not defend against disclosure, and it does not stop replay of a valid token; replay controls are exp, jti, audience restriction and sender-constrained tokens (RFC 7515 Section 10.10 covers replay protection as a separate concern).
Architecture in words
An issuer builds a protected header naming alg and kid, base64url encodes header and payload, and computes a signature over both. Verifiers fetch the issuer's public keys from a JWKS endpoint they have pinned in configuration, select the key by kid, check that the key's permitted algorithm matches the header, verify the signature, and only then evaluate claims. With HMAC (HS256 and friends) every verifier holds the same secret as the issuer, so any verifier can also mint tokens; RFC 7515 Section 10.5 spells out this difference between signatures and MACs.
Algorithms
| JWS alg | RFC 7518 requirement | Notes |
|---|---|---|
| HS256 | Required | Shared secret; key at least 256 bits; every verifier can forge |
| RS256 | Recommended | RSA PKCS #1 v1.5 signature; keys 2048 bits or larger |
| ES256 | Recommended+ | ECDSA P-256; RFC 8725 recommends deterministic ECDSA (RFC 6979) |
| PS256, PS384, PS512 | Optional | RSASSA-PSS; keys 2048 bits or larger |
| none | Optional | Unsecured JWS; must not be accepted by default |
The draft successor to RFC 8725 (draft-ietf-oauth-rfc8725bis-10, in the RFC Editor queue when checked in November 2026) also recommends fully specified identifiers such as Ed25519 over the polymorphic EdDSA for new deployments, per RFC 9864. Drafts change, so treat that as direction rather than a fixed rule.
Failure modes
- Algorithm confusion: a verifier that trusts the header
algcan be tricked into verifying an RS256 public key as an HS256 secret. RFC 8725 Section 3.1 requires libraries to let callers fix the allowed algorithm set and to bind each key to exactly one algorithm. - Accepting
none: RFC 7518 says implementations must not accept unsecured JWS by default. - Weak HMAC secrets: RFC 8725 Section 3.5 says human-memorable passwords must never be used directly as HMAC keys; they can be brute forced offline from a single token.
- Trusting header key references:
jku,x5uandjwklet the token tell you which key to trust. The 8725bis draft treatskid,jkuandx5uas attacker controllable, with injection and SSRF risks. - Cross-JWT confusion: an ID Token replayed as an access token, or a token for one API accepted by another. RFC 8725 Sections 3.8 to 3.12 call for issuer, audience and explicit type checks.
Forged or tampered tokens map to ATT&CK T1606 Forge Web Credentials; the defensive answer is strict verification rather than detection after the fact.
JWE in depth: threat model, architecture and failure modes
Threat model
JWE defends against disclosure to anyone other than the intended recipient: the browser that stores an access token, a TLS-terminating gateway, a log pipeline, or the client itself. RFC 9068 Section 6 notes that clients and end users can read claims in unencrypted JWT access tokens, and lists encryption, omitting sensitive claims or opaque tokens as mitigations. JWE also detects tampering with the ciphertext, because every enc value is an authenticated encryption algorithm, but with asymmetric key management that integrity says nothing about who created it.
Architecture in words
The sender generates a random Content Encryption Key (CEK), encrypts the plaintext with the enc algorithm, and protects the CEK for the recipient according to the alg key management mode. RFC 7516 Section 2 defines five modes: Key Encryption (for example RSA-OAEP), Key Wrapping (AES Key Wrap), Direct Key Agreement (ECDH-ES), Key Agreement with Key Wrapping (ECDH-ES+A256KW) and Direct Encryption (dir, where the shared key is the CEK). The protected header is used as AAD, so altering alg or enc breaks decryption.
Algorithms
| Parameter | Value | RFC 7518 requirement | Guidance |
|---|---|---|---|
| alg | RSA-OAEP, RSA-OAEP-256 | Recommended+, Optional | Preferred RSA key transport; RFC 8725 prefers OAEP over PKCS #1 v1.5 |
| alg | RSA1_5 | Recommended- (likely to decrease) | Avoid; deprecated in the 8725bis draft because of chosen ciphertext attacks |
| alg | ECDH-ES, ECDH-ES+A128KW, ECDH-ES+A256KW | Recommended+, Recommended | Validate ephemeral public keys on the curve (RFC 8725 Section 3.4) |
| alg | A128KW, A256KW, dir | Recommended | Symmetric; sender and recipient share a key |
| alg | PBES2-HS256+A128KW and siblings | Optional | Password based; cap the p2c iteration count |
| enc | A128CBC-HS256, A256CBC-HS512 | Required | AES-CBC with HMAC as a composite AEAD |
| enc | A128GCM, A256GCM | Recommended | AES-GCM; never reuse an IV with the same key |
Failure modes
- RSA1_5 padding oracles: RFC 7516 Section 11.4 describes adaptive chosen ciphertext attacks and downgrade from RSA-OAEP to RSA1_5; Section 11.5 says recipients should not reveal key format errors and should substitute a random CEK to avoid timing leaks.
- Invalid curve points: an ECDH-ES recipient that does not validate the sender's ephemeral key can leak its private key. RFC 8725 Section 3.4 points to partial public-key validation in NIST SP 800-56A Rev. 3, Section 5.6.2.3.4.
- Compression before encryption: the
zipvalueDEFcan leak plaintext through ciphertext length (RFC 8725 Section 3.6), and the 8725bis draft also asks implementations to cap decompressed size to avoid resource exhaustion. - Unbounded PBES2 work: an attacker-chosen
p2ccan make the recipient burn CPU. The 8725bis draft suggests rejecting counts above twice the OWASP-recommended 600,000 iterations. - Treating decryption as authentication: anyone can encrypt to a published recipient key. If the recipient acts on decrypted claims without an inner signature, an attacker can inject claims.
Nested JWTs: signing and encrypting together
A Nested JWT is a JWT used as the payload of an outer JWS or JWE. RFC 7519 Section 5.2 makes cty required in this one case, with the value JWT, so the recipient knows to process the inner token. Section 11.2 says the operations can be applied in either order but producers should normally sign first and then encrypt, because that stops an attacker stripping the signature from an encrypted message and keeps the signer's identity private; it also notes that signatures over encrypted content are not considered valid in many jurisdictions. OpenID Connect Core Section 2 states that an encrypted ID Token must be signed and then encrypted, and Section 3.1.3.7 says the relying party should reject an unencrypted ID Token when encryption was negotiated.
| Pattern | What the recipient learns | When it fits |
|---|---|---|
| JWS only | Who issued it, unchanged; content public to carriers | Access tokens with non-sensitive claims, client assertions, webhooks |
JWE only (symmetric, dir or AES KW) | Confidential; origin limited to holders of the shared key | Closed systems where one service encrypts state for itself, such as encrypted cookies |
| JWE only (asymmetric) | Confidential; origin unknown | Rarely appropriate for claims a recipient will act on |
| Sign then encrypt (Nested) | Confidential and origin authenticated; signer hidden from carriers | Encrypted ID Tokens, tokens with personal data crossing untrusted hops |
| Encrypt then sign | Origin of the ciphertext, not necessarily of the plaintext | Avoid for tokens; RFC 7519 recommends the opposite order |
RFC 8725 Section 3.3 requires the recipient to reject the whole token if any cryptographic operation fails, inner or outer, and Section 3.11 requires the inner JWT of a Nested JWT to carry typ. A recipient that decrypts and then forgets to verify the inner signature has turned a Nested JWT back into unauthenticated JWE.
Key management for each format
Key ownership is reversed between the two formats, and that drives who runs what infrastructure.
- JWS keys belong to the issuer: the authorisation server or token service holds signing keys, ideally in an HSM or cloud KMS, and publishes public keys via JWKS. Rotation is a publish, overlap, switch, retire cycle; see the JWKS Signing Key Rotation Guide.
- JWE keys belong to the recipient: each client or resource server that wants encrypted tokens holds a decryption private key and publishes the public part, for example through its registered JWKS. That means every recipient becomes a key custodian, which is a real operational cost for large fleets of services and non-human identities.
- Separate keys by use: RFC 8725 Section 3.1 binds each key to one algorithm and Section 3.12 recommends different keys for different kinds of JWT. Set the JWK
use(sigorenc) andalgmembers so a library cannot repurpose an encryption key for signing. - Symmetric shortcuts:
dirand HMAC are fast and simple but every party holding the key can both read and forge. Keep them inside a single trust boundary.
The Cryptographic Key Management Guide covers custody, rotation and destruction in general.
Decision guidance and trade-offs
| Question | If yes | If no |
|---|---|---|
| Does the recipient act on the claims (authorisation, identity)? | A signature is mandatory | Encryption alone may suffice for opaque state |
| Would disclosure to the client, browser or a log harm someone? | Encrypt, minimise claims, or use opaque tokens | JWS is enough |
| Does the client need to read the token? | JWS, or JWE to the client itself (ID Tokens) | Opaque or encrypted to the resource server |
| Can every recipient manage a decryption key safely? | JWE is viable | Prefer claim minimisation or opaque tokens |
| Is latency or token size critical (headers, cookies, mobile)? | Measure; JWE adds two parts and an extra key operation | Little concern |
- Performance: JWE adds a key management operation and an AEAD pass per token, and Nested JWTs add both a signature and a decryption. ECDH-ES and AES-GCM are cheap on modern hardware; RSA decryption at high request rates is the usual hotspot, so benchmark at peak load rather than assuming.
- Size: the encrypted key, IV and tag plus double base64url encoding in Nested JWTs grow tokens; check HTTP header and cookie limits on gateways.
- Visibility: encrypted tokens hide claims from gateways and security tooling too. If your gateway enforces scopes, it then needs the decryption key, widening key exposure; see API Gateway vs In-Service Token Validation.
- Small versus large organisations: a small team with one API is usually better served by short-lived signed tokens with minimal claims. Large estates with regulated personal data in tokens, or federation across organisations, are where JWE earns its operational cost.
Worked scenarios
Scenario 1, an internal microservice API: a token service issues access tokens to workloads with sub, aud, scope and exp. Nothing sensitive is in the claims, and TLS protects every hop. Choice: JWS with ES256, typ at+jwt as RFC 9068 Section 2.1 prefers, a 5 to 15 minute lifetime, and verifiers pinned to ES256 and the issuer's JWKS URL. Adding JWE would make every service a key custodian for no gain.
Scenario 2, a health portal's ID Token: the identity provider includes date of birth and a patient identifier for the relying party. The ID Token passes through the browser on a front-channel flow. Choice: sign then encrypt, with the relying party's registered RSA-OAEP-256 or ECDH-ES key and A256GCM, so the browser and any extensions see only ciphertext. The relying party decrypts, then verifies the inner RS256 or ES256 signature, and rejects the token if either step fails. Better still, move sensitive attributes to the UserInfo endpoint and keep the ID Token minimal.
Scenario 3, session state in a cookie: one web application stores a small state blob for itself. Choice: JWE with dir and A256GCM under a key only that application holds.
How to implement
- Inventory token flows: list every JWT type you issue or accept, who reads it, which hops it crosses and which claims it carries. Flag any token with personal data or internal entitlements.
- Minimise claims first: remove what the recipient does not need. The cheapest confidentiality control is a claim that is not there.
- Fix the algorithm allowlist: configure libraries with explicit
alg(and for JWE,enc) values per token type, rejectnone, RSA1_5 and anything unlisted. - Introduce explicit types: set
typper token kind (for exampleat+jwt) and reject mismatches. - Pilot encryption on one flow: register the recipient's encryption key, enable sign then encrypt for one client, and run both paths in parallel in a test ring; confirm the recipient rejects unencrypted tokens once encryption is negotiated.
- Load test: measure latency and token size at peak, including gateway header limits.
- Roll out with rollback: keep the issuer able to fall back to signed-only tokens per client through configuration, and remove the fallback once the pilot is stable.
- Automate rotation: schedule signing and encryption key rotation separately, with overlap windows published through JWKS.
Detection and measurement
Log verification and decryption outcomes at the validating component, never the token itself; see the JWT and Session Tokens in Code and Logs Guide. Useful signals are tokens presenting an unexpected alg or enc, unknown kid values, header jku or x5u pointing outside your allowlist, bursts of decryption failures from one source (a possible oracle probe), and unencrypted tokens arriving where encryption was negotiated.
- Allowlist coverage: percentage of token validators with an explicit algorithm allowlist (target 100%).
- Sensitive claims exposed: number of token types carrying personal data without encryption or minimisation (target 0).
- Deprecated algorithm use: count of tokens or keys using RSA1_5,
noneor HMAC secrets under 256 bits (target 0). - Rejection rate by reason: verification and decryption failures per million tokens, split by cause, to spot attacks and misconfiguration.
- Key rotation age: days since last rotation for each signing and encryption key against policy.
Common failure patterns
- Personal data in signed tokens: teams put email, phone or entitlements in a JWS access token believing the signature hides them; the client, the browser and every log can read them.
- Decrypt and trust: a service decrypts a JWE with its private key and authorises on the claims without checking an inner signature, so anyone who knows its public key can mint claims.
- One key for everything: the same RSA key pair used for RS256 signing and RSA-OAEP decryption, or a single HMAC secret shared across token types, enabling cross-purpose and cross-JWT attacks.
- Library defaults left alone: verifiers that read
algfrom the header, acceptnoneor followjkuto arbitrary URLs. - Encryption used to avoid fixing storage: encrypting a token that is then stored insecurely in the browser still leaves a bearer credential to steal; encryption hides claims, it does not stop replay. See the Browser Storage Security Guide.
Framework mapping
| Framework | Reference | JWS | JWE |
|---|---|---|---|
| NIST SP 800-53 Rev 5 | SC-8 Transmission Confidentiality and Integrity | Integrity of tokens in transit | Confidentiality of claims beyond TLS endpoints |
| NIST SP 800-53 Rev 5 | SC-12 Cryptographic Key Establishment and Management; SC-13 Cryptographic Protection | Signing key custody, allowed algorithms | Recipient key custody, key management modes |
| NIST SP 800-53 Rev 5 | AU-10 Non-repudiation; SI-7 Information Integrity | Asymmetric signatures bind origin | Only via an inner signature |
| NIST CSF 2.0 | PR.DS-02 data in transit protected | Integrity | Confidentiality and integrity |
| MITRE ATT&CK | T1606 Forge Web Credentials | Strict verification prevents forged tokens | Inner signature prevents injected claims |
Practitioner checklist
- Classify every JWT type you issue or accept as sign only, encrypt only or nested, and record why.
- Remove claims recipients do not need before considering encryption.
- Pin
algandencallowlists per token type; rejectnoneand RSA1_5. - Use sign then encrypt with
ctyJWTwhenever both properties are needed, and verify the inner signature every time. - Set explicit
typvalues and enforce issuer and audience checks to prevent cross-JWT confusion. - Give every key a single algorithm and a
useofsigorenc; never share keys across purposes. - Validate ECDH-ES ephemeral keys, disable or cap
zipdecompression, and cap PBES2p2c. - Ignore or allowlist
jku,x5uand embeddedjwkheaders; take keys from pinned configuration. - Rotate signing and encryption keys on separate schedules with JWKS overlap windows.
Standards and references
- IETF: RFC 7515 JSON Web Signature (JWS)
- IETF: RFC 7516 JSON Web Encryption (JWE)
- IETF: RFC 7518 JSON Web Algorithms (JWA)
- IETF: RFC 7519 JSON Web Token (JWT)
- IETF: RFC 8725 JSON Web Token Best Current Practices
- IETF: draft-ietf-oauth-rfc8725bis JSON Web Token Best Current Practices (Internet-Draft)
- IETF: RFC 9068 JWT Profile for OAuth 2.0 Access Tokens
- OpenID Foundation: OpenID Connect Core 1.0
Related NHI Mgmt Group resources: JWT Security Guide · Digital Signatures vs Encryption · JWT vs OAuth · JWT Client Authentication (private_key_jwt) Guide · SD-JWT and SD-JWT VC Guide