A adoção do JWT como "solução milagrosa" para segurança de APIs é exagerada, você provavelmente já caiu naquela clássica armadilha de achar que basta usar uma chavezinha básica no seu appsettings.json e achar que tá tudo protegido. Amigo, não é bem assim não...
Grandes nomes como Cisco e IBM já deixaram claro: cerca de 60% dos ataques vêm de dentro da sua própria empresa. Então, se você acha que guardar uma chave de JWT de maneira simplista é seguro, é bom repensar. O problema não é a chave simétrica em si, é a forma totalmente amadora que ela é usada.
Veja o exemplo clássico que todo dev júnior (e infelizmente alguns sêniors também) adora usar:
Nesse exemplo lindo, você simplesmente pega a chave direto do appsettings.json
. Poderia estar no banco, poderia estar hardcoded (espero que não!), mas qualquer dessas opções é uma tragédia anunciada.
Veja como é simples atacar
Basta ir no querido jwt.io, colar seu JWT gerado com toda inocência do mundo, e testar a chavezinha que você acha segura:
Quando utilizar uma chave simétrica?
No caso de um monolito velho de guerra, isso é até aceitável. Agora, se você já está brincando de "Netflix wannabe" com vários microsserviços rodando independentes, temos duas péssimas opções:
- Você centraliza tudo num serviço só pra gerar e validar tokens, criando um gargalo absurdo e uma latência de dar inveja na fila do SUS;
- Você espalha a chave por todo lado, tipo brinde de festa junina, aumentando consideravelmente o risco de alguém com má intenção fazer a festa com seus tokens.
O jeito "adulto" de lidar com isso? Implementar um servidor OAuth 2.0 com OpenId Connect, utilizando descobertas automáticas, JWKS, e chaves assimétricas. É mais complicado? É. Mas segurança não é brincadeira de criança, certo?
Se estiver mesmo no monolito raiz, pelo menos gere uma chave decente e guarde direito. Nada de gerar uma chave como no exemplo acima, porque aí sim você tem problemas reais.
Como gerar uma chave simétrica decente
Na hora de criar sua chave, lembre-se:
- Geração segura
- Tamanho adequado
- Armazenamento seguro (JWK)
Nunca use texto simples. O .NET Core já pensou nisso pra você:
Duas coisas importantes no exemplo acima:
- A chave tem 64 bytes (isso não é frescura, é recomendação do NIST)
- Usou
System.Security.Cryptography.RandomNumberGenerator
porque segurança não é gerar senha com aniversário do cachorro
Por que raios 64 bytes?
Porque segurança tem padrões. O NIST recomenda claramente:
"alg" | Algoritmo | Tamanho da Chave |
---|---|---|
HS256 | SHA-256 | 64 bytes |
HS384 | SHA-384 | 128 bytes |
HS512 | SHA-512 | 128 bytes |
Microsoft reforça esses valores (veja referências no final).
Armazenando a chave com dignidade
"Ah, mas gerar uma chave aleatória toda vez que iniciar o app é impraticável!" Sim, é mesmo. Por isso você armazena a chave JWK corretamente.
Melhores práticas usadas:
- Auto-geração segura
- Armazenamento protegido
Componentizando com Jwks.Manager
Não precisa reinventar a roda: use o Jwks.Manager para facilitar sua vida e gerenciar JWKs com prazo de validade.
Conclusão
Espero que essa abordagem faça você pensar duas vezes antes de brincar com JWTs. Dúvidas, críticas ou ironias adicionais, pode deixar nos comentários!
Download

Confira o código completo no GitHub