Quem assina seu token: como interpretar jku, kid, iss e aud corretamente
O desafio
A API de expedição da Caldera aceita tokens assinados por transportadoras parceiras. Um token chega, a API precisa da chave pública que vai verificá-lo, e a biblioteca que ela usa quer ajudar: em vez de obrigar o time a manter uma lista de chaves de parceiros, ela lê no próprio token onde buscar a chave. Esse é um campo de cabeçalho real, está na especificação, e bibliotecas de fato o respeitam. Três nomes de host diferentes aparecem neste token e todos são plausíveis. Só um decide se a assinatura é confiável, e é justamente o que o atacante escolhe. Decodifique o token e envie o host de onde o verificador buscaria a chave.
O que você vai aprender
- Diferenciar parâmetros de cabeçalho de claims do payload e saber quais são confiáveis
- Explicar o que é jku e quando uma biblioteca vai agir sobre ele
- Explicar por que iss não pode ser usado para escolher uma chave de verificação
- Descrever como um atacante forja uma assinatura válida fornecendo a própria chave
- Reconhecer um host sósia se passando por infraestrutura de um parceiro
Habilidades testadas
Pré-requisitos
- Os três segmentos de um JWT
- Que base64url é codificação, não criptografia
Como funciona
Verificar uma assinatura é responder duas perguntas, não uma. A assinatura é matematicamente válida, e ela é válida sob uma chave em que você realmente confia? A maioria dos bugs de JWT mora na segunda pergunta, e este vive inteiramente ali.
O token cita três hosts. iss é https://auth.brightway.example, uma claim de identidade no payload. aud é https://api.caldera.example, o destinatário pretendido. Nenhum dos dois é fonte de chave, e iss em particular não pode ser: ele está dentro do corpo cuja assinatura você ainda não verificou, então usá-lo para escolher a chave seria circular.
O terceiro está no cabeçalho. jku significa JWK Set URL e está definido na RFC 7515. Ele diz: as chaves que verificam este token estão publicadas aqui. Uma biblioteca que respeita jku fará essa requisição antes de validar qualquer coisa, o que coloca jwks.brightway-cdn.example no comando de se a assinatura é confiável.
Nada protege o cabeçalho. Quem envia o token o escreve. Então um atacante gera um par de chaves, assina um token com as claims que quiser, publica a chave pública correspondente como um JWK Set em um host que controla, aponta jku para lá, e o verificador baixa essa chave e confirma a assinatura. A matemática está correta do início ao fim. A chave era do atacante.
kid é o campo em que as pessoas esperam esse bug, e não é o bug aqui: prod-2026-08 identifica uma chave sem dizer onde ela mora. O host sósia é o toque final. Ao lado de auth.brightway.example, um host chamado brightway-cdn.example parece o CDN da mesma empresa, e é um registro completamente separado.
Erros comuns
- Responder auth.brightway.example.
issdiz quem alega ter emitido o token, e está dentro da parte que você ainda não verificou. - Responder api.caldera.example.
audé o destinatário, verificado depois da assinatura, nunca uma fonte de chave. - Responder prod-2026-08.
kidnomeia uma chave. Não é um host nem uma URL. - Supor que o cabeçalho está protegido porque o token está assinado. A assinatura só tem significado depois que você tem uma chave confiável, e é o cabeçalho que escolhe essa chave.
- Ler brightway-cdn.example como brightway.example. São registros diferentes, e esse é justamente o ponto do nome.
Como se proteger
Nunca deixe um token dizer qual chave o verifica.
- Desative
jkuex5uno verificador. A maioria das bibliotecas tem uma flag ou uma allowlist de parâmetros de cabeçalho. Se a sua resolve esses campos por padrão, essa é a configuração para mudar hoje. - Fixe a descoberta de chaves na configuração. Mapeie cada emissor para uma URL de JWKS que você controla, e busque apenas a partir desse mapa.
- Use
kidsó para selecionar entre chaves que você já possui, nunca para buscar uma nova, e rejeite por padrão qualquerkiddesconhecido. - Fixe o algoritmo esperado por emissor e rejeite qualquer outro, para que um token não consiga levar você a usar
noneou HMAC com uma chave pública como segredo. - Valide
isseauddepois da assinatura, nunca antes, e alerte para qualquer token cujo cabeçalho carregue um parâmetro de URL. Em uma implantação saudável esse número é zero.