Verificar a assinatura de um JSON Web Token (JWT)

verifica a assinatura de um JWT (HS256, HS384, HS512, RS256, RS384, RS512) a partir de um segredo ou de uma chave pública, e inspeciona as suas claims
Para HS256 / HS384 / HS512: a cadeia secreta. Para RS256 / RS384 / RS512: a chave pública em formato PEM.

Porquê verificar a assinatura de um JWT?

Um JSON Web Token (JWT) decompõe-se em três partes separadas por pontos: header.payload.signature. Descodificar um JWT consiste simplesmente em ler as duas primeiras partes (que são Base64URL). Qualquer pessoa pode fazê-lo, e qualquer pessoa pode fabricar um JWT com o payload à sua escolha. O que torna um JWT digno de confiança é unicamente a assinatura: sem verificação, aceitar um JWT é como deixar entrar em casa qualquer pessoa que afirme ser alguém, sem pedir documento de identidade.

Esta ferramenta verifica a assinatura de um JWT a partir de uma chave. Não se limita a descodificar: recalcula a assinatura a partir do header, do payload e da sua chave, e depois compara com a assinatura do token. Se as duas coincidirem, o token é autêntico. Caso contrário, foi forjado, modificado, ou assinado com outra chave.

A verificação é a pedra angular de qualquer arquitetura que utilize JWT para autenticação ou autorização: sem assinatura válida, o payload pode mentir. Um atacante que modificasse "role":"user" para "role":"admin" não teria qualquer dificuldade em fazê-lo se o servidor verificasse apenas o formato do token e não a sua assinatura.

Algoritmos comuns

A especificação JWT (RFC 7518, JSON Web Algorithms) define várias famílias de algoritmos. Eis os mais utilizados em produção:

  • HMAC (HS256, HS384, HS512): assinatura simétrica à base de HMAC-SHA. A mesma chave secreta serve para assinar e verificar. Simples de implementar, performante, mas qualquer parte capaz de verificar o token também é capaz de o emitir. Adequado a cenários onde o emissor e o verificador são a mesma equipa ou o mesmo serviço.
  • RSA (RS256, RS384, RS512): assinatura assimétrica. A chave privada assina, a chave pública verifica. Ideal quando o emissor e os verificadores são entidades distintas (OAuth2, OpenID Connect, federação de identidade). É o algoritmo privilegiado pela maioria dos fornecedores de identidade públicos.
  • ECDSA (ES256, ES384): assinatura assimétrica em curvas elípticas. Mesma lógica que RSA (chave privada para assinar, chave pública para verificar) mas com chaves e assinaturas mais compactas para um nível de segurança equivalente. Cada vez mais difundido nas arquiteturas modernas.

Como fornecer a chave

O formato da chave esperada depende do algoritmo declarado no header do JWT:

  • HS256, HS384, HS512: a chave é uma cadeia secreta (string). É o segredo partilhado com o emissor, frequentemente armazenado numa variável de ambiente como JWT_SECRET. Sem formatação particular, apenas o valor bruto.
  • RS256, RS384, RS512: a chave é uma chave pública RSA em formato PEM, que começa por -----BEGIN PUBLIC KEY----- e termina por -----END PUBLIC KEY-----. Conserve as quebras de linha tal como estão, caso contrário o OpenSSL recusa fazer parse.
  • ES256, ES384: a chave é uma chave pública ECDSA em formato PEM, na curva correspondente (P-256 para ES256, P-384 para ES384).

Exemplo de chave pública RSA esperada

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxe...
...vQIDAQAB
-----END PUBLIC KEY-----

Como funciona a verificação?

O nosso servidor reproduz exatamente a operação que o emissor fez:

  1. Separa o JWT em header, payload e signature.
  2. Concatena base64url(header) + "." + base64url(payload).
  3. Para HMAC, calcula um HMAC-SHA-256/384/512 com a sua chave secreta, depois compara com a assinatura recebida através de hash_equals (comparação em tempo constante para evitar ataques temporais).
  4. Para RSA, chama openssl_verify com a sua chave pública em formato PEM.

Casos de uso

  • Debug de autenticação de API: recebe um 401, verifique se o seu token está mesmo assinado com a chave esperada.
  • Validação de um token recebido de um fornecedor: um parceiro (Auth0, Keycloak, Cognito, Okta) envia-lhe JWTs assinados em RS256; quer confirmar que vêm mesmo dele com a sua chave pública.
  • Auditoria de segurança: verificar que um serviço terceiro assina corretamente os seus tokens com um algoritmo robusto, e não em HS256 com um segredo fraco.
  • Testes manuais: verificar que um JWT gerado pelo seu código passa bem a verificação com a chave pública fornecida.
  • Verificação rápida de um token recebido: durante a integração de um SSO ou de uma API parceira, verificar em segundos que a cadeia assinatura/chave funciona antes de escrever a primeira linha de código.

Como utilizar a ferramenta

  1. Cole o JWT completo (as três partes separadas por pontos).
  2. Indique a chave adequada ao algoritmo do header:
    • Para HS256, HS384 ou HS512, a chave é a cadeia secreta partilhada com o emissor. É uma cadeia livre, frequentemente armazenada numa variável de ambiente como JWT_SECRET.
    • Para RS256, RS384 ou RS512, a chave é a chave pública em formato PEM, que começa por -----BEGIN PUBLIC KEY----- e termina por -----END PUBLIC KEY-----.
  3. Inicie a verificação. A ferramenta mostra o estado (válido ou inválido) e o payload descodificado.

Armadilhas comuns a evitar

  • Algoritmo "none": a spec permite alg: none, que significa "sem assinatura". Uma falha clássica consiste em fabricar um token com este header esperando que o servidor o aceite. A nossa ferramenta rejeita sistematicamente os tokens com alg: none.
  • Confusão HMAC vs RSA (algorithm confusion): um atacante muda o algoritmo RS256 para HS256 e assina o payload com a chave pública RSA utilizada como segredo HMAC. Se o servidor não controlar o algoritmo, aceita o token. Bloqueie sempre o algoritmo esperado no servidor.
  • Segredos HMAC em código: um segredo commitado num repositório Git torna toda a confiança dos tokens caduca. Armazene os segredos em variáveis de ambiente ou num cofre aplicacional.
  • Chave pública vs chave privada: para verificar um JWT assinado em RSA ou ECDSA, fornece-se a chave pública, nunca a privada. A privada só serve para assinar e nunca deve sair do emissor.
  • Expiração ignorada: uma assinatura válida num token expirado nunca deve ser aceite. Pense em verificar exp e nbf.
  • Audiência não controlada: um token destinado à API A não deveria ser aceite pela API B. Verifique a claim aud.

Claims temporais: exp e nbf

Para além da assinatura, um JWT válido deve também respeitar as suas restrições temporais:

  • exp (expiration): o token deixa de ser válido após esta data.
  • nbf (not before): o token ainda não é válido antes desta data.

A nossa ferramenta sinaliza explicitamente quando um token está expirado ou ainda não válido, mesmo que a sua assinatura esteja correta. É importante: uma assinatura válida num token expirado nunca deve ser aceite em produção.

Diferença com o nosso JWT decoder

O nosso JWT decoder limita-se a descodificar as partes header e payload para as tornar legíveis. Não efetua qualquer verificação de assinatura e não pede chave. Utilize-o para inspecionar rapidamente o conteúdo de um token. Utilize o JWT verifier (esta página) assim que precisar de provar que um token é autêntico. Para fabricar um JWT assinado para fins de teste, utilize o nosso JWT builder.

Perguntas frequentes

Porquê RS256 em vez de HS256?

Com HS256, o emissor e o verificador partilham o mesmo segredo: qualquer verificador pode portanto emitir tokens. É gerível quando se controla os dois lados. Assim que se fala de um fornecedor de identidade único com vários serviços consumidores, passa-se a RS256: o emissor guarda a chave privada, distribui-se a chave pública a todas as APIs que devem verificar. Nenhuma API consumidora pode então forjar tokens.

Como obter a chave pública de um fornecedor de identidade (IdP)?

A maioria dos IdP expõe um endpoint JWKS padronizado (por exemplo https://exemplo.com/.well-known/jwks.json). Este endpoint devolve um JSON que contém as chaves públicas ativas. Pode converter a entrada JWK que corresponde ao kid do header do seu JWT em chave PEM através do comando openssl ou de uma biblioteca JWKS da sua stack (por exemplo jose-jwt, jwks-rsa).

Que fazer se o verify falhar?

Verifique primeiro o algoritmo: um token assinado em HS256 não se verifica com uma chave RSA, e inversamente. Verifique depois a chave: um caractere branco a mais, uma quebra de linha em falta numa chave PEM, ou um segredo HMAC ligeiramente diferente do utilizado pelo emissor bastam para fazer falhar a verificação. Se o IdP efetuou uma rotação de chave, o seu kid pode apontar para uma chave que já não tem.

JWKS, o que é?

JWKS (JSON Web Key Set, RFC 7517) é um formato JSON que descreve um conjunto de chaves públicas. Cada chave é identificada por um kid (key ID) e o JWT a verificar referencia esse kid no seu header. O mecanismo permite ao IdP fazer rodar as suas chaves sem partir os verificadores: estes interrogam simplesmente o endpoint JWKS para obter a chave correspondente ao kid do token recebido.

Como gerar um par de chaves RSA para assinar os meus JWT?

Com OpenSSL: openssl genrsa -out private.pem 2048 depois openssl rsa -in private.pem -pubout -out public.pem. A chave privada assina no lado do emissor, a chave pública verifica no lado do consumidor. Para novos serviços, prefira 3072 ou 4096 bits.

É preciso cifrar o JWT além de o assinar (JWE)?

Um JWT assinado (JWS) garante a integridade e a autenticidade, mas o payload continua legível por quem o recupere. Se o token contém dados sensíveis (identificadores internos, direitos detalhados, dados pessoais), considere o formato JWE (JSON Web Encryption) que cifra o payload além de o assinar.

Exemplo de pedido

curl -X POST https://cdrn.fr/api/v1/tools/jwt-verifier/execute \
  -H "Content-Type: application/json" \
  -d '{"token":"...","key":"..."}'

Esquema de entrada

Campo Tipo Obrigatório Predefinição
token text
key text

Pontos de acesso

  • GET https://cdrn.fr/api/v1/tools - lista todas as ferramentas disponíveis
  • GET https://cdrn.fr/api/v1/tools/jwt-verifier - obtém o esquema desta ferramenta
  • POST https://cdrn.fr/api/v1/tools/jwt-verifier/execute - executa esta ferramenta com um payload JSON