Todos os artigos
74 artigos · atualizado semanalmente Veja nossas Ferramentas
Todos os artigos
Tutoriais

Decodificando JWT no navegador: por que o segredo não importa aqui

JWT é assinado, não criptografado. Entenda o que dá pra inspecionar sem nenhuma chave, e quando isso é seguro fazer client-side.

COVER · Tutoriais

Toda semana alguém abre uma issue perguntando se é seguro decodificar tokens JWT no client — e a resposta surpreende quem espera um "depende". Spoiler: pra inspecionar o payload, depende muito pouco.

Este post explica por quê, mostra como fazer no navegador em sete linhas de JavaScript, e enumera onde a coisa muda de figura.

Anatomia de um JWT

Um JWT é só três strings em Base64URL separadas por ponto:

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0In0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

A primeira é o header (algoritmo, tipo). A segunda é o payload (suas claims). A terceira é a assinatura — calculada a partir das duas primeiras mais um segredo.

Repare: nenhuma das partes é criptografada. Base64URL não é criptografia, é só uma forma de embalar bytes em ASCII seguro pra URL. Qualquer um que tenha o token consegue ler o payload. Qualquer um. Inclusive você, sem precisar de nenhuma chave.

Decode ≠ Verify

Aqui está a confusão que vejo todo dia:

Decodificar um JWT te diz o que ele afirma. Verificar te diz se você pode acreditar nele.

São operações completamente diferentes. Decodificar precisa de zero segredo — qualquer pessoa faz, e não há problema nisso. Verificar precisa da chave compartilhada (HMAC) ou pública (RSA/ECDSA), e idealmente acontece no servidor.

Misturar as duas é a raiz da maioria dos bugs de segurança com JWT. A claim { "sub": "12345", "role": "admin" } não vale nada se você não verificou a assinatura antes de confiar nela.

Fazendo isso no navegador

O decoder JWT do Quick Tools faz literalmente isto:

function decodeJwt(token) {
  const [h, p] = token.split('.');
  return {
    header:  JSON.parse(atob(h.replace(/-/g, '+').replace(/_/g, '/'))),
    payload: JSON.parse(atob(p.replace(/-/g, '+').replace(/_/g, '/'))),
  };
}

Sete linhas, zero dependências, zero rede. O token nunca sai do seu navegador — não precisaria, porque qualquer parte interessada já consegue ler também.

Por que atob precisa do replace?

Base64URL substitui + por - e / por _ pra sobreviver dentro de URLs sem encoding. atob só entende o Base64 canônico (RFC 4648), então a gente desfaz a troca antes de decodificar. É um detalhe que aparece toda vez que alguém tenta implementar isso do zero sem ler a especificação.

O que tem no payload?

Tipicamente: sub (subject — quem é o usuário), iat (issued at), exp (expiration), e as claims customizadas que sua aplicação definiu. Tudo isso é legível sem nenhuma chave. Por isso, nunca coloque informação sensível no payload — dados de cartão, senha temporária, qualquer coisa que não deva ser lida por qualquer intermediário.

O que você NÃO deve fazer

  • Confiar no payload sem verificar a assinatura no backend. Qualquer pessoa forja claims; só a assinatura garante autenticidade.
  • Guardar segredos HMAC no client. Se a chave está no bundle JavaScript, ela está efetivamente pública.
  • Colar tokens de produção em ferramentas online que fazem requests para um servidor. Você não sabe o que elas fazem com o token. Use ferramentas client-only — é exatamente pra isso que elas existem.
  • Usar o algoritmo none. Algumas bibliotecas antigas aceitavam JWTs sem assinatura. Se a sua versão permite isso, atualize agora.

Decodifique com confiança, verifique no backend

Decodificar JWT no navegador é seguro e útil para debug, logging, e mostrar informações ao usuário (como nome de exibição ou permissões). Verificar JWT no navegador quase sempre é o sintoma de uma arquitetura que deveria mover essa lógica para o backend.

A regra é simples: decodificação é presentação, verificação é autorização. Nunca tome decisões de acesso baseadas em um token que só foi decodificado, não verificado.

O que pode e o que não pode ser feito no navegador

Decodificar JWT no navegador é seguro quando o objetivo é inspecionar header e payload. Esses dados já estão no token e não exigem segredo para leitura. Isso é útil para verificar exp, iat, aud, iss, roles e claims customizadas durante debug.

O que não deve acontecer no navegador é validação de confiança. A assinatura precisa ser verificada no backend ou em um ambiente onde a chave pública/segredo e as regras de permissão estejam sob controle. Um usuário pode alterar o payload localmente; sem validação de assinatura, o conteúdo não prova nada.

Checklist de debug

Ao inspecionar um token, confira primeiro o algoritmo, depois emissor, audiência e expiração. Verifique se o horário está em epoch seconds, não milliseconds. Em seguida, confirme se as claims usadas pela interface são apenas informativas. Autorização real deve vir de resposta do servidor.

Use o Decodificador JWT para ler o token sem enviá-lo a terceiros. Mesmo assim, evite colar tokens reais de produção se eles ainda estiverem válidos.

HT
Autor
Hugo Tanaka
Japonês-brasileiro de segunda geração, cresceu vendo o pai debugar planilhas de logística em BASIC. Desenvolvedor full-stack com foco em tooling e DX — o papel que finalmente uniu código, escrita e obsessão por produtividade num lugar só. Escreve para quem já sabe programar e quer resolver um problema agora.
Ver perfil