JWT de acesso: o que validar antes de confiar

GuiaUNIFOKAL6 min de leituraProduto e integração
Ver em Markdown

A lista do RFC 9068 para validar um token de acesso JWT, os ataques que cada checagem fecha pelo RFC 8725 e os erros que fazem um token de outro sistema passar no seu.

Neste artigo, 6 seções

Conferir só a assinatura de um token de acesso JWT parece suficiente e não é: um token legítimo, assinado pela chave certa, pode ter sido emitido para outra API, para outro tipo de uso ou ter vencido há horas. A resposta curta: o RFC 9068, na seção 4, lista o que o servidor que recebe o token tem de conferir, e são seis checagens (tipo, emissor, audiência, assinatura com algoritmo fixo, chave do emissor e validade), cada uma fechando um ataque descrito no RFC 8725.

  • O RFC 9068 manda rejeitar o token cujo cabeçalho "typ" não é "at+jwt" ou "application/at+jwt".
  • O emissor ("iss") tem de bater exatamente, e a audiência ("aud") tem de conter o identificador da sua API.
  • A lista de algoritmos aceitos é sua, não do token: o RFC 8725 manda a biblioteca usar só os algoritmos que você especificou.
  • A chave vem do emissor que você espera, nunca de uma URL que o próprio token aponta.
  • O vencimento ("exp") admite folga pequena para relógio, "usually no more than a few minutes".

A lista do RFC 9068

O RFC 9068 define o perfil de JWT para tokens de acesso OAuth 2.0. A seção 4 é a lista de validação para o servidor de recursos, a sua API, que recebe o token.

The resource server MUST verify that the "typ" header value is "at+jwt" or "application/at+jwt" and reject tokens carrying any other value.

RFC 9068, seção 4

Na ordem do RFC, as checagens são estas.

as checagens do RFC 9068, seção 4, e o que cada uma impede (o ataque é leitura da casa sobre o RFC 8725)
ChecagemO que o RFC dizO que impede
Tipo"typ" igual a "at+jwt" ou "application/at+jwt"Aceitar como acesso um token de outro tipo do mesmo emissor, como um ID Token
Emissoro emissor "MUST exactly match the value of the iss claim"Aceitar token de outro emissor que use a mesma biblioteca
Audiência"aud" tem de conter o identificador da sua APIUsar contra a sua API um token emitido para outra
Assinaturavalidar pelo algoritmo do cabeçalho e rejeitar "alg" igual a "none"Token forjado sem assinatura
Chave"MUST use the keys provided by the authorization server"Token assinado com chave escolhida pelo atacante
Validadehora atual antes de "exp", com folga pequenaToken vencido reaproveitado

Se qualquer checagem falha, o RFC manda responder com o erro "invalid_token", como define o RFC 6750 para tokens Bearer. Para quem chama, a mensagem é uma só; o motivo detalhado fica no seu log.

Algoritmo: a lista é sua

A checagem de assinatura do RFC 9068 diz para usar o algoritmo do cabeçalho "alg", e é exatamente aí que mora o ataque mais conhecido de JWT. Se a sua biblioteca aceita qualquer algoritmo que o token declarar, o atacante escolhe. Os dois casos clássicos são o "none", token sem assinatura, e a confusão de algoritmo, em que um token declara um algoritmo simétrico e usa a chave pública do emissor como segredo.

O RFC 8725, as boas práticas atuais de JWT, fecha isso na seção 3.1: as bibliotecas "MUST enable the caller to specify a supported set of algorithms and MUST NOT use any other algorithms when performing cryptographic operations", e cada chave "MUST be used with exactly one algorithm". Em termos práticos, a sua configuração diz "aceito só RS256" (ou o algoritmo que o emissor publica), e o token que declara outro é rejeitado antes de qualquer cálculo. O RS256 é o algoritmo que o RFC 9068 exige que todo servidor de recursos suporte.

Conferir "alg" contra uma lista dentro do seu código não basta se a biblioteca escolhe a chave e o algoritmo sozinha. Passe a lista de algoritmos aceitos na chamada de verificação, e confirme no teste que um token com "alg" igual a "none" ou a um algoritmo simétrico é recusado.

Chave: do emissor esperado, nunca do token

A chave pública vem do conjunto de chaves (JWKS) que o emissor publica, e o RFC 9068 recomenda anunciá-lo nos metadados do servidor de autorização. O cabeçalho do token traz o "kid", o identificador da chave, e esse valor serve só para escolher dentro do conjunto que você já confia.

O RFC 8725, na seção 3.10, avisa que seguir às cegas um cabeçalho "jku" ou "x5u", que apontam uma URL, pode levar a falsificação de requisição do lado do servidor, e que o "kid" deve ser validado antes de virar busca em banco ou diretório. A regra que sai daí: a URL do JWKS é configuração sua, amarrada ao emissor que você espera, e o token não escolhe de onde vem a chave.

A troca de chave do emissor acontece com o "kid": a chave nova aparece no JWKS antes de começar a assinar, e o seu verificador, com cache curto do conjunto, já a tem quando o primeiro token novo chega. Busque o JWKS de novo quando aparecer um "kid" desconhecido, com limite de frequência para um token inventado não virar enxurrada de busca.

Um emissor, vários tipos de token

O RFC 8725 dedica a seção 3.12 a um erro que a lista acima previne em conjunto: quando o mesmo emissor gera mais de um tipo de JWT, "the validation rules for those JWTs MUST be written such that they are mutually exclusive, rejecting JWTs of the wrong kind". As estratégias que o RFC lista são as que você já aplicou: tipo explícito no "typ", audiências diferentes, chaves diferentes, conjuntos de campos obrigatórios diferentes.

O mesmo raciocínio vale entre clientes de um mesmo serviço de autenticação. Se dois produtos usam o mesmo provedor e a sua API só confere a assinatura, um token emitido para o outro produto passa. Emissor por cliente e audiência por API tornam essa troca impossível. A credencial que nunca deve sair do servidor está no artigo sobre chave secreta no navegador, e o equivalente para webhooks, em assinatura de webhook.

Perguntas frequentes

Basta validar a assinatura do JWT?

Não. O RFC 9068 manda conferir também o tipo, o emissor, a audiência, a chave do emissor e o vencimento. Um token bem assinado pode ser de outro tipo, para outra API ou estar vencido.

Quanto de folga dar no vencimento do token?

O RFC 9068 permite uma folga pequena para diferença de relógio, "usually no more than a few minutes". Folga grande transforma o token curto num token longo.

Posso aceitar o algoritmo que vem no cabeçalho do token?

Só se ele estiver na lista que você configurou. O RFC 8725, seção 3.1, manda a biblioteca usar apenas os algoritmos especificados por quem chama, e o RFC 9068 manda rejeitar "alg" igual a "none".

De onde vem a chave pública para verificar o token?

Do JWKS do emissor que você espera, num endereço configurado por você. O "kid" do token só escolhe a chave dentro desse conjunto; "jku" e "x5u" vindos do token não são seguidos.

Fontes citadas

  • RFC 9068, JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens, seção 4, em rfc-editor.org
  • RFC 8725, JSON Web Token Best Current Practices, seções 3.1, 3.10 e 3.12, em rfc-editor.org
  • RFC 6750, The OAuth 2.0 Authorization Framework: Bearer Token Usage, seção 3.1, em rfc-editor.org