Pular para o conteúdo
ecdsa.com

JWS vs JWE: tokens JOSE assinados ou criptografados

Um JWS prova quem criou um payload, mas qualquer pessoa pode lê-lo. Um JWE esconde o payload de todos, exceto do destinatário, mas, com algoritmos de chave pública, não diz nada sobre quem o criou.

JWS vs JWE é a questão do que um token JOSE protege. Uma JSON Web Signature (RFC 7515) acrescenta integridade e autenticidade a um payload que continua legível; uma JSON Web Encryption (RFC 7516) acrescenta confidencialidade. Quase todo JWT que você encontra é um JWS, o que surpreende quem espera que as claims sejam secretas.

Visão geral

JWSJWE
EspecificaçãoRFC 7515RFC 7516
ProtegeIntegridade e origemConfidencialidade (e integridade por meio do AEAD)
Forma compactaheader.payload.signature (3 partes)header.encrypted_key.iv.ciphertext.tag (5 partes)
Algoritmos no cabeçalhoalg (por exemplo, ES256, RS256, HS256, Ed25519)alg para o gerenciamento de chaves (ECDH-ES, ECDH-ES+A256KW, RSA-OAEP-256, A256KW, dir) mais enc para o conteúdo (A256GCM, A128CBC-HS256)
Payload visívelSim, apenas em base64urlNão
Remetente autenticadoSim (assinatura ou MAC)Apenas com chave simétrica ou com acordo de chaves autenticado; não com ECDH-ES ou RSA-OAEP sozinhos
Tamanho para um conjunto de claims de 70 bytes (medido abaixo)186 caracteres (ES256)405 caracteres (ECDH-ES+A256KW com A256GCM)
Usos típicosTokens de acesso e tokens de ID JWT, DPoP, requisições assinadas, SD-JWTTokens de ID criptografados, tokens armazenados no lado do cliente, algumas respostas FAPI e EUDI

O que cada um protege

Um JWS assina base64url(header) + "." + base64url(payload). Qualquer pessoa que tenha o token pode decodificar e ler as claims; o depurador de JWT faz exatamente isso. A assinatura prova que o token veio do detentor da chave e não foi alterado, e é disso que uma API precisa para confiar nas claims.

Um JWE gera uma chave de criptografia de conteúdo (CEK) aleatória, criptografa o payload com o AEAD indicado em enc e entrega a CEK ao destinatário com o algoritmo de gerenciamento de chaves indicado em alg: encapsulada com RSA-OAEP, encapsulada com AES Key Wrap depois de um acordo de chaves ECDH-ES, derivada diretamente do ECDH-ES ou pré-compartilhada (dir). O cabeçalho protegido é autenticado como dados associados. Só o destinatário consegue ler o payload.

A assimetria importante: com gerenciamento de chaves por chave pública, qualquer pessoa que tenha a chave pública do destinatário pode criar um JWE válido. Descriptografar com sucesso não prova nada sobre o remetente. Por isso, tokens sensíveis que também precisam ser confiáveis são aninhados: um JWT assinado (JWS) colocado dentro de um JWE com cty: JWT e verificado depois da descriptografia.

Tamanhos na prática

O script abaixo, executado com o jose 6.2.13 no Node.js 26.10, protege as mesmas claims das duas formas.

jose: as mesmas claims como JWS e como JWE
import { CompactEncrypt, CompactSign, compactDecrypt, compactVerify, generateKeyPair } from "jose";

const claims = new TextEncoder().encode(JSON.stringify({ sub: "user-42", email: "alice@example.com", plan: "pro" }));

// JWS: integrity and origin; the payload is only base64url-encoded.
const signer = await generateKeyPair("ES256");
const jws = await new CompactSign(claims).setProtectedHeader({ alg: "ES256" }).sign(signer.privateKey);
await compactVerify(jws, signer.publicKey);

// JWE: confidentiality for the holder of the recipient's private key.
const recipient = await generateKeyPair("ECDH-ES+A256KW");
const jwe = await new CompactEncrypt(claims).setProtectedHeader({ alg: "ECDH-ES+A256KW", enc: "A256GCM" }).encrypt(recipient.publicKey);
const { plaintext } = await compactDecrypt(jwe, recipient.privateKey);

console.log("JWS parts:", jws.split(".").length, "length:", jws.length, "payload readable:", Buffer.from(jws.split(".")[1], "base64url").toString().includes("alice"));
console.log("JWE parts:", jwe.split(".").length, "length:", jwe.length, "decrypts:", new TextDecoder().decode(plaintext).includes("alice"));
Saída
JWS parts: 3 length: 186 payload readable: true
JWE parts: 5 length: 405 decrypts: true

A maior parte do tamanho adicional do JWE vem da chave pública efêmera (epk) no cabeçalho, da chave encapsulada, do IV de 12 bytes e da tag de 16 bytes. Um token aninhado, assinado e depois criptografado, cresce ainda mais, já que o JWS inteiro vira o texto claro do JWE.

Armadilhas

  • Tratar as claims de um JWS como secretas. Não coloque dados pessoais, identificadores internos que você não publicaria nem segredos em um token apenas assinado; ele vai acabar em logs, no armazenamento do navegador e em tickets de suporte.
  • Confiar em um JWE não assinado. A descriptografia com ECDH-ES ou RSA-OAEP prova apenas que o token foi criptografado para você.
  • Algoritmos de JWE fracos ou legados. O RSA1_5 é vulnerável a ataques de padding oracle, o PBES2 com uma contagem de iterações p2c enorme pode ser usado para negação de serviço, e implementações de ECDH-ES que aceitavam pontos fora da curva vazaram chaves privadas em 2017 (RFC 8725 §2.5). Prefira ECDH-ES+A256KW ou RSA-OAEP-256 com A256GCM e limite as iterações do PBES2 se precisar oferecer suporte a ele.
  • Escolha do algoritmo pelo token. Assim como no JWS, convém que o destinatário aceite apenas os valores de alg e enc que espera para cada chave.
  • Compressão. O zip: DEF do JWE comprime antes de criptografar, o que pode vazar informações sobre o texto claro pelo comprimento (RFC 8725 §3.6); evite-o.

Quando escolher cada um

  • Tokens de acesso verificados pelas suas APIs: JWS; mantenha as claims no mínimo ou use tokens opacos com introspecção se o conteúdo não puder ficar legível.
  • Tokens de ID com dados pessoais armazenados por um navegador ou aplicativo móvel: o JWS é o usual; JWE aninhado se o seu modelo de ameaças incluir o ambiente do cliente.
  • Tokens que passam por intermediários não confiáveis (redirecionamentos por terceiros, tokens embutidos em URLs ou QR codes): JWS aninhado dentro de um JWE.
  • Mensagens entre máquinas sobre TLS com autenticação mútua: muitas vezes, nenhum dos dois; o TLS já oferece confidencialidade e integridade em trânsito, e um JWS só agrega valor se a mensagem precisar continuar verificável depois de armazenada ou encaminhada.
  • Payloads com criptografia de ponta a ponta para um destinatário conhecido: JWE com ECDH-ES+A256KW e A256GCM, ou formatos baseados em HPKE, à medida que forem padronizados para o JOSE.

Notas de migração

  1. Classifique as claims dos tokens JWS existentes: tudo o que não pode ficar visível para o portador sai do token ou passa para um JWE.
  2. Introduza os tokens aninhados aos poucos: emita JWEs com cty: JWT para os clientes que conseguem descriptografá-los e mantenha o JWS interno inalterado, para que a lógica de verificação seja reaproveitada.
  3. Distribua as chaves de criptografia separadamente das chaves de assinatura: convém que as entradas do JWKS tenham use: enc ou use: sig e valores de kid distintos.
  4. Fixe os algoritmos dos dois lados e rejeite RSA1_5, PBES2 sem limite de iterações e compressão.
  5. Revise os logs: depois que os tokens passarem a ser criptografados, garanta que as claims descriptografadas não sejam registradas em logs onde o token nunca foi registrado. O inspetor de JWE mostra o cabeçalho e a estrutura de um JWE sem descriptografá-lo.

Referências

Todas as comparações