
localStorage
Tipos de tokens
- Os tokens de acesso são geralmente JWTs de curta duração assinados pelo servidor. Eles estão incluídos em todas as solicitações HTTP feitas pelo cliente ao servidor. Tokens são usados para autorizar solicitações.
- Os tokens de atualização são geralmente tokens de longa duração armazenados em um banco de dados e usados para obter um novo token de acesso quando o token anterior expira.
Onde exatamente os tokens devem ser armazenados no cliente?
Existem 2 maneiras comuns de armazenar tokens no cliente: armazenamento do navegador local e cookies. Há muito debate sobre qual método é melhor. A maioria das pessoas prefere cookies por causa de sua melhor segurança.
Vamos comparar o armazenamento local e os cookies. Nossa comparação é baseada principalmente neste material e nos comentários a ele.
Armazenamento local
▍Vantagens
A principal vantagem do armazenamento local é que é fácil de usar.
- Trabalhar com armazenamento local é muito conveniente, JavaScript puro é usado aqui. Se seu aplicativo não tiver um back-end e você depender de APIs de terceiros, nem sempre poderá solicitar que essas APIs definam cookies específicos para seu site.
- Usando o armazenamento local, é conveniente trabalhar com APIs que exigem a colocação de um token de acesso no cabeçalho da solicitação. Por exemplo - como segue:
Authorization Bearer ${access_token}
.
▍Desvantagens
A principal desvantagem do armazenamento local é sua vulnerabilidade a ataques XSS.
- Ao executar um ataque XSS, um invasor pode executar seu código JavaScript em seu site. Isso significa que um invasor pode obter acesso ao token de acesso armazenado em
localStorage
. - A origem do ataque XSS pode ser o código JavaScript de terceiros incluído em seu site. Pode ser algo como React, Vue, jQuery, script do Google Analytics e assim por diante. Em condições modernas, é quase impossível desenvolver um site que não inclua bibliotecas de terceiros.
Biscoitos
▍Vantagens
A principal vantagem dos cookies é que eles não podem ser acessados em JavaScript. Como resultado, eles não são tão vulneráveis a ataques XSS quanto o armazenamento local.
- Se você usar um sinalizador
HttpOnly
e cookies seguros, significa que o JavaScript não pode acessar esses arquivos. Ou seja, mesmo que um invasor consiga executar seu código em sua página, ele não conseguirá ler o token de acesso do cookie. - Os cookies são enviados automaticamente em cada solicitação HTTP ao servidor.
▍Desvantagens
Dependendo das circunstâncias específicas, pode acontecer que os tokens nos cookies não possam ser armazenados.
- O tamanho dos cookies é limitado a 4 KB. Portanto, se você usar JWTs grandes, armazená-los em cookies não funcionará para você.
- Existem cenários em que você não pode passar cookies para o seu servidor API. Também é possível que alguma API exija a colocação de um token no cabeçalho
Authorization
. Nesse caso, você não poderá armazenar tokens em cookies.
Ataques XSS
O armazenamento local é vulnerável a ataques XSS porque é muito fácil trabalhar usando JavaScript. Portanto, um invasor pode obter acesso ao token e usá-lo em sua vantagem. No entanto, embora os cookies HttpOnly não sejam acessíveis a partir do JavaScript, isso não significa que você está protegido contra ataques XSS usando cookies para roubar um token de acesso.
Se um invasor pode executar seu código JS em seu aplicativo, isso significa que ele pode simplesmente enviar uma solicitação ao seu servidor, e o token será incluído nesta solicitação automaticamente. Esse esquema de trabalho simplesmente não é tão conveniente para o invasor, já que ele não pode ler o conteúdo do token. Mas os invasores raramente precisam disso. Além disso, com esse esquema de trabalho, pode ser mais lucrativo para um invasor atacar o servidor usando o computador da vítima, em vez do seu próprio.
Ataques de cookies e CSRF
Ataques CSRF são ataques em que um usuário é de alguma forma coagido a fazer uma solicitação especial. Por exemplo, o site aceita solicitações para alterar o endereço de e-mail:
POST /email/change HTTP/1.1
Host: site.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 50
Cookie: session=abcdefghijklmnopqrstu
email=myemail.example.com
Em tal situação, um invasor pode criar um formulário com um campo oculto para inserir um endereço de e-mail que envia uma solicitação POST para
https://site.com/email/change
. Nesse caso, os cookies de sessão serão incluídos automaticamente em tal solicitação.
No entanto, essa ameaça pode ser facilmente protegida usando o atributo
SameSite
no cabeçalho de resposta e os tokens anti-CSRF .
Subtotais
Embora os cookies não sejam completamente imunes a ataques, a melhor maneira de armazenar tokens é, sempre que possível, escolhendo-os
localStorage
. Por quê?
- Tanto o armazenamento local quanto os cookies são vulneráveis a ataques XSS, mas será mais difícil para um invasor atacar se cookies HttpOnly forem usados.
- Os cookies são vulneráveis a ataques de CSRF, mas o risco de tais ataques pode ser atenuado usando o atributo
SameSite
e tokens anti-CSRF .
Os cookies podem ser usados mesmo quando você precisa usar um cabeçalho
Authorization: Bearer
ou quando o JWT é maior que 4 KB. Isso também é consistente com as diretrizes do OWASP: “Não armazene IDs de sessão no armazenamento local, pois os dados correspondentes estão sempre disponíveis em JavaScript. Os cookies podem ajudar a reduzir o seu risco com HttpOnly
. "
Uso de cookies para armazenar tokens OAuth 2.0
Vamos listar resumidamente as maneiras de armazenar tokens:
- Método 1: armazenar tokens no armazenamento local. Este método é suscetível a ataques XSS.
- Método 2: armazenar tokens em cookies HttpOnly. Este método é suscetível a ataques CSRF, mas o risco de tais ataques pode ser mitigado. Essa opção de armazenamento de token é um pouco melhor protegida de ataques XSS do que a primeira.
- Método 3: armazene tokens de atualização em cookies HttpOnly e tokens de acesso na memória. Essa maneira de armazenar tokens é mais segura em termos de ataques CSRF e um pouco melhor protegida contra ataques XSS.
A seguir, daremos uma olhada mais de perto no terceiro método de armazenamento de tokens, uma vez que, dos três listados, parece o mais interessante.
Por que armazenar o token de atualização em um cookie HttpOnly é mais seguro em termos de ataques CSRF?
Um invasor pode criar um formulário de acesso
/refresh_token
. Um novo token de acesso é retornado em resposta a esta solicitação. Mas o invasor não pode ler a resposta se usar um formulário HTML. Para evitar que um atacante execute com sucesso fetch ou solicitações AJAX e leia as respostas, a política CORS do servidor de autorização deve ser configurada corretamente, ou seja, para que o servidor não responda às solicitações de sites não autorizados.
Como você configura?
Etapa 1: devolver o token de acesso e atualizar o token ao autenticar o usuário
Após a autenticação do usuário, o servidor de autenticação retorna
access_token
(token de acesso) e refresh_token
(token de atualização). O token de acesso será incluído no corpo da resposta e o token de atualização no cookie.
Aqui está o que você precisa usar para configurar cookies para armazenar tokens de atualização:
- Sinalizar
HttpOnly
- para evitar que o JavaScript leia o token. - Um sinalizador
secure=true
que fará com que os dados sejam transmitidos apenas por HTTPS. - O sinalizador
SameSite=strict
deve ser usado sempre que possível para proteger contra ataques CSRF. Essa abordagem só pode ser usada se o servidor de autorização pertencer ao mesmo site que o front-end do sistema. Se esse não for o caso, o servidor de autorização deve definir cabeçalhos CORS no back-end ou usar outros métodos para garantir que uma solicitação com um token de atualização só possa ser feita por um site autorizado.
Etapa 2: armazenar o token de acesso na memória
Armazenar o token de acesso na memória significa que o token, no código de front-end, é gravado em uma variável. Isso, é claro, significa que o token será perdido se o usuário fechar a guia onde o site está aberto ou atualizar a página. É por isso que temos um token de atualização.
Etapa 3: obter um novo token de acesso usando o token de atualização
Se o token de acesso for perdido ou inválido, você precisará entrar em contato com o terminal
/refresh_token
. Nesse caso, o token de atualização que foi salvo no cookie na etapa 1 será incluído na solicitação. Você receberá um novo token de acesso que pode ser usado para fazer solicitações de API.
Tudo isso significa que os JWTs podem ser maiores do que 4 KB e podem ser colocados no cabeçalho
Authorization
.
Resultado
O que abordamos aqui deve fornecer algumas informações básicas sobre como armazenar JWTs no cliente e como tornar seu projeto mais seguro.
Como você armazena JWT no cliente?
