Autenticação JSON Web Token (JWT) em Node.js - Parte 2

Node.js

Autenticação JSON Web Token (JWT) em Node.js - Parte 2

Luiz Duarte
Escrito por Luiz Duarte em 02/07/2025
Junte-se a mais de 34 mil devs

Entre para minha lista e receba conteúdos exclusivos e com prioridade

JSON Web Token, o famoso JWT, é de longe a forma mais comum de segurança utilizada em web APIs RESTful depois do uso de SSL/TLS. Quando temos variados clientes consumindo nossas APIs, saber quem está autenticado ou não, ou ainda, que tem autorização ou não para fazer as chamadas, é importantíssimo.

Na primeira parte deste tutorial eu ensinei como implantar um mecanismo de JWT em uma API Node.js, adicionando esta importante camada de segurança. Dentre as dúvidas mais comuns acerca desta técnica está a segurança do token, afinal ele é o ponto mais exposto na técnica e sequestro de tokens é a forma mais comum de tentar burlar este mecanismo.

Claro que usando SSL (sugiro Lets Encrypt que é gratuito) esse risco de captura diminui consideravelmente uma vez que a conexão está criptografada e encorajo fortemente você a não aceitar requisições usando HTTP. No entanto, sabemos que para tudo existem brechas a serem exploradas principalmente por parte de pessoas maliciosas dentro da sua própria empresa muitas vezes…

Curso FullStack

#1 – O risco do JWT

Mas o token não é criptografado? Eu olhei ele e vi um monte de letras e números aleatórios…

Não, o token apenas é codificado em base64, uma representação textual de um conjunto de bytes, separada em três partes por pontos, veja com atenção o exemplo abaixo.

Se você jogá-lo em qualquer decodificador de base64 online, verá as três partes que o compõem, sendo que a única ilegível é a terceira onde temos a assinatura digital do servidor, atestando que aquele token foi gerado corretamente pelo seu servidor, o que impede que tokens fake se passem por tokens reais. Veja abaixo um exemplo.

No entanto, para essa assinatura digital é necessário um segredo/secret. Esse segredo é usado tanto para assinar quanto para verificar a assinatura (e autenticidade) de um token. Se você tem apenas uma webapi que usa o JWT, isso é bem tranquilo. Agora, se você possui diferentes microservices e todos eles precisam de autenticação/autorização via JWT, então você tem um problema pois:

  • ou você faz com que o cliente se autentique em cada um dos microservices que vai usar;
  • ou você compartilha o secret entre todos eles.;

Uma solução possível para este problema é usar um API Gateway na frente de todos microserviços. Daí todos eles confiariam no gateway, sem ficar pedindo ou verificando tokens. Mas geralmente bons APIs gateways custam caro, embora dê pra fazer um mais caseiro.

Mas voltando ao assunto central: como podemos adicionar mais segurança em nosso JWT em arquitetura de micro serviços? Ou melhor: como podemos garantir que este token possa ser usado em diferentes microservices sem compartilhar o secret com todos eles?

#2 – Assinatura assimétrica do JWT

Como estou falando de um token que navega entre diferentes microservices por meio inseguro (internet) o ideal é uma criptografia assimétrica, onde o emissor/servidor irá gerar o token assinado com a chave privada (de posse somente dele) e os consumidores/clientes podem verificá-lo usando a chave pública (de posse de todos microservices).

Assim, todos microservices confiam na emissão de tokens a partir um servidor central, enquanto podem validar sua assinatura para garantir que não foi forjado, sem saber o secret original.

Primeiro, vamos criar um par de chaves (uma pública e outra privada) usando o algoritmo RSA, um dos mais famosos e seguros do mundo de tipo assimétrico. O jeito mais fácil de gerar um par para fins de estudo é usando um gerador online, como esse aqui. O Format Scheme é PKCS #1 e o tamanho da chave varia de 256 bits a 2048 e embora chaves maiores sejam mais seguras (cada vez que você dobra o tamanho multiplica por 6x a dificuldade de quebra da chave) atente ao fato de que seu JWT deverá ser decifrado pelo servidor a cada requisição e que chaves maiores são mais demoradas para decifrar, mesmo com a chave certa.

Como é apenas para estudo, fiz com o tamanho comercialmente aceito (2048 -bit atualmente) e minha chave pública ficou assim (salve em um arquivo public.key):

Enquanto que minha privada ficou assim (salve em um arquivo private.key):

Agora, em nossa API, vamos mudar levemente o nosso código que gera os tokens para que os mesmos sejam assinados de maneira assimétrica.

As alterações estão apenas a partir da linha que declaro a privateKey, que substitui o nosso secret padrão que estávamos usando. A leitura do arquivo da chave é feita usando módulo fs (adicione um require no topo do arquivo) e nas opções de assinatura (terceiro argumento da função) dizemos o algoritmo de hashing que o RSA vai usar no seu algoritmo interno (RS256 no meu caso represa SHA-256).

Atenção: caso o seu arquivo de chave secreta tenha senha (como são geralmente os .PEM gerados pelo OpenSSL, que é a ferramenta mais recomendada) você deve passar um objeto {key, passphrase} no segundo argumento do sign ao invés de apenas a privatekey.

Agora, ao se autenticar, o servidor lhe retornará um token com assinatura criptografada de forma assimétrica, o que permite que vários serviços possam verificar sua assinatura usando a chave pública, ao invés da chave privada, usada para assinar o token da primeira vez, o equivalente ao secret original.

Atenção: se você tiver o erro “digest too big for RSA key” quer dizer que sua chave é pequena demais para o texto a ser cifrado. Neste caso, use chaves maiores (eu não tive problema a partir de 1024-bit).

Curso Beholder
Curso Beholder

#3 – Verificando o JWT

Agora é hora de ajustar o código que verifica a assinatura do JWT. Enquanto que para criarmos a assinatura usamos a private key, para verificar a mesma usamos a public key.

Note que a alteração foi bem sutil mesmo: carregamos a public key a partir do respectivo arquivo, passamos ela pra função verify, bem como um objeto informando o algoritmo de hashing que usamos junto do RSA (RS256 refere-se a SHA-256). Sim, é um array de algoritmos e passei apenas um.

Agora, se você obter um token pela rota de login e usá-lo para acessar a rota de clientes, verá que está funcionando como deveria e que no uso comum de apenas um cliente e servidor, não mudou em nada. Mas se tiver que confiar neste token em diferentes microservices, todos eles podem verificar a assinatura usando a chave pública.

Note que esta abordagem consome mais recursos computacionais que os tokens comuns. Esteja preparado para um aumento custo de hardware e/ou do tempo entre cada requisição que necessita deste token.

Espero que tenha gostado do artigo e se gosta do assunto criptografia, o vídeo abaixo é para você!

 

Curso Node.js e MongoDB

TAGS:

Olá, tudo bem?

O que você achou deste conteúdo? Conte nos comentários.

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

2 Replies to “Autenticação JSON Web Token (JWT) em Node.js – Parte 2”

Gabriel

Excelente artigo! É o básico bem feito para garantir o mínimo de segurança na autenticação de microserviços.
Apenas um adentro, acredito que atualmente faça mais sentido utilizar authorization bearer ao invés de x-access-token para autorização do jwt!

Luiz Duarte

Sim, o cabeçalho mais comum de ser usado atualmente é o Authorization mesmo. Independente disso, o funcionamento é o mesmo. O mesmo vale para a notação ‘Bearer’ que não tem qualquer efeito prático ou diferente no funcionamento, é apenas uma convenção comum para dizer que o conteúdo do cabeçalho é um token do portador (Bearer).