A lógica da segurança por trás dos tokens JWT
Tabela de Conteúdo
A lógica da segurança por trás dos tokens JWT
Eu, mesmo sabendo fazer autenticação RESTful no Spring por bastante tempo, ainda confundia minha cabeça pra entender como o JWT é seguro. Como assim passar informações em um token que pode ser facilmente ser decodificado? Esse é um apanhado geral do tema que busca explicar isso.
1: Tokens web
No contexto de APIs web, tokens são “chaves” que utilizamos para o servidor verificar quem somos. Geralmente recebemos um token no login, e temos de guardar a informação no front-end para utilizá-la junto das nossas próximas requisições para acessar recursos restritos 1
Vamos usar como exemplo a TokenAuthentication, um módulo do DRF 2
from rest_framework.authtoken.models import Token
token = Token.objects.create(user=...) # cria token
print(token.key)
Esse módulo provê o model Token, e cada usuário pode estar ligado a um token apenas. A ideia é que o dev, no login, use a função mostrada acima para criar um token que vai ficar salvo no banco de dados, e em seguida retorne ele para o usuário.
Aqui está um pedaço da minha View de login, onde além de retornar o token, eu retorno dados do usuário junto:
if senha_correta(usuario, request.data['password']):
token = Token.objects.create(user=usuario) # cria o token
usuario_serializado = UsuarioSerializer(
usuario,
context = {"request":request}
)
r = Response( # monta request
data={
"token":str(token), # token
"usuario": usuario_serializado.data # dados do usuário
}
)
r["Access-Control-Allow-Origin"] = "http://localhost:5173"
return r
Uma vez com o token recebido, o front-end deverá salvá-lo e envia junto de toda requisição que necessitar de autorização.
Caso o usuário não inclua o token numa requisição restrita, o servidor provavelmente responderá com o código 401 UNAUTHORIZED.
Em cada requisição, o servidor vai checar a qual usuário percente aquele token (com base no banco de dados, guarde essa informação) e assim devolver os dados correspondentes.
2: Tokens JWT
O JWT é um padrão aberto que define uma forma específica de montar tokens. Diferente do nosso token simplório do módulo do DRF, o JWT pode carregar diversas informações dentro dele.
Um JWT tem três partes:
- Cabeçalho
- Conteúdo
- Assinatura
Esse tipo de token tem a vantagem de carregar consigo tudo que o servidor precisa pra identificar um usuário. Isso mesmo, dentro dele. A autenticidade dele não depende de nada no banco de dados.
Essa é uma característica definida pelo padrão REST, chamada stateless (sem estado). Ou seja, o servidor não guarda informações de “sessão” de nenhum usuário, os dados necessários ficam nos tokens. Recebeu uma nova requisição? Olha no token de quem que é.
Essa característica é relevante por vários motivos:
- Simplifica a lógica no servidor, pois não é necessário armazenar nada sobre a sessão atual do cliente.
- Ajuda o escalonamento do aplicativo: Você pode ter vários servidores diferentes rodando a sua API, e qualquer um deles consegue checar a veracidade dos dados já que não há sessões em cada um deles (normalmente sessões são implentadas por servidor).
- Simplifica os processos de cache, pois normalmente seria necessário checar dados de sessão para decidir o que cachear ou não.
A importância das assinaturas
Algo que me incomodou ao entender como isso tudo funcionava, era o fato de que o servidor depende somente das informações contidas no token pra identificar o usuário. Até aí beleza, mas lembra que eu te disse que os dados não necessariamente são criptografados? Você mesmo consegue ir num site de decodificação de JWT e olhar o conteúdo de um token desse tipo.
Na verdade, você pode inclusive modificar o conteúdo do token e mandar para o servidor de volta… Isso, na minha cabeça, significava uma falha de segurança grave. Mas aí que entram as assinaturas.
A tal da assinatura em um token JWT é gerada a partir do conteúdo original do token e de uma chave secreta que só o servidor sabe.
Isso significa que a toda requisição, o servidor checa se os dados batem com a assinatura e a chave secreta.
E por isso tokens JWT são seguros.
Considerações finais
Eu simplifiquei o possível tentando não perder muito conteúdo. Por exemplo, ainda há outra forma de assinatura de JWT que utiliza uma chave pública e outra privada, mas preferi deixar de fora pois entendendo o que eu mostrei você já tá massinha.
Referências
JWT.io - Introdução https://jwt.io/introduction
RestFulAPI - Statelessness https://restfulapi.net/statelessness/