Comprender los tokens web JSON (JWT)

Durante mucho tiempo, la autenticación de usuarios en la web consistía en almacenar algunos datos muy simples (como una identificación de usuario) en el navegador del usuario como una cookie. Esto funcionó bastante...

Durante mucho tiempo, la autenticación de usuarios en la web consistía en almacenar algunos datos muy simples (como una identificación de usuario) en el navegador del usuario como una cookie. Esto funcionó bastante bien (y todavía lo hace para muchas aplicaciones), pero a veces se necesita más flexibilidad.

Tradicionalmente, para obtener esta flexibilidad, tenía que almacenar 'estado' en el servidor, lo que podría decirle cosas como quién es el usuario, qué tipo de permisos tiene, etc. Para almacenar estos datos, generalmente tenía que tener un almacén de datos dedicado, como Redis o una base de datos, lo que aumenta la complejidad de su aplicación.

En los últimos años, ha aparecido un nuevo estándar abierto que está siendo adoptado cada vez más por algunos de los principales sitios web y aplicaciones. Este estándar es el Token web JSON (JWT). A lo largo de este artículo, le mostraremos cómo funcionan y, lo que es más importante, por qué querría usarlos.

Nota: el estándar JWT se vuelve un poco más complejo con JWS y JWE adicionales ), por lo que para este artículo nos centraremos solo en lo que se especifica para JWT.

¿Por qué usar tokens web JSON? {#por qué usar tokens web de json}

Con las cookies de sesión clásicas utilizadas en la mayoría de los sitios web, almacena un token en el lado del cliente en una cookie del navegador (normalmente), que luego se usa para buscar los datos de su sesión en el lado del servidor. Esto funciona bien para la mayoría de los sitios web simples, pero debe adaptarse a todos estos datos adicionales del lado del servidor con algún tipo de almacén de datos.

Por otro lado, con los JWT, puede almacenar estos datos de sesión en el lado del cliente sin tener que preocuparse de que el usuario u otro tercero los manipulen, gracias a que están firmados por el servidor. Su cookie de sesión clásica también está firmada/cifrada, pero al menos ahora los datos residen en el usuario.

El almacenamiento del lado del cliente de datos también reduce cierta complejidad en el lado del servidor, ya que ya no necesita un almacén de datos de baja latencia para almacenar y recuperar datos de sesión con frecuencia.

También obtiene un poco más de flexibilidad con el tipo de datos que almacena con JWT. Dado que los datos están en formato JSON, puede utilizar estructuras de datos profundamente anidadas. Claro, esto también es posible hacerlo con el lado del servidor de datos de sesión, pero nuevamente, con JWT no necesita tratar con otra base de datos.

Entonces, además de reducir la complejidad y aumentar la flexibilidad, ¿por qué más usaría un JWT? ¿Qué tipo de datos se almacenan típicamente allí? El estándar (RFC 7519) no especifica un tamaño máximo para el token, por lo que teóricamente puede almacenar tantos datos como desee, siempre que no excede el tamaño máximo de su medio de almacenamiento. Sin embargo, esto no es realmente recomendable ya que se supone que los JWT son lo más compactos posible.

La mayoría de las aplicaciones con las que me he encontrado se beneficiarían de tener al menos la siguiente información:

  • Identificación de usuario (un UID, correo electrónico, etc.): bueno para buscar fácilmente más detalles sobre el usuario en su base de datos
  • Marca de tiempo de caducidad del token: en la mayoría de los casos, los tokens no deberían durar para siempre y el usuario debería volver a autenticarse
  • ID de JWT: bueno para revocar un JWT, lo que obliga a un usuario a tener que volver a iniciar sesión

Por supuesto, puede tener mucha más información que esta en su token, pero este suele ser un buen punto de partida para una nueva aplicación.

También se recomienda que los JWT se almacenen en el almacenamiento local y no en cookies, aunque también se pueden usar cookies. Esta es la forma recomendada ya que Intercambio de recursos de origen cruzado (CORS) no usa cookies por defecto. En su lugar, debe enviar un JWT en el encabezado 'Autorización' utilizando el esquema 'Portador'.

Ahora veamos de qué se compone un JWT en la siguiente sección.

Estructura

Un token web JSON se compone de tres secciones: un encabezado, una carga útil y una firma. Tanto el encabezado como la carga útil almacenan datos en formato JSON, que está codificado en Base64, mientras que la firma se crea alimentando el encabezado y la carga útil a través de una firma. algoritmo (que se especifica en el encabezado) junto con un secreto. Con esta firma, se puede verificar la autenticidad del token y se puede verificar que un tercero no autorizado falsificó ningún dato.

La estructura general del token se parece a esto:

1
hhhhhhhh.ppppppppp.sssssssss

Donde h es el encabezado, p es la carga útil y s es la firma.

El encabezado

La sección de encabezado está destinada a proporcionar información importante sobre el token. Por lo general, le dirá el tipo ("typ") de token que es (que recomendamos usar siempre "JWT"), y el algoritmo utilizado para firmar el token:

1
2
3
4
{
    "typ":"JWT",
    "alg":"HS256"
}

En este ejemplo, el encabezado afirma que "HS256", o HMAC-SHA256, se usó para firmar el token.

Si su JWT es un poco más complejo y tiene una firma o encriptación anidada, también debe usar el parámetro de encabezado "cty" con un valor de "JWT", de lo contrario, puede omitirse. Por otro lado, un JWT sin firma o cifrado debe tener un valor de "alg" de "ninguno".

La carga útil {#la carga útil}

La carga útil del token es donde realmente puede transmitir su información a la aplicación. Para tratar de hacer que los JWT sean más interoperables entre varias aplicaciones, se han establecido algunos estándares para definir qué y cómo se comunican ciertos datos. Esto se hace usando "reclamaciones". Hay tres tipos de reclamos definidos por JWT:

  • Registered Claims: Son reclamaciones registradas en el registro Reclamos de token web JSON de IANA. No es necesario configurar ninguno de estos, pero se proporcionan como punto de partida para aumentar la interoperabilidad entre las aplicaciones. Algunos ejemplos son el emisor ("iss"), el asunto ("sub"), la audiencia ("aud") y el tiempo de vencimiento ("exp"). Se prefieren los nombres de notificación cortos para limitar la longitud del token.
  • Reclamos públicos: son reclamos que se pueden definir "a voluntad", lo que significa que no hay restricciones explícitas. Para evitar colisiones entre nombres, debe registrarlos en el registro de reclamos de token web JSON de IANA o usar un nombre resistente a colisiones.
  • Reclamos privados: Esta suele ser la información más específica para su aplicación. Mientras que un reclamo público puede contener información como "nombre" y "correo electrónico", los reclamos privados serían más específicos, como "ID de usuario" o "ámbito de autorización".

Si bien creo que es importante adherirse al estándar JWT con respecto a los reclamos de nombres, no es necesario que use ninguno de los que hemos mencionado aquí (o cualquier otro especificado dentro del estándar). En su lugar, puede usar los suyos propios, pero no espere que sus tokens tengan interoperabilidad con otras aplicaciones de terceros.

La Firma

La firma se crea utilizando la codificación Base 64 (más sobre esto a continuación) del encabezado y la carga útil, que se concatenan con un punto ('.'). Para que esta firma funcione, se debe utilizar una clave secreta que solo conoce la aplicación de origen.

El pseudocódigo para crear la firma se vería así:

1
2
unsignedToken = base64Encode(header) + '.' + base64Encode(payload)
signature = HS256('your-secret-key', unsignedToken) 

Normalmente, el algoritmo criptográfico utilizado es HMAC con [SHA-256] (https://en.wikipedia.org/wiki/SHA-256) o [RSA] (https://en.wikipedia.org/wiki/Digital_signature ). ) firma con SHA-2

Además de esto, existe otro estándar que especifica muchos más algoritmos para su uso en la autenticación y el cifrado basados ​​en tokens. Este es el estándar Algoritmo web JSON (JWA). Si necesita más opciones de encriptación y firma, consulte esto.

Codificación JWT

En esta sección, veremos un ejemplo real de cómo tomar nuestros datos JSON y crear un token web a partir de ellos. Estos son los datos con los que trabajaremos:

Encabezamiento

1
2
3
4
{
  "alg": "HS256",
  "typ": "JWT"
}

Carga útil

1
2
3
4
5
{
  "sub": "123456789",
  "name": "Scott Robinson",
  "awesome": true
}

Tal vez recuerdes de las páginas anteriores de este artículo que el token resultante debe tener la forma de:

1
hhhhhhhh.ppppppppp.sssssssss

El primer paso es codificar URL Base64 para cada sección. Codificar el encabezado desde arriba nos da lo siguiente:

1
2
base64Header = base64Encode(header)
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Hacer lo mismo con la carga útil de los rendimientos anteriores:

1
2
base64Payload = base64Encode(payload)
// eyJzdWIiOiIxMjM0NTY3ODkiLCJuYW1lIjoiU2NvdHQgUm9iaW5zb24iLCJhd2Vzb21lIjp0cnVlfQ

Desde aquí necesitamos generar nuestra firma. Como probablemente haya notado en los datos del encabezado, usaremos el algoritmo HS256 para generarlo.

1
2
signature = HS256(base64Header + '.' + base64Payload, 'super-sekret')
// WwR-0ZlhUBRkBlUBZ6l6lWvBZNGmdAsageRCvry3bY0

Para obtener el token final, juntamos estas tres partes, uniendo cada una con un punto ('.'), así:

1
2
token = base64Header + '.' + base64Payload + '.' + signature
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkiLCJuYW1lIjoiU2NvdHQgUm9iaW5zb24iLCJhd2Vzb21lIjp0cnVlfQ.WwR-0ZlhUBRkBlUBZ6l6lWvBZNGmdAsageRCvry3bY0

Esto es bastante sencillo de hacer por tu cuenta, pero probablemente no tengas que hacerlo. Todavía tengo que usar un lenguaje de programación que aún no tenga una biblioteca para generar tokens JWT como este para usted. Para obtener enlaces a algunas de las bibliotecas más populares, consulte la sección bibliotecas a continuación.

Decodificación y verificación de JWT

Ahora que sabemos cómo codificar un JWT, la decodificación es bastante fácil. Comenzamos dividiendo el token por puntos y luego decodificamos cada sección por separado:

1
2
3
4
5
6
base64Header, base64Payload, signature = token.split('.')

header = base64Decode(base64Header)
payload = base64Decode(base64Payload)

// Read the header and payload data here

La parte un poco más complicada es cuando necesitamos verificar la firma. Hacemos esto recreando la firma del encabezado y la carga usando nuestro secreto, y luego verificamos si coincide con la firma que nos dieron. Si no es así, entonces el token no es auténtico o se ha alterado de alguna manera.

1
2
3
4
computedSig = HS256(base64Header + '.' + base64Payload, 'super-sekret')

if computedSig != signature:
    print('FAILED')

Tenga en cuenta que, en la mayoría de los casos, debe verificar el encabezado para ver qué algoritmo se usó en la firma, pero podemos omitir esa parte aquí para nuestros propósitos.

Bibliotecas

Afortunadamente, no hay falta de soporte para JWT en todos los lenguajes web populares. Aquí hay algunas bibliotecas notables que vale la pena consultar:

Hay bastantes más que no mencioné aquí, pero estos son los que encontrará más comúnmente para el desarrollo de aplicaciones web.

Conclusión

Es un pequeño cambio de la forma tradicional de hacer las cosas, pero en mi opinión vale la pena. En mi opinión, la flexibilidad de poder almacenar de manera ordenada y * segura * más datos del lado del cliente supera cualquier confusión menor que pueda tener sobre los JWT al principio. Solo recuerde, los JWT no necesitan usarse en todas las aplicaciones, pero si el suyo tiene cierta complejidad, entonces puede beneficiarse de ellos.

Esperamos que este artículo te haya ayudado a darte cuenta de que no son tan confusos como inicialmente pueden parecer. Recomiendo encarecidamente que lea [RFC para tokens web JSON] (https://tools.ietf.org/html/rfc7519). Es sorprendentemente fácil de leer y proporciona mucha información útil sobre el estándar. Si va a usar esto como autorización para las cuentas de sus usuarios, entonces vale la pena tomarse el tiempo para comprender todos los detalles detrás de los JWT.

¿Cómo ha utilizado los JWT en sus aplicaciones? ¿Cómo fue tu experiencia usándolos? ¡Cuéntanos en los comentarios! *