O que é JWT? JSON Web Tokens explicados com um exemplo prático
Um JWT permite que um servidor confie em uma solicitação sem procurar nada. Construa um do zero, tente alterá-lo e observe-o falhar, depois aprenda quando os tokens vencem as sessões e como usá-los com segurança.
- Um JWT é um token compacto e assinado com três partes: cabeçalho, carga útil e assinatura.
- A assinatura prova que um token não foi alterado, portanto o servidor pode confiar nele sem precisar consultá-lo.
- A carga é legível por qualquer pessoa, portanto, nunca coloque segredos nela.
- Os tokens são difíceis de revogar, portanto, mantenha-os de curta duração e verifique-os com uma biblioteca confiável.
Depois de fazer login, como um servidor sabe que cada solicitação posterior é realmente sua? Uma resposta popular é um JSON Web Token, ou JWT, geralmente pronunciado como “jot”. É um pequeno dado assinado que o cliente envia com cada solicitação, e o servidor pode verificá-lo sem tocar no banco de dados.
A maneira mais rápida de entender um JWT é construir um e depois tentar quebrá-lo. Fizemos isso usando apenas a biblioteca padrão do Python, no Python 3.13, e a saída abaixo vem dessa execução. Este código é para aprendizado. Em uma aplicação real, use uma biblioteca bem mantida, porque é fácil errar nos detalhes da verificação.
O que é um JWT?
JWT é um padrão aberto, definido na RFC 7519, para passar declarações entre duas partes como uma string compacta. Uma reivindicação é simplesmente uma declaração, como “o ID deste usuário é 42”. Como o token está assinado, o destinatário pode confirmar quem o emitiu e se ninguém o alterou.
Pense em uma pulseira de festival. A equipe no portão verifica seu ingresso uma vez e lhe dá uma banda difícil de falsificar. Depois disso, cada barraca fica apenas olhando a banda, sem necessidade de ligar para a bilheteria. Um JWT funciona da mesma maneira que uma API.
Como funciona o login JWT
- O usuário envia um nome de usuário e uma senha para o endpoint de login.
- O servidor os verifica e cria um token assinado.
- O cliente armazena o token.
- Cada solicitação posterior inclui-o, normalmente em um cabeçalho:
Authorization: Bearer <token>. - O servidor verifica a assinatura e a expiração e, em seguida, lê as declarações.
O principal benefício está na etapa 5. O servidor não precisa procurar uma sessão, o que é conveniente quando muitos serviços ou servidores precisam verificar o mesmo token. Se você é novo em APIs, comece com o que é uma API.
As três partes de um JWT
Um JWT parece um texto aleatório, mas consiste em três seções codificadas em Base64URL separadas por pontos. Aqui está um que geramos:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJzdWIiOiI0MiIsInJvbGUiOiJ1c2VyIiwiaWF0IjoxNzkzNTAwMDAwLCJleHAiOjE3OTM1MDA5MDB9
.7YfUDH_T-iDh8bMmUT8mmKYwOstzT8Ty--DTTAmQGWM
Adicionamos quebras de linha para facilitar a leitura. A decodificação das duas primeiras partes fornece JSON simples:
Header: {"alg":"HS256","typ":"JWT"}
Payload: {"sub":"42","role":"user","iat":1793500000,"exp":1793500900}
| Parte | Contém | Objetivo |
|---|---|---|
| Cabeçalho | Tipo de token e algoritmo de assinatura | Diz como verificar o token |
| Carga útil | Reivindicações sobre o usuário ou sessão | A informação real |
| Assinatura | Um hash das duas primeiras partes, usando uma chave | Prova integridade e origem |
Reivindicações padrão
| Reivindicar | Nome | Significado |
|---|---|---|
substituto |
Assunto | Sobre quem se trata o token, geralmente um ID de usuário |
isso |
Emissor | Quem criou o token |
aud |
Público | A quem se destina o token |
experiência |
Expiração | Quando o token deixa de ser válido |
isso |
Emitido em | Quando foi criado |
nbf |
Não antes | Quando começa a ser válido |
jti |
ID do JWT | Um ID exclusivo para este token |
Você também pode adicionar declarações personalizadas, como role. Os tempos são carimbos de data/hora Unix, em segundos. Mantenha a carga útil pequena, pois o token viaja com cada solicitação.
Construa um você mesmo
A criação de um token envolve a codificação das duas partes JSON e, em seguida, assiná-las com um segredo usando HMAC-SHA256:
import base64, hashlib, hmac, json
SECRET = b"demo-secret-do-not-use-in-production"
def b64url(data):
return base64.urlsafe_b64encode(data).rstrip(b"=").decode()
def sign(message):
return b64url(hmac.new(SECRET, message.encode(), hashlib.sha256).digest())
def create_token(payload):
header = {"alg": "HS256", "typ": "JWT"}
parts = [
b64url(json.dumps(header, separators=(",", ":")).encode()),
b64url(json.dumps(payload, separators=(",", ":")).encode()),
]
message = ".".join(parts)
return message + "." + sign(message)
A verificação funciona recalculando a assinatura e comparando-a com a do token. Somente alguém com o segredo pode produzir um correspondente:
def verify_token(token, now):
header, payload, signature = token.split(".")
expected = sign(header + "." + payload)
if not hmac.compare_digest(signature, expected):
raise ValueError("bad signature")
claims = json.loads(b64url_decode(payload))
if claims["exp"] < now:
raise ValueError("token expired")
return claims
Um token válido retornou suas reivindicações conforme esperado. compare_digest é utilizado porque roda em tempo constante, o que evita vazamento de informações por diferenças de tempo.
Tente mexer com isso
Suponha que um invasor decodifique a carga útil, altere "role":"user" para "role":"admin", codifique-a novamente e mantenha a assinatura antiga. Tentamos isso, junto com outros dois ataques:
| Tentativa | Resultado |
|---|---|
| Mudou a função para administrador | assinatura incorreta |
| Usou o token 1 segundo após expirar | token expirou |
Defina "alg":"none" e remova a assinatura |
assinatura incorreta |
Esta última tentativa visa uma verdadeira fraqueza histórica. Algumas bibliotecas já confiaram no próprio cabeçalho do token, então um invasor poderia simplesmente declarar que nenhuma assinatura era necessária. Nosso código sempre usa um algoritmo fixo, por isso foi rejeitado. Em uma biblioteca real, sempre especifique os algoritmos que você aceita.
Assinado, não criptografado
Este é o ponto mais incompreendido sobre os JWTs. Nosso token forjado foi rejeitado, mas seu conteúdo ainda era perfeitamente legível:
{"sub":"42","role":"admin","iat":1793500000,"exp":1793500900}
Base64URL é uma codificação, não uma criptografia, e qualquer pessoa pode revertê-la sem nenhuma chave. A assinatura impede que as pessoas alterem os dados, mas não os leiam. Portanto, nunca coloque senhas, números de cartão ou dados pessoais privados em uma carga JWT. Se o conteúdo precisar ser oculto, use tokens criptografados (JWE) ou apenas mantenha os dados no servidor.
HS256 versus RS256
| Aspecto | HS256 | RS256 ou ES256 |
|---|---|---|
| Tipo | Simétrico | Assimétrico |
| Chaves | Um segredo compartilhado | Uma chave privada assina, uma chave pública verifica |
| Quem pode criar tokens | Qualquer um que guarde o segredo | Somente o detentor da chave privada |
| Melhor para | Um serviço ou um pequeno grupo confiável | Muitos serviços, terceiros, provedores de identidade |
Com a assinatura assimétrica, outros serviços podem verificar tokens sem serem capazes de cunhar os seus próprios, o que limita os danos caso um deles seja comprometido.
JWT versus sessões
Os JWTs são frequentemente apresentados como um substituto moderno para sessões. Na realidade, cada um tem vantagens e desvantagens:
| Aspecto | Sessão do servidor | JWT |
|---|---|---|
| Onde o estado mora | No servidor | No token |
| Pesquisa em cada solicitação | Normalmente | Não é necessário |
| Saindo ou revogando | Fácil, exclua a sessão | Difícil antes de expirar |
| Tamanho por solicitação | ID pequeno | Token maior (o nosso tinha 161 caracteres) |
| Em muitos serviços | Precisa de armazenamento compartilhado | Cada um pode verificá-lo diretamente |
| Alterando permissões | Aplica-se imediatamente | O token antigo permanece válido até expirar |
Para um site tradicional renderizado por servidor, um cookie de sessão costuma ser mais simples e seguro. JWTs tendem a fazer mais sentido para APIs consumidas por diversos serviços ou para clientes móveis.
Dica: Não escolha JWT só porque parece sem estado. Muitos sistemas reais adicionam uma lista de bloqueios ou uma verificação de banco de dados para dar suporte ao logout, o que traz de volta a pesquisa que o JWT deveria evitar.
Onde armazenar tokens em um navegador
| Localização | Risco XSS | Risco de CSRF | Notas |
|---|---|---|---|
armazenamento local |
Alto, pois os scripts podem lê-lo | Baixo | Simples, mas exposto a qualquer script injetado |
| Cookie somente http | Inferior, pois os scripts não podem lê-lo | Precisa de proteção | Use Secure e SameSite |
| Variável na memória | Inferior | Baixo | Perdido na atualização, por isso precisa de um fluxo de atualização |
Muitos guias de segurança favorecem HttpOnly, Secure, SameSite cookies para aplicativos da web. Seja qual for sua escolha, sempre use HTTPS. Para enviar um token de JavaScript, consulte nosso tutorial da API JavaScript Fetch.
Atualizar tokens
A expiração curta limita o dano causado por um token roubado, mas ninguém quer fazer login a cada 15 minutos. A solução usual usa dois tokens:
- Token de acesso: de curta duração, como minutos, e enviado com cada solicitação de API.
- Token de atualização: tem vida mais longa, é armazenado com mais cuidado e é usado apenas para obter novos tokens de acesso.
O servidor monitora os tokens de atualização, para que possa revogá-los, o que proporciona um logout prático. Girá-los a cada uso também ajuda a detectar roubo, porque um token antigo roubado é reproduzido novamente.
Erros comuns de segurança
- Decodificação sem verificação. Ler uma carga útil não a torna confiável. Verifique sempre a assinatura.
- Aceitar qualquer algoritmo. Defina uma lista explícita de algoritmos permitidos.
- Usando um segredo fraco. Segredos curtos podem ser decifrados offline. Use um valor aleatório longo e mantenha-o fora do controle de origem.
- Ignorando a expiração. Tokens sem
exppodem funcionar para sempre se roubados. - Não verificar
isseaud. Caso contrário, um token emitido para um serviço poderá ser aceito por outro. - Confiar em declarações obsoletas. Uma função dentro de um token pode ficar desatualizada até expirar.
- Tokens de registro. Qualquer pessoa que leia seus registros poderá reutilizá-los.
- Não testar caminhos de falha. Adicione testes para tokens expirados e modificados, bem como para tokens válidos. Nosso guia de teste de API com Postman pode ajudar.
Considerações finais
Um JWT são dados assinados nos quais um servidor pode confiar sem uma pesquisa, não um contêiner secreto e não uma atualização automática durante as sessões. Use prazos curtos, verifique tudo com uma biblioteca mantida e mantenha os dados privados fora da carga útil. Para ver por si mesmo, execute um pequeno script como o nosso, altere um caractere de um token e observe a falha na verificação.
Perguntas frequentes
Para que é usado um JWT?
JWTs são usados principalmente para autenticação e autorização em APIs. Após o login, um cliente envia o token com cada solicitação e o servidor o verifica para identificar o usuário.
Um JWT é criptografado?
Geralmente não. Um JWT padrão é assinado e codificado, para que qualquer pessoa possa ler sua carga útil. A assinatura apenas impede alterações. Os tokens criptografados usam um formato relacionado chamado JWE.
Quanto tempo deve durar um JWT?
Os tokens de acesso geralmente têm vida curta, geralmente minutos em vez de dias. Combine-os com um token de atualização, para que os usuários não precisem fazer login constantemente.
Um JWT pode ser revogado?
Não por si só, pois permanece válido até expirar. A revogação requer rastreamento do lado do servidor, como uma lista de bloqueios, que remove alguns dos benefícios sem estado.
O JWT é melhor do que sessões?
Nem sempre é melhor. As sessões são mais simples de revogar e adequadas para muitos sites, enquanto os JWTs são adequados para APIs e serviços que precisam verificar a identidade de forma independente.
Continue aprendendo
Precisa de ajuda com testes, WordPress ou SEO?
Ajudamos as equipes a fornecer software melhor e a expandir seus sites. Conte-nos no que você está trabalhando e sugeriremos um próximo passo claro.
Mais guias sobre testes, IA e desenvolvimento moderno
Gostou deste artigo? Escolha um tópico e vá direto para nossos tutoriais mais recentes.
Explicação das alucinações de IA: por que os modelos de linguagem inventam as coisas e como reduzi-las
Explicação das alucinações de IA: o que são, por que os modelos de linguagem as produzem, tipos comuns, como reduzi-las com aterramento e instruções…
Leia o artigo →DevOpsVariáveis de ambiente explicadas: arquivos .env, segredos e configuração bem feitos
Variáveis de ambiente explicadas com exemplos de Python que executamos: leitura e configuração, por que cada valor é uma string, arquivos .env, hera…
Leia o artigo →Desenvolvimento de softwareExplicação da limitação de taxa de API: algoritmos, cabeçalhos e tratamento de erros 429
Limitação de taxa de API explicada com uma simulação que executamos: token bucket, janela fixa, janela deslizante, 429 respostas, cabeçalhos de limi…
Leia o artigo →Automação de testesZombando em Python: unittest.mock, patch e os erros que fazem os testes mentirem
Zombaria em Python com unittest.mock, testado: Mock e return_value, side_effect, patch onde um nome é usado, especificação e autospec, zombaria de d…
Leia o artigo →Teste de softwareCobertura de código explicada: cobertura de linha versus filial e o que 100% realmente significa
Cobertura de código explicada com uma execução cobertura.py real: cobertura de linha, ramal e condição, leitura de um relatório, por que 100% de cob…
Leia o artigo →Bancos de dados e SQLDELETE vs TRUNCATE vs DROP em SQL: diferenças, reversão e quando usar cada um
DELETE vs TRUNCATE vs DROP explicado com SQL testado: o que cada um remove, rollback, aciona, redefinição de identidade, velocidade e como PostgreSQ…
Leia o artigo →Bancos de dados e SQLExibições SQL explicadas: crie, use e evite as armadilhas, com exemplos
Visualizações SQL explicadas com consultas que executamos: criação de uma visualização, resultados ao vivo, ocultação de colunas, desempenho, visual…
Leia o artigo →Desenvolvimento de softwareInterface TypeScript vs tipo: diferenças reais, testadas com o compilador
Interface TypeScript versus tipo, verificada com o compilador real: o que ambos podem fazer, o que apenas um pode fazer, mesclagem de declarações, e…
Leia o artigo →Desenvolvimento de softwarePython *args e **kwargs explicados: funções flexíveis com exemplos claros
Python *args e **kwargs explicados com o código que executamos: empacotamento e descompactação, ordem dos parâmetros, argumentos somente de palavras…
Leia o artigo →Desenvolvimento de softwareFunções Python Lambda: sintaxe, usos reais e quando não usá-los
Funções lambda do Python explicadas com o código que executamos: sintaxe, classificação com chave, mapa e filtro, fechamentos, armadilha de ligação …
Leia o artigo →Inteligência artificialO que são agentes de IA? Como eles funcionam, com um loop de agente simples que você pode executar
O que são agentes de IA? Um guia simples para o loop do agente, ferramentas, memória e proteções, com um pequeno loop Python que executamos, riscos …
Leia o artigo →DevOpsMonitoramento versus observabilidade: a verdadeira diferença, com logs, métricas e rastreamentos
Explicação do monitoramento versus observabilidade: falhas conhecidas e desconhecidas, logs, métricas e rastreamentos, um passo a passo do incidente…
Leia o artigo →DevOpsDocker Volumes vs Bind Mounts: persista os dados do contêiner da maneira certa
Volumes Docker versus montagens de ligação explicadas: por que os dados do contêiner desaparecem, volumes nomeados, montagens de ligação, tmpfs, exe…
Leia o artigo →Automação de testesTDD vs BDD: principais diferenças, um recurso criado de ambas as maneiras e quando usar cada um
TDD vs BDD explicado com um recurso de desconto construído em ambos os sentidos: refatoração vermelho-verde com o código que executamos, cenários da…
Leia o artigo →Teste de softwareTeste de aceitação do usuário (UAT): processo, lista de verificação e exemplos reais
Explicação do teste de aceitação do usuário: quem o executa, como ele difere do controle de qualidade, um processo de sete etapas, um modelo de caso…
Leia o artigo →Bancos de dados e SQLValores SQL NULL explicados: IS NULL, COALESCE, NULLIF e as armadilhas a serem evitadas
Valores SQL NULL explicados com consultas que executamos: lógica de três valores, IS NULL, COALESCE, NULLIF, como agregações, junções e classificaçã…
Leia o artigo →Bancos de dados e SQLInstrução SQL CASE WHEN: sintaxe, exemplos reais e erros comuns
SQL CASE WHEN explicado com consultas que executamos: formulários simples e pesquisados, ordem de condição, pivôs com SUM, truques COUNT, classifica…
Leia o artigo →Desenvolvimento de softwareO que é JWT? JSON Web Tokens explicados com um exemplo prático
O que é JWT? JSON Web Tokens explicados: as três partes, um token construído e adulterado no código que executamos, JWT vs sessões, armazenamento, t…
Leia o artigo →Desenvolvimento de softwareLoop de eventos JavaScript explicado: pilha de chamadas, microtarefas e temporizadores com exemplos
O loop de eventos JavaScript explicado com o código que executamos: pilha de chamadas, filas de tarefas e microtarefas, por que setTimeout 0 não é i…
Leia o artigo →Desenvolvimento de softwareGeradores Python e rendimento explicado: economize memória com exemplos reais
Geradores Python e rendimento explicados com o código que executamos: como eles pausam e reiniciam, medem economia de memória, pipelines, sequências…
Leia o artigo →Inteligência artificialO que são incorporações? Similaridade de vetores e cossenos explicada de forma simples
O que são incorporações? Um guia em linguagem simples para vetores, similaridade de cossenos e pesquisa semântica, com um pequeno exemplo que execut…
Leia o artigo →DevOpsImplantação azul-verde vs canário: diferenças, compensações e como escolher
Explicação da implantação azul-verde versus canário: como cada um funciona, custo, risco e reversão, agrupamento estável de usuários que testamos, m…
Leia o artigo →Desenvolvimento de softwareGit merge vs rebase: diferenças, exemplos e quando usar cada um
Git merge vs rebase explicado com saída real do log, uma tabela comparativa, a regra de ouro do rebase e um fluxo de trabalho seguro para equipes.
Leia o artigo →Desenvolvimento de softwareList comprehension em Python: sintaxe, exemplos e quando evitar
List comprehension em Python explicada: sintaxe, filtros, if-else, laços aninhados, tempos medidos e quando um laço for comum é a melhor escolha.
Leia o artigo →Bancos de dados e SQLSQL JOIN explicado com exemplos (Inner, Left, Right, Full)
Entenda os joins SQL passo a passo com tabelas e consultas de exemplo: INNER, LEFT, RIGHT, FULL, CROSS e SELF, além de erros comuns e dicas de desem…
Leia o artigo →