¿Almacenamiento local o cookies? Almacenar JWT de forma segura en el cliente

JWT (JSON Web Token) es un maravilloso estándar basado en JSON para crear tokens de acceso que se utilizan comúnmente para la autenticación en aplicaciones cliente / servidor. Al usar estos tokens, surge la pregunta sobre cómo almacenarlos de manera segura en el front-end de la aplicación. Este problema debe resolverse inmediatamente después de que se genere el token en el servidor y se transfiera al lado del cliente de la aplicación. El material, cuya traducción publicamos hoy, está dedicado al análisis de los pros y contras de utilizar el almacenamiento local del navegador ( ) y las cookies para almacenar JWT.







localStorage



Tipos de tokens



  • Los tokens de acceso suelen ser JWT de corta duración firmados por el servidor. Se incluyen en cada solicitud HTTP que un cliente realiza a un servidor. Los tokens se utilizan para autorizar solicitudes.
  • Los tokens de actualización suelen ser tokens de larga duración que se almacenan en una base de datos y se utilizan para obtener un nuevo token de acceso cuando expira el token anterior.


¿Dónde exactamente deben almacenarse los tokens en el cliente?



Hay 2 formas comunes de almacenar tokens en el cliente: almacenamiento del navegador local y cookies. Existe un gran debate sobre qué método es mejor. La mayoría de las personas se inclinan por las cookies debido a su mayor seguridad.



Comparemos el almacenamiento local y las cookies. Nuestra comparación se basa principalmente en este material y en los comentarios al mismo.



Almacenamiento local



▍Ventajas



La principal ventaja del almacenamiento local es que es cómodo de usar.



  • Trabajar con almacenamiento local es muy conveniente, aquí se usa JavaScript puro. Si su aplicación no tiene un backend y depende de API de terceros, es posible que no siempre pueda solicitar estas API para configurar cookies específicas para su sitio.
  • Al usar el almacenamiento local, es conveniente trabajar con API que requieren colocar un token de acceso en el encabezado de la solicitud. Por ejemplo - como sigue: Authorization Bearer ${access_token}.


▍Desventajas



La principal desventaja del almacenamiento local es su vulnerabilidad a los ataques XSS.



  • Al realizar un ataque XSS, un atacante puede ejecutar su código JavaScript en su sitio. Esto significa que un atacante puede obtener acceso al token de acceso almacenado en localStorage.
  • La fuente del ataque XSS puede ser un código JavaScript de terceros incluido en su sitio. Podría ser algo como React, Vue, jQuery, script de Google Analytics, etc. En las condiciones modernas, es casi imposible desarrollar un sitio que no incluya bibliotecas de terceros.


Galletas



▍Ventajas



La principal ventaja de las cookies es que no se puede acceder a ellas desde JavaScript. Como resultado, no son tan vulnerables a los ataques XSS como el almacenamiento local.



  • Si usa una bandera HttpOnlyy cookies seguras, significa que JavaScript no puede acceder a estos archivos. Es decir, incluso si un atacante puede ejecutar su código en su página, no podrá leer el token de acceso de la cookie.
  • Las cookies se envían automáticamente en cada solicitud HTTP al servidor.


▍Desventajas



Dependiendo de las circunstancias específicas, puede suceder que los tokens de las cookies no se puedan almacenar.



  • El tamaño de las cookies está limitado a 4 KB. Por lo tanto, si utiliza JWT grandes, almacenarlos en cookies no funcionará para usted.
  • Hay escenarios en los que no puede pasar cookies a su servidor API. También es posible que alguna API requiera colocar un token en el encabezado Authorization. En este caso, no podrá almacenar tokens en cookies.


Ataques XSS



El almacenamiento local es vulnerable a los ataques XSS porque es muy fácil trabajar con JavaScript. Por lo tanto, un atacante puede obtener acceso al token y usarlo en su beneficio. Sin embargo, aunque las cookies de HttpOnly no son accesibles desde JavaScript, esto no significa que esté protegido contra ataques XSS mediante el uso de cookies para robar un token de acceso.



Si un atacante puede ejecutar su código JS en su aplicación, esto significa que simplemente puede enviar una solicitud a su servidor, y el token se incluirá en esta solicitud automáticamente. Este esquema de trabajo simplemente no es tan conveniente para el atacante, ya que no puede leer el contenido del token. Pero los atacantes rara vez necesitan esto. Además, con este esquema de trabajo, puede ser más rentable para un atacante atacar el servidor utilizando la computadora de la víctima, en lugar de la suya propia.



Ataques de cookies y CSRF



Los ataques CSRF son ataques en los que se obliga a un usuario a realizar una solicitud especial. Por ejemplo, el sitio acepta solicitudes para cambiar la dirección de correo electrónico:



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


En tal situación, un atacante puede crear un formulario con un campo oculto para ingresar una dirección de correo electrónico a la que envía una solicitud POST https://site.com/email/change. Esto incluirá automáticamente las cookies de sesión en dicha solicitud.



Sin embargo, esta amenaza se puede proteger fácilmente mediante el uso del atributo SameSiteen el encabezado de respuesta y tokens anti-CSRF .



Subtotales



Aunque las cookies no son completamente inmunes a los ataques, la mejor manera de almacenar tokens es, siempre que sea posible, elegirlas localStorage. ¿Por qué?



  • Tanto el almacenamiento local como las cookies son vulnerables a los ataques XSS, pero será más difícil para un atacante atacar si se utilizan cookies HttpOnly.
  • Las cookies son vulnerables a los ataques CSRF, pero el riesgo de tales ataques se puede mitigar utilizando el atributo SameSitey los tokens anti-CSRF .


Las cookies se pueden usar incluso cuando necesita usar un encabezado Authorization: Bearero cuando el JWT es más grande que 4 KB. Esto también es consistente con las pautas de OWASP: “No almacene los ID de sesión en el almacenamiento local, ya que los datos correspondientes siempre están disponibles en JavaScript. Las cookies pueden ayudar a mitigar el riesgo HttpOnly".



Uso de cookies para almacenar tokens de OAuth 2.0



Enumeremos brevemente las formas de almacenar tokens:



  • Método 1: almacenar tokens en almacenamiento local. Este método es susceptible a ataques XSS.
  • Método 2: almacenar tokens en cookies HttpOnly. Este método es susceptible a ataques CSRF, pero el riesgo de tales ataques puede mitigarse. Esta opción de almacenamiento de tokens está ligeramente mejor protegida contra ataques XSS que la primera.
  • Método 3: almacene tokens de actualización en cookies HttpOnly y acceda a tokens en la memoria. Esta forma de almacenar tokens es más segura en términos de ataques CSRF y está ligeramente mejor protegida contra ataques XSS.


A continuación, veremos más de cerca la tercera forma de almacenar tokens, ya que, de las tres enumeradas, parece la más interesante.



¿Por qué almacenar el token de actualización en una cookie HttpOnly es más seguro en términos de ataques CSRF?



Un atacante podría crear un formulario que acceda /refresh_token. Se devuelve un nuevo token de acceso en respuesta a esta solicitud. Pero el atacante no puede leer la respuesta si usa un formulario HTML. Para evitar que un atacante ejecute con éxito las solicitudes de recuperación o AJAX y lea las respuestas, la política CORS del servidor de autorización debe configurarse correctamente, es decir, para que el servidor no responda a las solicitudes de sitios web no autorizados.



¿Cómo lo configuras?



Paso 1: devuelva el token de acceso y actualice el token al autenticar al usuario



Después de que el usuario se autentica, el servidor de autenticación regresa access_token(token de acceso) y refresh_token(token de actualización). El token de acceso se incluirá en el cuerpo de la respuesta y el token de actualización en la cookie.



Esto es lo que necesita usar para configurar cookies para almacenar tokens de actualización:



  • Marcar HttpOnly: para evitar que JavaScript lea el token.
  • Una bandera secure=trueque hará que los datos se transmitan solo a través de HTTPS.
  • La bandera SameSite=strictdebe usarse siempre que sea posible para proteger contra ataques CSRF. Este enfoque solo se puede utilizar si el servidor de autorización pertenece al mismo sitio que la interfaz del sistema. Si este no es el caso, entonces el servidor de autorización debe establecer encabezados CORS en el backend o usar otros métodos para asegurarse de que una solicitud con un token de actualización solo la pueda realizar un sitio web autorizado.


Paso 2: almacena el token de acceso en la memoria



Almacenar el token de acceso en la memoria significa que el token, en el código de la interfaz, se escribe en una variable. Esto, por supuesto, significa que el token se perderá si el usuario cierra la pestaña donde está abierto el sitio o actualiza la página. Por eso tenemos un token de actualización.



Paso 3: obtener un nuevo token de acceso usando el token de actualización



Si el token de acceso se pierde o no es válido, debe comunicarse con el punto final /refresh_token. En este caso, el token de actualización que se guardó en la cookie en el paso 1 se incluirá en la solicitud. A continuación, recibirá un nuevo token de acceso que puede utilizar para realizar solicitudes de API.



Todo esto significa que los JWT pueden tener un tamaño superior a 4 KB y que se pueden colocar en el encabezado Authorization.



Salir



Lo que hemos cubierto aquí debería brindarle información básica sobre el almacenamiento de JWT en el cliente y cómo hacer que su proyecto sea más seguro.



¿Cómo almacena JWT en el cliente?






All Articles