Blog Series #2 — Seu JWT está protegido?

O JWT depende da assinatura para garantir que ninguém alterou o seu conteúdo. Se o segredo usado nessa assinatura for fraco ou exposto, um atacante pode criar tokens válidos e manipular a aplicação.

José Eduardo Morini Castro5 min
Capa do Blog Series #2 com o título "JWT", um cursor terracota e o subtítulo "JSON Web Token".Capa do Blog Series #2 com o título "JWT", um cursor terracota e o subtítulo "JSON Web Token".

O JWT, ou JSON Web Token, é um dos formatos mais utilizados para autenticação em aplicações web e APIs. Depois que o usuário faz login, o servidor gera um token e o entrega ao cliente, que passa a enviá-lo em cada requisição, normalmente no cabeçalho Authorization. A partir desse token, o servidor identifica quem está fazendo a requisição e quais permissões essa pessoa possui.

Toda a segurança desse modelo depende de um único ponto: a assinatura. É ela que garante que o conteúdo do token foi gerado pelo servidor e não foi alterado no caminho. Quando o segredo usado para assinar o token é fraco ou acaba exposto, um atacante pode gerar seus próprios tokens e a aplicação passa a aceitá-los como se fossem legítimos.

O que é um JWT

Um JWT é formado por três partes separadas por ponto: o cabeçalho, o payload e a assinatura.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjMiLCJuYW1lIjoiTWFyaWEiLCJyb2xlIjoidXNlciJ9.<assinatura>

O cabeçalho informa qual algoritmo foi utilizado para assinar o token:

{
  "alg": "HS256",
  "typ": "JWT"
}

O payload contém as informações que a aplicação utiliza para identificar o usuário, como o identificador, o nome e o perfil de acesso:

{
  "sub": "123",
  "name": "Maria",
  "role": "user"
}

Um ponto importante é que essas duas primeiras partes não são criptografadas. Elas são apenas codificadas em Base64, o que significa que qualquer pessoa que tenha acesso ao token consegue ler o seu conteúdo. O JWT não foi feito para esconder informações, e sim para garantir que elas não sejam alteradas.

Como funciona a assinatura

A terceira parte do token é a assinatura. No algoritmo HS256, que é um dos mais comuns, o servidor pega o cabeçalho e o payload e calcula um HMAC utilizando um segredo que somente ele deveria conhecer.

Quando o token volta em uma requisição, o servidor refaz esse cálculo. Se o resultado for igual à assinatura presente no token, o servidor considera que o conteúdo é confiável. Se alguém alterar qualquer informação do payload, por exemplo trocando "role": "user" por "role": "admin", a assinatura deixa de corresponder e o token é rejeitado.

Isso significa que a proteção do JWT não está no token em si, mas no segredo utilizado para assiná-lo. Enquanto esse segredo permanecer desconhecido, ninguém consegue gerar uma assinatura válida para um payload modificado.

O risco de gerar assinaturas em sites

Durante o desenvolvimento, é comum utilizar sites que decodificam, validam e geram JWTs. Basta colar o token, informar o segredo e o site mostra o conteúdo e calcula a assinatura.

O problema aparece quando isso é feito com tokens e segredos reais. Ao colar o segredo de produção em um site de terceiros, ele deixa de estar apenas no servidor da aplicação. Não há como garantir o que acontece com essa informação depois de enviada: ela pode ser registrada, armazenada ou exposta de alguma forma que foge completamente do controle de quem a utilizou.

O mesmo vale para o token. Um token de produção representa a sessão de um usuário real e, enquanto estiver válido, pode ser utilizado por qualquer pessoa que tenha acesso a ele.

Gerar ou validar assinaturas deve ser responsabilidade da própria aplicação, com o segredo armazenado de forma segura no servidor. Para testes, o ideal é utilizar tokens e segredos criados especificamente para esse fim.

Quando o segredo pode ser quebrado

Mesmo que o segredo nunca seja colado em nenhum site, ainda existe outro risco. Um token assinado com HS256 contém tudo o que é necessário para testar se um determinado segredo está correto: o cabeçalho, o payload e a assinatura resultante.

Isso permite que um atacante que tenha obtido um token tente descobrir o segredo de forma offline, sem precisar fazer nenhuma requisição para a aplicação. Ferramentas como o Hashcat, amplamente utilizadas para quebra de senhas, conseguem testar uma enorme quantidade de possibilidades em pouco tempo, utilizando listas de palavras comuns e combinações.

Se o segredo for curto, previsível ou baseado em valores conhecidos, como o nome da aplicação, secret, 123456 ou um valor de exemplo copiado da documentação, ele pode ser descoberto rapidamente.

O impacto

Com o segredo em mãos, o atacante passa a ter a mesma capacidade que o servidor: ele consegue gerar tokens válidos com qualquer conteúdo.

Na prática, isso permite alterar o payload para se passar por outro usuário, trocando o valor de sub, ou elevar as próprias permissões, alterando o campo role para admin. Como a assinatura é calculada com o segredo correto, a aplicação não tem como diferenciar esse token de um token legítimo.

A partir desse ponto, tudo o que a aplicação controla com base no JWT fica comprometido: acesso a contas de outros usuários, painéis administrativos, endpoints internos da API e qualquer dado que dependa dessa autenticação.

Como se proteger

A principal medida é utilizar um segredo longo e gerado de forma aleatória, que não possa ser adivinhado nem encontrado em listas de palavras. Esse segredo deve ficar armazenado apenas no servidor, fora do código-fonte e do repositório.

Também é importante nunca utilizar segredos ou tokens de produção em sites de terceiros para decodificar ou gerar assinaturas.

Além disso, o servidor deve definir explicitamente qual algoritmo aceita, em vez de confiar no valor informado no cabeçalho do token, e os tokens devem ter um tempo de expiração curto. Em cenários em que vários serviços precisam validar tokens, algoritmos assimétricos, como o RS256, permitem que apenas o emissor tenha a chave privada usada para assinar, enquanto os demais serviços utilizam somente a chave pública para validar.

Integre DevSecOps ao seu fluxo de trabalho com o runtz agora

Comece a analisar riscos de segurança. Comece em runtz.dev.