Executando verificação de segurança...
7

[Dúvida] - Como lidar com Token no Front-End usando uma SPA de forma (quase) 100% segura?

Olá! Eu estou desenvolvendo um simples projetinho para estudos que consiste em uma API feito em Java com o Spring e um Front-End que irá consumir essa API usando React. Tudo estava ocorrendo bem, até que eu comecei a entrar no topico que (aparentemente) assombra os desenvolvedores Back-End, o tal da segurança, nesse caso um sistema de autenticação.

Eu resolvi implementar um sistema de autenticação usando tokens, mais especificamente o JWT, e está funcionando da seguinte forma:

  1. O usuário envia suas credenciais (username e senha);
  2. O Back-End faz a verificação;
  3. Se tudo estiver de acordo, ele irá retornar 2 Tokens, um Access Token e um Refresh Token.

O Access Token expira em alguns poucos minutos e é enviado para o Back-End sempre que o usuário precisar ser autenticado. Já o Refresh Token é usando apenas para conseguir outro access token quando o antigo expirar, ele dura alguns dias, é salvo no banco de dados e só pode haver um único refresh token em circulação. Sempre que ele é usando para gerar um novo access token, um novo refresh token vem junto e o antigo expira. (Abordagem Rotativa)

Parece bacana, né? Bem, quase. Meus problemas oficialmente começaram no momento em que eu tenho que integrar isso com o Front-End de uma maneira segura. Em Frameworks com SSR, Nextjs por exemplo, basta armazenar os tokens como http-only cookies, mas com React eu não tenho acesso a eles. As outras alternativas são usar o LocalStore, Cookies ou guardar em memória. Vamos analisar cada uma dessas alternativas juntos?

  • Caso eu escolha guardar em LocalStore (calma, por favor não me machuque, é uma suposição), meu app fica vulnerável a XSS attacks, o que pode até não parecer mas não é algo legal.

  • O segundo é guardar em Cookies, que possui mais camadas de segurança, mas ainda é vulnerável a XSS attacks, ou seja, é melhor que o LocalStore, mas não muito.

  • O terceiro é guardar em memória, que é 100% seguro e códigos javascript de terceiros não o afetam, entretanto gera um outro problema, o de usabilidade, pois tudo que é guardado na memória é apagado quando a página sofre reload, logo, o úsuario precisaria fazer login sempre que a página recarregar, o que não é uma boa opção.

Mas calma, aparentemente nem tudo está perdido, pois é para isso que serve o refresh token, então, meu plano para a implementação seria (o que talvez seja óbvio para aqueles que já lidaram com isso):

Quando o úsuario fizer login, o refresh token é guardado em Cookie/Local Storage, mas o access token, que é o principal, fica na memória, assim, sempre que o usuário perder o acesso, seja por que ele expirou ou a página foi atualizada, a aplicação ainda conseguiria manter o usuário logado, pois ela utilizaria do refresh token para conseguir outro access token!

Maravilhoso, correto? Todos os problemas resolvidos! Chora Elon Musk! Ou será que não?

Minha principal dúvida, e o que me fez criar essa publicação não tão concisa é: Mas e se o refresh token for obtido por um terceiro?

Bem, ele conseguiria gerar novos access tokens, mas o refresh token do usuário expiraria e na tentativa de acessar uma página que precisa de autenticação, o login seria requisitado, ele iria logar e isso criaria um novo refesh token e invalidaria o que estava em circulação.

Mas, e se o nosso úsuario demorasse alguns dias para logar? Então um terceiro conseguiria fazer qualquer coisa em nome do nosso úsuario até que o refresh token expirasse. Ou talvez mais simples ainda, mesmo se o usuário logasse logo em seguida, o terceiro ainda teria um access token gerado com o antigo refresh token, e ele ainda duraria alguns minutos. Isso me parece um problema de seguraça, correto?

E por fim, resumindo todo esse post: Existe uma maneira mais segura de lidar com tokens em uma SPA? Eu estou deixando alguma informação relevante passar e esse problema é só coisa da minha cabeça? Qual outra alternativa eu tenho para autenticação que resolveria esses problemas?

Obrigado se você leu até aqui, aguardo respostas!

Carregando publicação patrocinada...
8

Acho que você se equivocou um pouco nos conceitos.

Primeiro, você está equivocado quando diz que guardar em localStorage ou cookies deixa seu app vulnerável a XSS. Na verdade o que acontece é o contrário: onde você guarda ou deixa de guardar o token não te deixa vulnerável a XSS, mas se o seu site tiver alguma vulnerabilidade XSS, ela pode ser usada para extrair essas informações se não forem bem protegidas.

Segundo, você não tem que fazer planos pensando "se o refresh token for obtido por um terceiro", da mesma forma que você não faz planos pensando "se alguém tiver acesso ao meu banco de dados através uma SQL injection". Você não planeja o seu sistema pensando no que fazer se tiver vulnerabilidades - você planeja o seu sistema para não ter vulnerabilidades. Se alguém obter o refreshToken de um terceiro ele pode fazer tudo em nome desse terceiro. É seu papel impedir isso de acontecer.

Terceiro, cookies HTTP-only transmitidos através de HTTPS não podem ser lidos pelo javascript. Se XSS é o maior dos seus problemas, essa é a solução.

1

Interessante a discussão e aproveitando o tópico: alguém conhece algum bom curso/video que auxilie na implementação de uma autenticação dessa forma?

Em outras palavras, uma Capela Sistina de como criar um bom método de autenticação.

1

Se um terceiro consegue acessar o Refresh Token de um usuário, sua aplicação tem problemas muito maiores do que apenas impedir que esse terceiro consiga se passar por um cliente.

-1