QuickUse Generator

Decoder & Inspector de JWT

Decodifique qualquer JSON Web Token, leia as claims e verifique a assinatura com seu segredo ou chave pública. 100% client-side — seu segredo não sai da página.

0 / 8000 caracteres

Cole um JWT acima para decodificar header, payload, assinatura e claims.

Guia editorial

Sobre este gerador

Leitura técnica honesta sobre o que está acontecendo por trás do botão Gerar.

Um JWT são três segmentos codificados em base64url e separados por pontos: o cabeçalho que nomeia o algoritmo, o payload com as claims e a assinatura que prova que cabeçalho e payload não foram adulterados depois da emissão. Decodificar mostra o que o token diz. Verificar prova que ele continua confiável. Esta ferramenta faz as duas coisas, roda inteiramente no seu navegador e nunca gera nem assina um token — não há caminho com chave aqui para um atacante explorar.

Três segmentos, dois deles legíveis

A RFC 7519 define o formato compacto do JWT. Cabeçalho e payload são objetos JSON, cada um codificado em base64url conforme a RFC 4648 §5 — a variante URL-safe que troca + por -, / por _ e elimina o padding =. A assinatura é o MAC ou a assinatura digital sobre a string header.payload concatenada, ela mesma codificada em base64url. Aplique decodeJwt no exemplo canônico da RFC 7519 §3.1 e você obtém cabeçalho { "typ": "JWT", "alg": "HS256" } e payload { "iss": "joe", "exp": 1300819380, "http://example.com/is_root": true }.

base64url não é criptografia. Qualquer pessoa que leia a URL lê o payload — esse é o objetivo. JWTs são à prova de adulteração, não confidenciais. Se você colocou um segredo de sessão, um telefone ou um identificador interno dentro do payload, você publicou esse dado. Use JWE (RFC 7516) quando precisar de confidencialidade. Este decoder lê JWS apenas; tokens criptografados (JWE) estão fora do escopo.

As claims registradas e o que elas significam de fato

As sete claims registradas no IANA (RFC 7519 §4.1) são as únicas com semântica portável entre bibliotecas: iss (issuer), sub (subject), aud (audience), exp (expiração, NumericDate em segundos desde a época), nbf (not-before), iat (issued-at) e jti (identificador único para defesa contra replay). Tudo o resto é claim pública ou privada — a spec é deliberada em não prescrever em excesso. O widget lista as registradas primeiro, na ordem da RFC, depois as suas claims customizadas na ordem em que aparecem, e formata exp / iat / nbf como timestamps ISO com uma dica de tempo relativo — um token expirado salta na cara.

Um token está expirado quando exp × 1000 ≤ agora. A checagem é local à sua máquina e ao instante em que você abriu a página — não há negociação de drift de relógio. Se o seu serviço rejeita um token que o decoder mostra válido por alguns segundos, a diferença é entre o relógio do seu servidor e o do emissor, não um bug do decoder.

HS, RS, PS, ES — e por que o alg importa

A RFC 7518 cataloga todos os algoritmos que um JWS pode usar. As três famílias relevantes na prática são HMAC (HS256, HS384, HS512) com um segredo simétrico compartilhado; RSASSA-PKCS1-v1_5 (RS256, RS384, RS512) e sua versão com padding PSS (PS256, PS384, PS512) usando um par de chaves RSA; e ECDSA (ES256 na curva P-256, ES384 na P-384) usando par de chaves de curva elíptica. HMAC simétrico é o mais fácil de operacionalizar — os dois lados guardam o mesmo segredo. As variantes assimétricas existem quando o verificador não deve poder emitir tokens, que é o caso normal em arquiteturas OAuth: o servidor de autorização assina com a chave privada e os servidores de recurso verificam com a pública correspondente.

O painel de verificação roteia por família. HMAC recebe o segredo como bytes brutos via SubtleCrypto.importKey("raw", …). RSA, RSA-PSS e ECDSA recebem uma chave pública em PEM, e o widget retira o invólucro -----BEGIN PUBLIC KEY-----, decodifica o base64 para bytes SPKI DER e entrega para importKey("spki", …). Chaves privadas são rejeitadas no parser — você não consegue colar uma chave de assinatura por engano no verificador.

Dois ataques conhecidos que o decoder não esconde de você

O primeiro é alg=none. Um JWT com {"alg":"none"} no cabeçalho não carrega assinatura nenhuma (o terceiro segmento é vazio). A spec original permite isso em contextos não assinados, mas para tokens em produção é uma vulnerabilidade: uma biblioteca que "verifica" um token alg=none pulando a checagem da assinatura aceita qualquer coisa. O OWASP JWT cheat-sheet sinaliza isso desde 2015 e a Web Security Academy do PortSwigger ainda usa esse caso como exercício de teaching. O widget mostra uma faixa vermelha INSECURE no instante em que o cabeçalho é parseado como none, e o pipeline de verificação recusa retornar resultado.

O segundo é algorithm confusion. Um verificador vulnerável que alterna entre RS256 (assimétrico, chave pública RSA) e HS256 (simétrico, segredo compartilhado) confiando no alg auto-declarado do token pode ser induzido a usar os bytes da chave pública RSA como segredo HMAC. Quem leia a chave pública forja tokens. A mitigação do lado do servidor é uma allow-list estrita: um verificador configurado para RS256 trata token HS256 como malformado, ponto final. Este decoder mostra o alg declarado, a família para a qual ele rotearia e como o material de chave é interpretado — você identifica a divergência na hora.

Por que Web Crypto, por que client-side

O SubtleCrypto do Web Crypto existe em todo browser evergreen e no Node 18+. É a mesma primitiva que a sua biblioteca de autenticação usa no servidor, acessível dentro da página. A chamada de verify é um subtle.verify() contra uma chave devolvida por importKey(), e a matemática roda na implementação nativa de cripto do host, não em JavaScript. Isso compra comparações em tempo constante contra timing attacks na checagem de assinatura, aceleração por hardware em plataformas que oferecem, e uma única superfície de auditoria atravessando browsers e Node.

O decoder usa Web Crypto só para verificar. Não há caminho de assinatura no código-fonte — busque por subtle.sign no repositório e você só encontra em arquivos de teste gerando vetores de round-trip para o verificador. Seu segredo ou chave privada, caso você cole um por engano, vive em state local do componente React pelo tempo de vida da aba e nunca é gravado em localStorage, sessionStorage ou em qualquer egresso de rede.

O que a página faz com o seu token

O JWT de entrada, o segredo e a chave pública são processados integralmente na aba do navegador. Cabeçalho, payload e assinatura decodificados vivem no state do componente. O painel de histórico recente persiste a string do JWT — base64url não é segredo, é byte público — sob a chave de escopo recent-inputs:jwt-decoder:v1, junto com um rótulo de variante (decoded ou decoded-verified) e um timestamp. O segredo de verificação e qualquer PEM colado nunca são escritos nesse armazenamento; um teste em tempo de execução varre todo o localStorage depois de uma verify para garantir que os bytes do segredo não aparecem em disco. Recarregar uma entrada do histórico repete só o JWT — o campo de segredo é limpo e você precisa reentrar.

JWT em fintech brasileira: Open Finance e FAPI

No Brasil, o uso mais regulado de JWT está no Open Finance Brasil, que herdou os padrões do antigo Open Banking e segue a especificação FAPI-1.0 Part 2 Advanced. As instituições autorizadas pelo Bacen autenticam clientes de API por mTLS e private_key_jwt simultaneamente — o JWS assinado entra como prova de posse da chave privada cadastrada no diretório. A Instrução Normativa BCB nº 134/2021 referencia os padrões técnicos de segurança que regem essa cadeia. Na prática, é o decoder que você usa para conferir uma request object assinada antes de subir uma integração em sandbox, e o verify panel responde "valid" ou "invalid" sem você precisar instalar o openssl numa máquina compartilhada.

Onde isso justifica o tempo

A sessão típica de debug dura dois minutos: cola um token de um cabeçalho HTTP, lê as claims, confere se exp passou, copia sub para uma query no banco. As ferramentas vizinhas cobrem a camada de codificação — o Codificador/Decodificador Base64 é a mesma foundation base64url que este decoder consome, e o Gerador de Hash é exatamente o que a família HMAC desta página chama por baixo. Para pipelines de CI que tocam credenciais, cole o segredo de assinatura ou a chave pública, rode o JWT e o verify panel diz se a assinatura está íntegra antes de você fazer deploy de uma rotação de chave.

Perguntas frequentes

Meu JWT ou segredo são enviados para algum lugar quando uso esta ferramenta?

Não. Cada passo — dividir os três segmentos, decodificar header e payload em base64url, parsear JSON, chamar SubtleCrypto.verify na assinatura — roda na aba do seu navegador. Não há chamada de rede no caminho de decode ou verify. A Content Security Policy deste site bloqueia fetch saindo de código de gerador. A string do JWT fica em localStorage sob a chave `recent-inputs:jwt-decoder:v1` para você recarregar tokens recentes, mas o segredo de verificação ou a chave pública PEM colada nunca é persistido. Um teste em runtime no repositório varre todo o localStorage depois de uma verify para garantir que essa promessa continua valendo entre refactors.

O que é alg=none e por que é perigoso?

Um JWT com `{"alg":"none"}` no header não carrega assinatura — o terceiro segmento é vazio. A RFC 7519 original permite isso em casos em que o canal já é autenticado, mas em produção é vulnerabilidade. Bibliotecas que "verificam" um token none pulando a checagem aceitam qualquer coisa; um atacante que consiga reescrever o payload dona a sessão. A RFC 8725 (JWT Best Current Practices BCP, fevereiro de 2020) §3.1 deprecia none explicitamente fora de fronteiras de confiança bem definidas. Este decoder mostra uma faixa vermelha INSECURE no instante em que detecta alg=none e recusa retornar veredito "valid" pelo painel de verify.

Posso confiar em um JWT decodificado sem verificar a assinatura?

Não. Decodificar apenas prova que os bytes são base64url bem formados e que header/payload são objetos JSON. Qualquer atacante que intercepte um token pode produzir um token que decodifica limpo para as claims que ele quiser — sem verificação você não tem prova de que aquelas claims vieram do emissor. Decode para debug e inspeção; verify antes de conceder acesso. É por isso que o widget separa os dois painéis e nunca faz verify automático: o resultado de verify é uma ação deliberada do usuário contra uma chave que o usuário fornece, não uma suposição silenciosa.

Qual é a diferença entre HS256, RS256 e ES256?

HS256 usa HMAC-SHA256 com um segredo compartilhado — os dois lados sabem a mesma string. RS256 usa RSASSA-PKCS1-v1_5 com SHA-256 contra um par de chaves RSA: o emissor assina com a privada, qualquer um com a pública verifica mas não assina. ES256 usa ECDSA na curva P-256 com SHA-256, também assimétrico mas com chaves e assinaturas substancialmente menores (tipicamente ~64 bytes contra ~256 para RSA-2048). A RFC 7518 §3 cataloga todos. A escolha depende do modelo de ameaça: HMAC para pares com confiança simétrica (microsserviços atrás do mesmo mesh), RSA/ECDSA quando o verificador não pode poder emitir tokens.

Por que o painel de verify pede um bloco PEM em vez de só a chave pública?

O Web Crypto SubtleCrypto.importKey aceita bytes de chave pública apenas no formato SubjectPublicKeyInfo (SPKI) DER. PEM é o invólucro de texto universal — DER em base64 entre marcadores `-----BEGIN PUBLIC KEY-----` — usado por openssl, AWS KMS, GCP Cloud KMS e pela maioria das CAs. O widget retira o invólucro PEM, decodifica o corpo em base64 e entrega os bytes DER para importKey. PEMs de chave privada (`-----BEGIN PRIVATE KEY-----` ou `-----BEGIN RSA PRIVATE KEY-----`) são rejeitados no parser — você não consegue colar acidentalmente uma chave de assinatura em um verificador.

Como o decoder lida com ataques de algorithm confusion?

O widget exibe o `alg` declarado, a família para a qual ele roteia (HMAC / RSA / RSA-PSS / ECDSA) e que entrada de chave ele espera (segredo cru para HMAC, PEM SPKI para as famílias assimétricas). Um token declarando `alg: HS256` mas colado em um contexto em que você esperava RS256 aparece como uma tentativa de verify com família divergente, em vez de um fall-through silencioso. É a mesma mitigação que a RFC 8725 §3.6 recomenda do lado do servidor: allow-list estrito por endpoint, sem confiança cega no algoritmo auto-declarado pelo token.

Por que o widget diz que meu token expirou quando meu servidor ainda aceita?

Expiração é conferida localmente — o widget compara `exp × 1000` com o relógio JavaScript no momento em que você abriu a aba. Não há negociação de drift de relógio. Se seu servidor aceita um JWT que este widget marca expirado por alguns segundos, a diferença é entre o relógio do seu servidor e o do emissor; deploys de produção tipicamente toleram de 30 a 120 segundos de drift por design. Se a diferença é de horas em vez de segundos, você tem um token genuinamente expirado e o verificador no servidor está mal configurado.

A ferramenta gera ou assina JWTs?

Não, por decisão deliberada de escopo. Esta é uma ferramenta de decode e verify — read-only por design. Não há UI de emissão, não há chamada `subtle.sign` em código de produção (só em arquivos de teste que constroem vetores de round-trip), não há caminho que aceite chave privada. A razão é posicionamento de segurança: uma ferramenta que assina tokens precisa segurar uma chave, o que cria superfície de ataque. Para testar fluxos de emissão de JWT, use sua biblioteca de autenticação localmente e cole o token resultante aqui para inspeção.