Entendendo a autenticação com JWT
Faz bastante tempo que trabalho usando JWT para autenticação, seja consumindo no client ou nos meus próprios serviços de autenticação. No começo eu tinha muita dificuldade em entender como que um token, em que as informações poderiam ser vistas por qualquer um que obtivesse acesso a ele , poderia garantir que eu realmente estava autenticado. Conforme o tempo foi passando, eu passei a entender melhor e com isso comecei a perceber que muitas pessoas ao meu redor acabavam deixando falhas de segurança na aplicação também por não entenderem direito o funcionamento e quais eram as boas práticas envolvidas.
Neste artigo, que foi inspirado em um workshop dado no evento DoWhile 2020 da Rocketseat, quero mostrar brevemente como o JWT garante que um usuário está autenticado e quais são erros comuns que podem ser cometidos e que comprometem a segurança dos envolvidos.
Caso queira ver o código usado no workshop, em que implementei algumas boas práticas usando Node.js, clique aqui.
O que é um JWT
Podemos entender o JWT como uma forma segura de transmitir informações, na qual as partes envolvidas possuem formas de validar que o conteúdo foi gerado por uma entidade confiável. Um token JWT é composto por três partes separadas por "." .

Header
Um JSON composto por 2 informações: o tipo do token, que nesse caso é JWT e o algoritmo usado para gerar a assinatura. Este JSON é então "encodado" em Base64.
Payload
Um JSON no qual ficam as claims, que são as informações contidas no token, geralmente atreladas a um usuário. Algumas claims são reservadas, e possuem informações sobre o próprio JWT, como veremos em breve. Este JSON também “encodado” em Base64.
Assinatura
A assinatura consiste de uma combinação do header, do payload e mais um secret ou uma chave. Usando essas informações é gerada uma hash com o algoritmo especificado no header.
Note que se alterarmos o conteúdo do header ou do payload, alteraríamos também a assinatura. Porém, como não temos a chave usada para gerá-la, é fácil de validar que aquele token foi alterado por quem não poderia tê-lo feito, portanto é assim que podemos garantir que a informação contida nele é confiável.
Erros comuns
Aqui vamos ver alguns erros que são comuns, principalmente quando estamos criando os nossos primeiros serviços de autenticação e não temos o total domínio sobre como funciona o JWT.
Colocar informações sensíveis no payload
Ao olhar um JWT, podemos esquecer que as informações contidas nele devem ser consideradas como públicas, dado que qualquer um que obtiver um token pode facilmente verificar as informações contidas no payload. Para verificar isso, pegue qualquer JWT token e coloque no debugger do site jwt.io e, mesmo que não consiga validar, vai conseguir ver todas as informações contidas nele.

Lembre de colocar sempre apenas o que for necessário e nunca colocar informações como senhas, CPF, ou outras informações sensíveis, que além de fazerem o token ficar maior, podem acabar gerando uma falha de segurança.
Não configurar um tempo de expiração
Desde a primeira implementação é extremamente importante colocar um tempo de expiração no JWT. Como todos os serviços que usam o token para validar se o usuário está autenticado confiam nas informações contidas nele, caso este token não tenha um tempo de expiração, qualquer um que obtiver ele estará "logado" para sempre.

Quando você coloca um tempo de expiração, diminui muito a chance de que alguém mal intencionado obtenha um token válido e se passe por outro usuário nas chamadas para os seus serviços.
Colocar um tempo de expiração muito alto
Também não é aconselhado colocar um tempo de expiração muito alto, dado que a ideia é exatamente evitar que tokens válidos circulem entre pessoas mal intencionadas. Vale sempre pensar nas necessidades do seu sistema, dado que muitas vezes se faz desnecessário um tempo de uma semana para a expiração. Um tempo que geralmente é recomendado como padrão é 15 minutos.
Como alternativa à "deslogar" o usuário da aplicação, pode-se usar refresh tokens para gerar novos tokens de autenticação JWT. Os refresh tokens são enviados para o serviço de autenticação quando o seu JWT expirou ou está para expirar e é validado junto ao banco de dados, dando a possibilidade de desabilitar um refresh token. Vale a pena estudar mais a fundo técnicas como esta para ter JWTs que não duram muito e ainda assim garantir uma boa experiência para os usuários.
Utilizar um algoritmo fraco para a assinatura
O algoritmo padrão para usado para assinar um JWT é o HS256, no qual você usa uma secret, como se fosse uma senha, tanto para gerar os tokens quanto para validá-los (ou seja é um algoritmo simétrico). Isso funciona bem, porém, como diversos serviços podem conter esta secret apenas para validar o JWT (imagine que você tem o seu serviço de autenticação e um de gerenciamento de pedidos, os dois teriam a mesma secret em suas configurações) as chances que ela vaze podem ser maiores, e se alguém conseguir esta secret, consegue facilmente gerar tokens de autenticação válidos e isso vai gerar muita dor de cabeça. Uma pessoa que tenha a secret, pode modificar valores no payload para que se passe por outros usuários, ou até mesmo escalar privilégios caso o token seja usado para autorização.


Uma alternativa, é usar um algoritmo assimétrico, ou seja, um algoritmo no qual uma chave privada é utilizada para gerar os tokens e uma chave pública é usada para validá-lo. Com algoritmos como os RSA ou ECDSA, somente seu serviço de autenticação terá a chave necessária para gerar os tokens, enquanto os demais serviços só terão a chave para validá-los, então mesmo que elas "vazem", você não terá tantos problemas.
Não utilizar as demais claims reservadas do JWT
Não é só o tempo de expiração que pode ser configurado no JWT, alguns outros campos (claims) também são reservados e possuem suporte das bibliotecas de JWT e podem ser usados para aumentar ainda mais a segurança dos seus tokens.
Claims como aud que especifica quais os serviços que devem aceitar aquele token ou o iss, que diz qual serviço gerou o token, podem ajudar bastante. O primeiro passo é conhecer tais claims e entender o que elas fazem. Você pode ler mais na RFC do JWT.
Conclusão
Depois que entendemos melhor como funciona um JWT, percebemos que, gastando muito pouco tempo, conseguimos deixar nossos serviços muito mais seguros. Algumas práticas simples conseguem evitar estragos muito grandes!
Aqui eu coloquei alguns dos erros comuns relacionados ao JWT, sugiro que, quando criar seu serviço de autenticação pesquise quais são os erros mais comuns e as melhores práticas. Não siga cegamente o primeiro exemplo que achar e tente entender o que de fato está acontecendo por trás dos métodos sign
, verify
e decode
. Uma vez que você tenhas esse conhecimento, conseguirá julgar com muito mais facilidade o que é uma boa implementação e o que não é.