Guias JWT

Como Decodificar JWT sem Secret ou Chave

Veja como ler cabeçalho, payload e claims de um JWT assinado sem conhecer o secret, e entenda exatamente quando a verificação da assinatura passa a ser necessária.

Por JWTDecoder.toolsÚltima verificação em 2026-09-12

Voz editorial: a primeira pessoa descreve o fluxo de trabalho documentado do JWTDecoder.tools; JWTDecoder.tools é o editor e autor organizacional.

Fluxo para decodificar JWT sem secret antes de verificar a assinatura
Primeiro decodifico e inspeciono; só depois uso material de chave confiável para decidir autenticidade.

Quando recebo um JWT e a primeira pergunta é apenas “o que existe dentro deste token?”, eu não começo procurando o secret de assinatura. Primeiro verifico se estou diante de um JWT/JWS assinado comum ou de um JWE criptografado. No formato assinado de três segmentos, consigo decodificar o cabeçalho e o payload sem conhecer secret, chave privada ou chave pública.

Essa é a distinção que mantenho durante toda a depuração: decodificar mostra o que o token declara; verificar mostra se eu posso confiar em quem assinou esses bytes.

Se já tenho o token, normalmente começo pelo Decodificador JWT. Ele mostra cabeçalho, payload, claims de tempo e o segmento de assinatura localmente no navegador, antes de eu fazer qualquer suposição sobre a chave usada pelo emissor.

Primeiro passo: JWT assinado ou JWE criptografado?

A primeira coisa que faço é contar os segmentos separados por ponto.

Um JWT/JWS assinado comum tem esta forma:

header.payload.signature

São três segmentos.

Um JWE no formato compacto normalmente tem cinco. Se encontro cinco segmentos, paro de tratar o payload como algo que deveria ficar legível com uma simples decodificação Base64URL. JWE envolve criptografia; decodificar e descriptografar são tarefas diferentes.

Esse teste simples evita um erro comum: assumir que qualquer string começando com eyJ deve revelar claims legíveis.

Por que consigo decodificar JWT sem o secret

Em um JWT assinado normal, cabeçalho e payload são objetos JSON codificados em Base64URL. Base64URL é codificação, não criptografia.

Ao decodificar o cabeçalho, posso encontrar algo como:

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "key-2026-09"
}

E no payload:

{
  "sub": "user_123",
  "iss": "https://auth.example.com/",
  "aud": "api://backend",
  "scope": "read:user write:profile",
  "iat": 1789000000,
  "exp": 1789003600
}

Nenhum secret foi necessário para revelar esses dois objetos. A RFC 7519 define o modelo de claims do JWT e a RFC 7515 define a representação JWS assinada. A chave de assinatura protege integridade e autenticidade; ela não transforma os claims de um JWS comum em conteúdo confidencial.

Por isso, não trato JWT assinado como um formato adequado para esconder informações sensíveis do portador do token. Se confidencialidade é requisito, é preciso avaliar criptografia/JWE e o desenho do protocolo, não apenas assinatura.

O que verifico primeiro no payload

Depois de decodificar, não leio todos os campos com a mesma prioridade. Minha ordem costuma ser:

  1. alg — qual algoritmo o cabeçalho declara?
  2. kid — existe um identificador de chave que será usado depois?
  3. iss — qual emissor o token declara?
  4. aud — para qual API ou destinatário ele foi emitido?
  5. exp, nbf, iat — está expirado, ainda não ativo ou com uma linha do tempo estranha?
  6. scope, roles, groups — quais dados de autorização aparecem?
  7. sub — qual sujeito o token identifica?

O guia de claims JWT aprofunda cada campo. Para diagnóstico rápido, essa ordem costuma separar cedo problemas de tempo, ambiente, audiência e seleção de chave.

Por que ainda não confio no payload decodificado

Um payload pode parecer totalmente plausível e ainda ter sido fabricado por qualquer pessoa.

Alguém pode criar:

{
  "sub": "user_123",
  "role": "admin"
}

codificar em Base64URL e montar uma string com formato de JWT.

Também é possível pegar um token assinado e alterar:

{
  "role": "user"
}

para:

{
  "role": "admin"
}

O resultado continuará sendo decodificável. O que não deve continuar válido é a assinatura original, porque os bytes do payload mudaram.

É por isso que não uso “decodificou com sucesso” como sinônimo de “JWT válido”. No JWTDecoder.tools mantemos estados separados: a estrutura pode estar legível enquanto a assinatura permanece presente — não verificada.

Quando eu realmente preciso do secret ou da chave pública

A necessidade de chave aparece quando a pergunta muda de:

O que este token contém?

para:

Um emissor que eu confio assinou exatamente estes bytes?

Com algoritmos HMAC como HS256, preciso do segredo compartilhado que o sistema confiável usa para assinar/verificar.

Com algoritmos assimétricos como RS256, normalmente preciso da chave pública do emissor, não da chave privada. O emissor assina com a privada; a aplicação verifica com a pública.

Quando já tenho material confiável, uso o Verificador de Assinatura JWT. Se o emissor publica um conjunto de chaves, uso o Validador JWKS para selecionar a JWK correta por kid.

Decodificar, verificar e validar não são a mesma coisa

Uso três perguntas separadas porque elas detectam classes diferentes de problema.

1. Decodificar

Pergunta: o que está presente no token?

Aqui inspeciono cabeçalho, claims, timestamps e assinatura como dados.

2. Verificar

Pergunta: a assinatura corresponde a uma chave que eu já confio?

Essa é uma operação criptográfica. A procedência da chave importa tanto quanto o resultado matemático.

3. Validar

Pergunta: esta aplicação deve aceitar o token?

Isso pode depender de exp, nbf, iss, aud, tipo de token, scopes, roles e política específica. O Validador JWT e o Depurador JWT ajudam nessa etapa.

Separar essas três tarefas evita transformar uma operação simples de Base64URL em uma conclusão de segurança que ela não sustenta.

Como trato exp, iat e nbf

Se encontro exp, lembro que JWT NumericDate usa segundos desde o Unix epoch. JavaScript Date.now() usa milissegundos.

Portanto, ao criar uma data em JavaScript, a conversão normal é:

const expiresAt = new Date(payload.exp * 1000);

Se minha dúvida é apenas “este token expirou?”, uso o Verificador de Expiração JWT, que também exibe iat, nbf, idade do token e janela de validade.

Se exp ainda está no futuro e a API responde 401, paro de culpar a expiração e avanço para assinatura, emissor e audiência.

Posso colar um JWT de produção em um decodificador?

Trato bearer tokens de produção como credenciais, mesmo quando o payload é legível.

Minha regra é: se um token sintético ou com dados removidos reproduz o problema, prefiro esse token.

O decodificador do JWTDecoder.tools foi projetado para processar o texto em memória no navegador e não enviar intencionalmente o JWT para uma API de decodificação, nem salvá-lo em localStorage, cookies ou na URL. Isso reduz uma classe importante de risco, mas não protege contra software que já esteja observando o dispositivo, como extensões maliciosas, malware, utilitários de clipboard ou captura de tela.

Também nunca coloco refresh tokens, chaves privadas ou secrets HMAC de produção em issues públicas, chats ou exemplos de documentação.

Meu checklist para decodificar JWT sem secret

Quando quero ler um JWT sem chave de assinatura, sigo esta sequência:

  1. Confirmo se há três segmentos ou se parece ser um JWE de cinco segmentos.
  2. Decodifico cabeçalho e payload com o Decodificador JWT.
  3. Leio alg, kid, iss, aud, exp, nbf, iat, scopes e roles.
  4. Trato todos os claims como não confiáveis até verificar a assinatura.
  5. Se o problema parece temporal, uso o Verificador de Expiração JWT.
  6. Se autenticidade importa, uso o Verificador de Assinatura JWT com uma chave confiável.
  7. Se a API continua rejeitando, comparo issuer e audience no Depurador JWT.

A regra prática é simples: não preciso do secret para ler um JWT assinado comum, mas preciso de material de chave confiável antes de acreditar que os claims legíveis são autênticos.

Fontes e referências

Guias relacionados