Como decodificar e verificar um JWT em JavaScript e Python
Decodificar um JWT é só ler o header e o payload, que são dados em Base64URL e podem ser lidos por qualquer pessoa. Verificar é conferir a assinatura com a chave, e só isso prova que o token é autêntico. Este guia mostra as duas coisas em JavaScript e Python, com os cuidados que evitam falhas de segurança.
As três partes de um JWT
Um JWT tem três partes separadas por ponto: header, payload e assinatura. As duas primeiras são objetos JSON codificados em Base64URL, uma variação do Base64 que usa - e _ no lugar de + e / e dispensa o preenchimento com =. Você pode ler qualquer uma delas com o decodificador de Base64. A terceira é a assinatura.
- Header: o algoritmo e o tipo, como {"alg":"HS256","typ":"JWT"}.
- Payload: os dados, chamados de claims, como sub, iat e exp.
- Assinatura: a prova de que o header e o payload não foram alterados.
Você pode ver as três partes de qualquer token no decodificador de JWT.
Decodificar um JWT em JavaScript
Decodificar não exige chave nenhuma, porque o conteúdo do token não é secreto.
function decodificarJWT(token) {
const [header, payload] = token.split('.');
const decodificar = (parte) => {
// Base64URL usa - e _ no lugar de + e /, e não tem o preenchimento com =
const base64 = parte.replace(/-/g, '+').replace(/_/g, '/');
const binario = atob(base64.padEnd(Math.ceil(base64.length / 4) * 4, '='));
const bytes = Uint8Array.from(binario, (c) => c.charCodeAt(0));
return JSON.parse(new TextDecoder().decode(bytes));
};
return { header: decodificar(header), payload: decodificar(payload) };
} Funciona no navegador e no Node.js, e entende acentos no payload.
Atenção: isso só lê o token. Nunca use o resultado para decidir se alguém está autorizado, porque qualquer pessoa consegue fabricar um token com o conteúdo que quiser.
Verificar a assinatura em Node.js (HS256)
No HS256, a assinatura é um HMAC-SHA256 do texto header.payload, calculado com um segredo compartilhado (você pode reproduzir esse cálculo no gerador de HMAC). Verificar é recalcular o HMAC e comparar:
import { createHmac, timingSafeEqual } from 'node:crypto';
function verificarHS256(token, segredo) {
const [header, payload, assinatura] = token.split('.');
// Aceite só o algoritmo que você espera, nunca o que o token diz
const cabecalho = JSON.parse(Buffer.from(header, 'base64url').toString());
if (cabecalho.alg !== 'HS256') throw new Error('Algoritmo não aceito');
const esperada = createHmac('sha256', segredo).update(`${header}.${payload}`).digest();
const recebida = Buffer.from(assinatura, 'base64url');
if (recebida.length !== esperada.length || !timingSafeEqual(recebida, esperada)) {
throw new Error('Assinatura inválida');
}
const dados = JSON.parse(Buffer.from(payload, 'base64url').toString());
if (dados.exp && dados.exp * 1000 <= Date.now()) throw new Error('Token expirado');
return dados;
} A função faz as quatro coisas que não podem faltar:
- Aceita só o algoritmo esperado, e não o que o token declara.
- Recalcula a assinatura com o segredo.
- Compara em tempo constante, com
timingSafeEqual. - Confere a expiração (
exp) depois de validar a assinatura.
Em produção, prefira uma biblioteca consolidada, como a jose, que também cobre outros algoritmos e as demais regras de validação.
Verificar a assinatura em Python
A mesma verificação, com a biblioteca padrão:
import base64
import hashlib
import hmac
import json
import time
def base64url(parte: str) -> bytes:
return base64.urlsafe_b64decode(parte + "=" * (-len(parte) % 4))
def verificar_hs256(token: str, segredo: str) -> dict:
header, payload, assinatura = token.split(".")
# Aceite só o algoritmo que você espera, nunca o que o token diz
if json.loads(base64url(header)).get("alg") != "HS256":
raise ValueError("Algoritmo não aceito")
esperada = hmac.new(segredo.encode(), f"{header}.{payload}".encode(), hashlib.sha256).digest()
if not hmac.compare_digest(esperada, base64url(assinatura)):
raise ValueError("Assinatura inválida")
dados = json.loads(base64url(payload))
if "exp" in dados and dados["exp"] <= time.time():
raise ValueError("Token expirado")
return dados O que conferir além da assinatura
exp: o token não expirou.nbf: o token já pode ser usado, quando o campo existir.iss: quem emitiu é quem você espera.aud: o token foi feito para a sua aplicação.- O algoritmo: aceite uma lista fixa, nunca
nonenem o que vier no header.
Erros que causam falhas de segurança
- Aceitar
alg: none: códigos antigos aceitavam tokens sem assinatura quando o header pedia. Fixe o algoritmo no servidor. - Colocar dados sigilosos no payload: ele pode ser lido por qualquer pessoa.
- Comparar assinaturas com
==: use uma comparação em tempo constante. - Guardar o token onde scripts da página conseguem ler (como o localStorage) em aplicações expostas a XSS: cookies
HttpOnlyreduzem esse risco.
Achou um erro neste guia? Avise por e-mail.
Ferramentas para usar junto
- Decodificador JWTDecodifique um JWT online e veja header, payload e datas de exp, iat e nbf
- Gerador de HMACGere HMAC-SHA256, SHA1, SHA384, SHA512 ou MD5 com uma chave secreta e confira assinaturas de webhook
- Decodificar Base64Decodifique Base64 para texto online, com suporte a UTF-8 e Base64 URL-safe
Perguntas frequentes
Decodificar um JWT é o mesmo que verificar?
Não. Decodificar só lê o conteúdo, que não é secreto. Verificar confere a assinatura com a chave e é o que prova que o token é autêntico. Só se deve confiar nos dados de um token depois de verificado.
O JWT é criptografado?
Em geral, não. O JWT mais comum é apenas assinado (JWS): o conteúdo é legível por qualquer pessoa, e a assinatura só garante que ele não foi alterado. Por isso não se deve colocar senhas ou dados sigilosos no payload.
Qual a diferença entre HS256 e RS256?
No HS256, a mesma chave secreta assina e verifica, então quem verifica também poderia assinar. No RS256, a assinatura usa uma chave privada e a verificação usa a chave pública, o que permite que vários serviços verifiquem tokens sem poder emiti-los.
Posso colar um token de produção no decodificador do PraDev?
O decodificador roda no navegador e não envia o token a nenhum servidor, mas um token ativo funciona como uma senha temporária. Prefira tokens de teste ou já expirados.