Comprensión de los tokens web JSON (JWT)

C

Durante mucho tiempo, la autenticación de usuarios en la web consistió 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 requiere 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 dato dedicado almacenar, como Redis o una base de datos, que se sumaba a 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é realmente querría usarlos.

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

¿Por qué utilizar JSON Web Tokens?

Con las cookies de sesión clásicas que se utilizan en la mayoría de los sitios web, usted almacena un token en el lado del cliente en una cookie del navegador (normalmente), que luego se utiliza 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, en cambio, puede almacenar los datos de esta sesión en el lado del cliente sin tener que preocuparse de que sean manipulados por el usuario u otro tercero, gracias a que está firmado por el servidor. Su cookie de sesión clásica también está firmada / encriptada, pero al menos ahora los datos residen con 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 los JWT. Dado que los datos están en formato JSON, puede utilizar estructuras de datos profundamente anidadas. Claro, esto también es posible con el lado del servidor de datos de sesión, pero nuevamente, con los JWT no es necesario lidiar 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 normalmente allí? El estandar (RFC 7519) no especifica un tamaño máximo para el token, por lo que, en teoría, puede almacenar tantos datos como desee, siempre que no exceda 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 vencimiento del token: en la mayoría de los casos, los tokens no deberían durar para siempre y el usuario debería tener que 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 un almacenamiento local y no en cookies, aunque también se pueden usar cookies. Esta es la forma recomendada ya que Uso compartido de recursos entre orígenes (CORS) no utiliza cookies de forma predeterminada. 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 es Base64-encoded, mientras que la firma se crea alimentando el encabezado y la carga útil a través de un algoritmo de firma (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 ningún tercero no autorizado haya falsificado datos.

La estructura general del token se parece a esto:

hhhhhhhh.ppppppppp.sssssssss

Dónde 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 indicará el tipo (“tipo”) de token que es (que recomendamos usar siempre “JWT”) y el algoritmo utilizado para firmar el token:

{
    "typ":"JWT",
    "alg":"HS256"
}

En este ejemplo, el encabezado afirma que se utilizó “HS256” o HMAC-SHA256 para firmar el token.

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

La carga útil

La carga útil del token es donde realmente puedes transmitir tu información a la aplicación. Para intentar 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 mediante “reclamaciones”. Hay tres tipos de reclamos definidos por JWT:

  • Reclamaciones registradas: estas son reclamaciones registradas en el Reclamaciones de token web JSON de IANA registro. No es necesario configurar ninguno de estos, pero se dan como punto de partida para aumentar la interoperabilidad entre aplicaciones. Algunos ejemplos son el emisor (“iss”), el asunto (“sub”), la audiencia (“aud”) y el tiempo de vencimiento (“exp”). Se prefieren los nombres de reclamo cortos para limitar la longitud del token.
  • Reclamos públicos: estos 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 reclamaciones de IANA JSON Web Token o utilizar un nombre resistente a colisiones.
  • Reclamaciones privadas: esta suele ser la información más específica de su aplicación. Si bien una reclamación pública puede contener información como “nombre” y “correo electrónico”, las reclamaciones privadas serían más específicas, como “ID de usuario” o “alcance de autorización”.

Si bien creo que es importante adherirse al estándar JWT en lo que respecta a las afirmaciones de nombres, no está obligado a utilizar ninguno de los que hemos mencionado aquí (o cualquier otro especificado en el estándar). En su lugar, puede usar los suyos, 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 sea conocida por la aplicación de origen.

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

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

Normalmente, el algoritmo criptográfico utilizado es HMAC con SHA-256 o un RSA firma con SHA-256.

Además de esto, existe otro estándar que especifica muchos más algoritmos para usar en la autenticación y el cifrado basados ​​en tokens. Este es el Algoritmo web JSON (JWA) estándar. Si necesita más opciones sobre cifrado 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. Aquí están los datos con los que trabajaremos:

Encabezamiento

{
  "alg": "HS256",
  "typ": "JWT"
}

Carga útil

{
  "sub": "123456789",
  "name": "Scott Robinson",
  "awesome": true
}

Puede recordar de anteriormente en este artículo que el token resultante debe tener la forma de:

hhhhhhhh.ppppppppp.sssssssss

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

base64Header = base64Encode(header)
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Hacer lo mismo con la carga útil desde arriba produce:

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.

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

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

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

Esto es bastante sencillo de hacer por su cuenta, pero probablemente no tenga que hacerlo. Todavía tengo que usar un lenguaje de programación que aún no tiene 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:

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 útil 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.

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 falta 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 enumeré 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 vale la pena, en mi opinión. En mi opinión, la flexibilidad de poder almacenar de forma 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 pueden parecer inicialmente. Le recomiendo encarecidamente que lea el RFC para tokens web JSON. Es sorprendentemente fácil de leer y proporciona mucha información útil sobre el estándar. Si va a utilizar 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 JWT en sus aplicaciones? ¿Cómo fue tu experiencia al usarlos? ¡Háznoslo saber en los comentarios!

 

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con tus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad