Assinar e verificar JWTs
Um JWT são três segmentos base64url — cabeçalho, payload, assinatura — e os dois primeiros podem ser lidos por qualquer pessoa que tenha o token. Descodificar um diz-lhe o que ele afirma; só o terceiro segmento diz se deve acreditar. Este artigo é sobre esse terceiro segmento: como uma assinatura é feita, quem a pode fazer, como os verificadores a conferem e o ponto exato onde "verificado" deixa de significar "confiável". Pode executar cada passo você mesmo no assinador e verificador de JWT, que assina e verifica localmente no seu navegador.
Descodificar não é verificar
O mal-entendido mais comum sobre JWTs é também o mais perigoso: que ler um token é o mesmo que verificá-lo. Não é. O cabeçalho e o payload são base64url — uma codificação, não cifragem — por isso qualquer pessoa que intercepte um token, o encontre num registo ou o copie do armazenamento local de um navegador pode descodificá-lo em segundos, sem chave nenhuma. As claims lá dentro (para quem é, quando expira, que papéis afirma) são afirmações, e afirmações são baratas.
A assinatura existe precisamente porque as claims são baratas. É uma ligação criptográfica entre o cabeçalho, o payload e uma chave que só o emissor detém: mude um carácter do payload e a assinatura deixa de corresponder. Verificar é o ato de repetir essa matemática — recalcular a assinatura sobre o cabeçalho e o payload recebidos, com a chave do emissor, e comparar. Se corresponder, o token é exatamente como o emissor o assinou; se não corresponder, alguma coisa mudou, e o token não vale nada, por mais plausíveis que as suas claims pareçam.
Simétrico vs. assimétrico: quem pode emitir um token
Com HS256, o segredo que assina é o mesmo segredo que verifica. Isso torna o HMAC rápido, simples e totalmente adequado dentro de um serviço — a sua API assina os seus próprios tokens e verifica-os ela mesma, e o segredo nunca sai do edifício. No momento em que uma segunda parte precisa de verificar, o modelo quebra-se: entregar-lhe o segredo é entregar-lhe o poder de emitir tokens, porque verificar e assinar são a mesma operação. Um segredo HMAC vazado não é um problema de verificação; é um problema de falsificação.
Os algoritmos assimétricos separam os dois poderes. RS256, ES256 e EdDSA assinam com uma chave privada e verificam com uma chave pública, por isso o emissor pode publicar as suas chaves públicas — como um JWKS — e todos os verificadores do mundo podem verificar tokens sem nunca deterem o poder de forjar um. Essa separação é o que faz os JWTs funcionarem entre serviços: a sua API confia em tokens de um fornecedor de identidade porque confia nas chaves publicadas por esse fornecedor, não porque partilhe um segredo com ele. O preço é a complexidade — formatos de chave, rotação, correspondência de kid — que é o que o assinador e verificador deste site existe para tornar concreto.
O ataque de confusão de algoritmo
O ataque clássico a JWT não precisa de criptografia quebrada — precisa de um verificador que confie no token para nomear o seu próprio algoritmo. O atacante pega num token RS256 legítimo, cuja chave pública é publicada (como as chaves públicas são), e volta a assinar o mesmo payload com HS256, usando a própria chave pública como segredo HMAC. Um verificador que lê o alg do cabeçalho e escolhe a sua rotina de verificação em conformidade vai agora "verificar" o token forjado com HMAC, usando a chave pública como segredo — e a chave pública é pública.
A defesa é estrutural, não um conselho: o verificador tem de fixar os algoritmos que aceita, e a família da chave tem de corresponder à família do algoritmo — uma chave RSA nunca pode verificar um token HMAC, e um segredo nunca pode verificar um token RSA. Um verificador que reinterpreta uma chave entre famílias está quebrado por design, por mais correta que seja a sua criptografia. É por isso que o verificador deste site recusa chaves de outra família com um aviso explícito, em vez de tentar a verificação: a recusa é a funcionalidade.
Como são, na prática, o JWKS e a rotação de kid
Um fornecedor de identidade não entrega uma chave eterna; publica um conjunto — um JWKS, geralmente em /.well-known/jwks.json — e roda as chaves adicionando uma nova e, após um período de graça, removendo a antiga. O cabeçalho de cada token traz um kid que nomeia a chave que o assinou, por isso um verificador busca o conjunto, escolhe a chave correspondente e verifica. Os tokens assinados com a chave antiga continuam a funcionar até ela ser removida, e é isso que torna a rotação segura em vez de um dia de bandeira.
Os detalhes operacionais são onde tudo quebra. Os verificadores têm de guardar o JWKS em cache (buscá-lo a cada pedido é uma interrupção auto-infligida), lidar com o caso em que um kid é desconhecido — geralmente uma rotação que a cache ainda não viu, por isso volte a buscar antes de falhar — e nunca aceitar um conjunto de chaves de um URL em que não confiam: o conjunto só é tão confiável quanto o canal por onde veio, e é por isso que os endpoints JWKS vivem atrás de HTTPS e que o construtor de JWKS aqui deriva cada kid como uma impressão digital RFC 7638 da própria chave, para que o id não possa ser falsificado independentemente da chave.
exp, nbf, iat — e o problema do relógio
Uma assinatura prova que um token foi assinado; não diz nada sobre se ele ainda deve ser honrado. Esse é o trabalho das claims de tempo: exp (quando deixa de ser válido), nbf (quando começa a ser válido), iat (quando foi emitido). Um verificador que confere a assinatura e ignora o relógio aceita um token para sempre — incluindo um roubado há um ano. Vidas curtas são a resposta prática: tokens de acesso medidos em minutos, tokens de atualização a carregar a confiança de longa duração, e a revogação resolvida simplesmente por não se voltar a emitir.
Os relógios discordam, por isso os verificadores permitem uma pequena folga — tipicamente um ou dois minutos de tolerância em exp e nbf — mas a folga é uma tolerância para a deriva, não uma licença para ignorar a expiração. Um dispositivo cujo relógio está minutos adiantado ou atrasado vai produzir tokens que o seu próprio servidor rejeita, e um token com um iat no futuro é um sinal de falsificação, não um erro de arredondamento. A mesma disciplina de relógio aplica-se às ferramentas: o verificador confere a expiração com o relógio que você pode ver e diz claramente quando um token expirou, em vez de apenas "inválido".
O que "verificado" prova e o que não prova
Um token verificado prova três coisas e nada mais: o cabeçalho e o payload são exatamente como foram assinados, quem assinou detinha a chave, e o token está dentro da sua janela de validade. Não prova que o emissor é quem você pensa que é — isso depende de onde veio a chave, e uma chave buscada num URL controlado por um atacante verifica os tokens do atacante na perfeição. Não prova que as claims são verdadeiras — uma assinatura autentica a declaração, não os factos por trás dela. E não prova que o token não foi roubado: um token válido apresentado por qualquer pessoa é aceite, e é por isso que os tokens têm vida curta e âmbito limitado.
Por isso a decisão de confiança vive fora do token. A sua API confia num token porque buscou a chave no URL bem conhecido do seu fornecedor de identidade por HTTPS, porque o iss corresponde ao fornecedor que configurou e porque o aud corresponde ao serviço que é. A verificação é a última verificação, não a primeira: proveniência da chave, emissor, audiência, depois assinatura, depois tempo. Uma ferramenta que diz "VÁLIDO" sem esse contexto está a dizer a verdade sobre a matemática e nada sobre a confiança — e é por isso que o verificador deste site o diz na página, ao lado do veredicto.
Perguntas frequentes
Posso verificar um token sem a chave?
Não — a verificação precisa da chave pública do emissor (ou do segredo partilhado, no caso do HMAC). Descodificar não precisa de chave, e é exatamente essa a confusão a evitar: um token descodificado é legível, não verificado. Se o emissor publicar um JWKS, busque-o no URL bem conhecido e faça corresponder o kid do token; o verificador deste site faz isso por você.
Por que o meu token RS256 falha com o meu segredo?
Porque um segredo e uma chave RSA são famílias diferentes, e um verificador sólido recusa-se a misturá-las. Os tokens RS256 são verificados com a chave pública RSA do emissor — PEM, JWK ou JWKS — nunca com um segredo de texto. Usar um segredo seria o ataque de confusão de algoritmo: a chave pública é pública, por isso qualquer pessoa poderia forjar tokens se a verificação HMAC a aceitasse como segredo.
Quanto tempo deve viver um JWT?
Minutos, não dias, para tokens de acesso: 5 a 15 minutos é o intervalo comum, com um token de atualização a carregar a sessão mais longa. Uma vida curta limita o dano de um token roubado à janela antes de ele expirar, e faz da revogação uma questão de não voltar a emitir, em vez de manter uma lista de bloqueio. Tokens que vivem semanas são cookies de sessão com ferramentas piores.