¿Qué es JWT? Tokens web JSON explicados con un ejemplo práctico
Un JWT permite que un servidor confíe en una solicitud sin buscar nada. Cree uno desde cero, intente manipularlo y observe cómo falla, luego aprenda cuándo los tokens superan las sesiones y cómo usarlos de forma segura.
- Un JWT es un token compacto y firmado con tres partes: encabezado, carga útil y firma.
- La firma demuestra que un token no ha sido modificado, por lo que el servidor puede confiar en él sin necesidad de realizar una búsqueda.
- La carga útil es legible para cualquiera, así que nunca le pongas secretos.
- Los tokens son difíciles de revocar, así que manténgalos de corta duración y verifíquelos con una biblioteca confiable.
Después de iniciar sesión, ¿cómo sabe un servidor que cada solicitud posterior realmente proviene de usted? Una respuesta popular es un Token web JSON, o JWT, que generalmente se pronuncia “jota”. Es un pequeño dato firmado que el cliente envía con cada solicitud y el servidor puede verificarlo sin tocar una base de datos.
La forma más rápida de entender un JWT es construir uno y luego intentar romperlo. Lo hicimos usando solo la biblioteca estándar de Python, en Python 3.13, y el resultado a continuación proviene de esa ejecución. Este código es para aprender. En una aplicación real, utilice una biblioteca bien mantenida, porque es fácil equivocarse en los detalles de verificación.
¿Qué es un JWT?
JWT es un estándar abierto, definido en RFC 7519, para transmitir reclamaciones entre dos partes como una cadena compacta. Un reclamo es simplemente una declaración, como “el ID de este usuario es 42”. Como el token está firmado, el receptor puede confirmar quién lo emitió y que nadie lo ha alterado.
Piense en una pulsera de festival. El personal en la puerta revisa tu boleto una vez y te entrega una banda que es difícil de falsificar. Después de eso, cada puesto sólo mira a la banda, sin necesidad de llamar a la taquilla. Un JWT funciona de forma muy similar a una API.
Cómo funciona el inicio de sesión de JWT
- El usuario envía un nombre de usuario y contraseña al punto final de inicio de sesión.
- El servidor los verifica y crea un token firmado.
- El cliente almacena el token.
- Cada solicitud posterior lo incluye, normalmente en un encabezado:
Authorization: Bearer <token>. - El servidor verifica la firma y la caducidad, luego lee las reclamaciones.
El principal beneficio está en el paso 5. El servidor no tiene que buscar una sesión, lo cual resulta conveniente cuando muchos servicios o servidores necesitan verificar el mismo token. Si es nuevo en las API, comience con qué es una API.
Las tres partes de un JWT
Un JWT parece texto aleatorio, pero consta de tres secciones codificadas en Base64URL separadas por puntos. Aquí hay uno que generamos:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJzdWIiOiI0MiIsInJvbGUiOiJ1c2VyIiwiaWF0IjoxNzkzNTAwMDAwLCJleHAiOjE3OTM1MDA5MDB9
.7YfUDH_T-iDh8bMmUT8mmKYwOstzT8Ty--DTTAmQGWM
Hemos agregado saltos de línea para que sea más fácil de leer. Al decodificar las dos primeras partes se obtiene JSON simple:
Header: {"alg":"HS256","typ":"JWT"}
Payload: {"sub":"42","role":"user","iat":1793500000,"exp":1793500900}
| parte | Contiene | Propósito |
|---|---|---|
| encabezado | Tipo de token y algoritmo de firma | Dice cómo verificar el token. |
| Carga útil | Reclamaciones sobre el usuario o sesión | La información real |
| Firma | Un hash de las dos primeras partes, usando una clave. | Demuestra integridad y origen. |
Reclamaciones estándar
| Reclamar | Nombre | Significado |
|---|---|---|
sub |
Asunto | De quién se trata el token, generalmente una identificación de usuario |
es |
Emisor | ¿Quién creó el token? |
aud |
Audiencia | ¿A quién está destinado el token? |
exp. |
Caducidad | Cuando el token deja de ser válido |
eso es |
Emitido en | Cuando fue creado |
nbf |
No antes | Cuando empieza a ser válido |
jti |
ID de JWT | Una identificación única para este token |
También puede agregar reclamos personalizados, como role. Los tiempos son marcas de tiempo de Unix, en segundos. Mantenga la carga útil pequeña, ya que el token viaja con cada solicitud.
Construye uno tú mismo
Crear un token implica codificar las dos partes JSON y luego firmarlas con un secreto usando HMAC-SHA256:
import base64, hashlib, hmac, json
SECRET = b"demo-secret-do-not-use-in-production"
def b64url(data):
return base64.urlsafe_b64encode(data).rstrip(b"=").decode()
def sign(message):
return b64url(hmac.new(SECRET, message.encode(), hashlib.sha256).digest())
def create_token(payload):
header = {"alg": "HS256", "typ": "JWT"}
parts = [
b64url(json.dumps(header, separators=(",", ":")).encode()),
b64url(json.dumps(payload, separators=(",", ":")).encode()),
]
message = ".".join(parts)
return message + "." + sign(message)
La verificación funciona recalculando la firma y comparándola con la del token. Sólo alguien con el secreto puede producir uno que coincida:
def verify_token(token, now):
header, payload, signature = token.split(".")
expected = sign(header + "." + payload)
if not hmac.compare_digest(signature, expected):
raise ValueError("bad signature")
claims = json.loads(b64url_decode(payload))
if claims["exp"] < now:
raise ValueError("token expired")
return claims
Un token válido devolvió sus reclamos como se esperaba. compare_digest se utiliza ya que se ejecuta en tiempo constante, lo que evita la fuga de información por diferencias de tiempo.
Intenta manipularlo
Supongamos que un atacante decodifica la carga útil, cambia "role":"user" a "role":"admin", la codifica nuevamente y conserva la firma anterior. Probamos esto, junto con otros dos ataques:
| intento | Resultado |
|---|---|
| Cambió el rol a administrador. | mala firma |
| Usó el token 1 segundo después de su vencimiento. | token caducado |
Establecí "alg":"none" y eliminé la firma. |
mala firma |
Ese último intento apunta a una verdadera debilidad histórica. Algunas bibliotecas alguna vez confiaron en el propio encabezado del token, por lo que un atacante podría simplemente declarar que no se necesitaba firma. Nuestro código siempre utiliza un algoritmo fijo, por eso fue rechazado. En una biblioteca real, especifique siempre los algoritmos que acepta.
Firmado, no cifrado
Este es el punto más incomprendido sobre los JWT. Nuestro token falsificado fue rechazado, pero su contenido aún era perfectamente legible:
{"sub":"42","role":"admin","iat":1793500000,"exp":1793500900}
Base64URL es una codificación, no una encriptación, y cualquiera puede revertirla sin ninguna clave. La firma impide que las personas cambien los datos, pero no lean. Por lo tanto, nunca coloque contraseñas, números de tarjetas o datos personales privados en una carga útil de JWT. Si el contenido debe ocultarse, utilice tokens cifrados (JWE) o simplemente mantenga los datos en el servidor.
HS256 frente a RS256
| Aspecto | HS256 | RS256 o ES256 |
|---|---|---|
| Tipo | simétrico | asimétrico |
| llaves | Un secreto compartido | Una clave privada firma, una clave pública verifica |
| ¿Quién puede crear tokens? | Cualquiera que tenga el secreto | Sólo el titular de la clave privada |
| Lo mejor para | Un servicio o un pequeño grupo de confianza | Muchos servicios, terceros, proveedores de identidad. |
Con la firma asimétrica, otros servicios pueden verificar tokens sin poder acuñar los suyos propios, lo que limita el daño si uno se ve comprometido.
JWT frente a sesiones
Los JWT a menudo se presentan como un reemplazo moderno de las sesiones. En realidad, cada uno tiene sus ventajas y desventajas:
| Aspecto | Sesión del servidor | JWT |
|---|---|---|
| donde vive el estado | en el servidor | en la ficha |
| Búsqueda en cada solicitud | Generalmente | No es necesario |
| Cerrar sesión o revocar | Fácil, elimina la sesión. | Duro antes de caducar |
| Tamaño por solicitud | Identificación pequeña | Token más grande (el nuestro tenía 161 caracteres) |
| En muchos servicios | Necesita almacenamiento compartido | Cada uno puede verificarlo directamente. |
| Cambiar permisos | Se aplica inmediatamente | El token antiguo sigue siendo válido hasta que caduque |
Para un sitio web tradicional generado por un servidor, una cookie de sesión suele ser más sencilla y segura. Los JWT tienden a tener más sentido para las API consumidas por varios servicios o para clientes móviles.
Consejo: No elija JWT solo porque suena sin estado. Muchos sistemas reales agregan una lista de denegados o una verificación de base de datos para admitir el cierre de sesión, lo que recupera la búsqueda que JWT debía evitar.
Dónde almacenar tokens en un navegador
| Ubicación | riesgo XSS | riesgo CSRF | Notas |
|---|---|---|---|
almacenamiento local |
Alto, como los scripts pueden leerlo. | Bajo | Simple, pero expuesto a cualquier script inyectado. |
| Cookie HttpOnly | Más bajo, ya que los scripts no pueden leerlo. | Necesita protección | Utilice Secure y SameSite |
| variable en memoria | inferior | Bajo | Se perdió al actualizar, por lo que necesita un flujo de actualización |
Muchas guías de seguridad favorecen las cookies HttpOnly, Secure, SameSite para aplicaciones web. Cualquiera que elijas, utiliza siempre HTTPS. Para enviar un token desde JavaScript, consulte nuestro tutorial de JavaScript Fetch API.
Actualizar fichas
El vencimiento corto limita el daño de un token robado, pero nadie quiere iniciar sesión cada 15 minutos. La solución habitual utiliza dos tokens:
- Token de acceso: de corta duración, como minutos, y se envía con cada solicitud de API.
- Token de actualización: tiene una vida más larga, se almacena con más cuidado y se usa solo para obtener nuevos tokens de acceso.
El servidor realiza un seguimiento de los tokens de actualización, por lo que puede revocarlos, lo que le brinda un cierre de sesión práctico. Rotarlos en cada uso también le ayuda a detectar robos, porque una ficha antigua robada se vuelve a reproducir.
Errores de seguridad comunes
- Decodificar sin verificar. Leer una carga útil no la hace confiable. Comprueba siempre la firma.
- Aceptar cualquier algoritmo. Establecer una lista explícita de algoritmos permitidos.
- Usar un secreto débil. Los secretos breves se pueden descifrar sin conexión. Utilice un valor aleatorio largo y manténgalo fuera del control de fuente.
- Omitiendo el vencimiento. Los tokens sin
exppueden funcionar para siempre si son robados. - No marcar
issyaud. De lo contrario, un token emitido para un servicio puede ser aceptado por otro. - Confiar en afirmaciones obsoletas. Una función dentro de un token puede estar desactualizada hasta que caduque.
- Tokens de registro. Cualquiera que lea sus registros podría reutilizarlos.
- No probar rutas de falla. Agregue pruebas para tokens vencidos y modificados, así como también válidos. Nuestra guía Pruebas de API con Postman puede resultar útil.
Conclusión
Un JWT son datos firmados en los que un servidor puede confiar sin una búsqueda, no un contenedor secreto ni una actualización automática a lo largo de las sesiones. Utilice una caducidad corta, verifique todo con una biblioteca mantenida y mantenga los datos privados fuera de la carga útil. Para comprobarlo usted mismo, ejecute un pequeño script como el nuestro, cambie un carácter de un token y observe cómo falla la verificación.
Preguntas frecuentes
¿Para qué se utiliza un JWT?
Los JWT se utilizan principalmente para autenticación y autorización en API. Después de iniciar sesión, un cliente envía el token con cada solicitud y el servidor lo verifica para identificar al usuario.
¿Está cifrado un JWT?
Generalmente no. Un JWT estándar está firmado y codificado, por lo que cualquiera puede leer su carga útil. La firma sólo impide cambios. Los tokens cifrados utilizan un formato relacionado llamado JWE.
¿Cuánto tiempo debe durar un JWT?
Los tokens de acceso suelen ser de corta duración, a menudo minutos en lugar de días. Combínelos con un token de actualización, para que los usuarios no tengan que iniciar sesión constantemente.
¿Se puede revocar un JWT?
No por sí solo, ya que sigue siendo válido hasta que caduque. La revocación requiere un seguimiento del lado del servidor, como una lista de denegados, que elimina parte del beneficio sin estado.
¿JWT es mejor que las sesiones?
Ninguno de los dos es siempre mejor. Las sesiones son más sencillas de revocar y se adaptan a muchos sitios web, mientras que los JWT se adaptan a las API y servicios que necesitan verificar la identidad de forma independiente.
sigue aprendiendo
¿Necesitas ayuda con pruebas, WordPress o SEO?
Ayudamos a los equipos a enviar mejor software y hacer crecer sus sitios. Cuéntenos en qué está trabajando y le sugeriremos un siguiente paso claro.
Más guías sobre testing, IA y desarrollo moderno
¿Te gustó este artículo? Elige un tema y salta directo a nuestros tutoriales más recientes.
Explicación de las alucinaciones de la IA: por qué los modelos de lenguaje inventan cosas y cómo reducirlas
Se explicaron las alucinaciones de la IA: qué son, por qué los modelos de lenguaje las producen, tipos comunes, cómo reducirlas con conexión a tierr…
Leer artículo →DevOpsExplicación de las variables de entorno: archivos .env, secretos y configuración bien hechos
Variables de entorno explicadas con ejemplos de Python que ejecutamos: leerlas y configurarlas, por qué cada valor es una cadena, archivos .env, her…
Leer artículo →Desarrollo de softwareExplicación de la limitación de velocidad de API: algoritmos, encabezados y manejo de errores 429
La limitación de la tasa de API se explica con una simulación que ejecutamos: depósito de tokens, ventana fija, ventana deslizante, 429 respuestas, …
Leer artículo →Automatización de pruebasBurlarse en Python: unittest.mock, patch y los errores que hacen que las pruebas mientan
Burlarse en Python con unittest.mock, probado: Mock y return_value, side_effect, parches donde se usa un nombre, especificación y autospec, fechas d…
Leer artículo →Pruebas de softwareExplicación de la cobertura del código: cobertura de línea versus sucursal y lo que realmente significa 100%
Cobertura del código explicada con una ejecución real de cobertura.py: cobertura de línea, rama y condición, lectura de un informe, por qué una cobe…
Leer artículo →Bases de datos y SQLDELETE vs TRUNCATE vs DROP en SQL: diferencias, reversión y cuándo usar cada uno
DELETE vs TRUNCATE vs DROP explicado con SQL probado: qué elimina cada uno, reversión, activadores, restablecimiento de identidad, velocidad y en qu…
Leer artículo →Bases de datos y SQLVistas SQL explicadas: cree, use y evite los errores, con ejemplos
Vistas SQL explicadas con consultas que ejecutamos: creación de una vista, resultados en vivo, ocultación de columnas, rendimiento, vistas actualiza…
Leer artículo →Desarrollo de softwareInterfaz TypeScript versus tipo: diferencias reales, probadas con el compilador
Interfaz TypeScript versus tipo, verificado con el compilador real: qué pueden hacer ambos, qué puede hacer solo uno, fusión de declaraciones, exten…
Leer artículo →Desarrollo de softwarePython *args y **kwargs explicados: funciones flexibles con ejemplos claros
Python *args y **kwargs explicados con el código que ejecutamos: empaquetado y desempaquetado, orden de parámetros, argumentos solo de palabras clav…
Leer artículo →Desarrollo de softwareFunciones Lambda de Python: sintaxis, usos reales y cuándo no utilizarlas
Funciones lambda de Python explicadas con el código que ejecutamos: sintaxis, clasificación con clave, mapa y filtro, cierres, trampa de enlace tard…
Leer artículo →Inteligencia artificial¿Qué son los agentes de IA? Cómo funcionan, con un bucle de agente simple que puedes ejecutar
¿Qué son los agentes de IA? Una guía sencilla sobre el bucle del agente, las herramientas, la memoria y las barreras de seguridad, con un pequeño bu…
Leer artículo →DevOpsMonitoreo versus observabilidad: la verdadera diferencia, con registros, métricas y seguimientos
Explicación del monitoreo versus observabilidad: fallas conocidas y desconocidas, registros, métricas y seguimientos, un recorrido por incidentes de…
Leer artículo →DevOpsDocker Volumes versus Bind Mounts: persistir los datos del contenedor de la manera correcta
Se explican los volúmenes de Docker frente a los montajes de enlace: por qué desaparecen los datos del contenedor, volúmenes con nombre, montajes de…
Leer artículo →Automatización de pruebasTDD vs BDD: diferencias clave, una función creada en ambos sentidos y cuándo usar cada una
TDD vs BDD explicado con una característica de descuento construida en ambos sentidos: refactor rojo-verde con el código que ejecutamos, escenarios …
Leer artículo →Pruebas de softwarePruebas de Aceptación de Usuario (UAT): Proceso, Lista de Verificación y Ejemplos Reales
Explicación de las pruebas de aceptación del usuario: quién las ejecuta, en qué se diferencia del control de calidad, un proceso de siete pasos, una…
Leer artículo →Bases de datos y SQLValores NULL de SQL explicados: IS NULL, COALESCE, NULLIF y las trampas que se deben evitar
Los valores NULL de SQL se explican con las consultas que ejecutamos: lógica de tres valores, IS NULL, COALESCE, NULLIF, cómo los agregados, las uni…
Leer artículo →Bases de datos y SQLDeclaración SQL CASE WHEN: sintaxis, ejemplos reales y errores comunes
CASO SQL CUANDO se explicó con las consultas que ejecutamos: formularios simples y buscados, orden de condiciones, pivotes con SUMA, trucos COUNT, c…
Leer artículo →Desarrollo de software¿Qué es JWT? Tokens web JSON explicados con un ejemplo práctico
¿Qué es JWT? JSON Web Tokens explicó: las tres partes, un token creado y manipulado en el código que ejecutamos, JWT vs sesiones, almacenamiento, to…
Leer artículo →Desarrollo de softwareExplicación del bucle de eventos de JavaScript: pila de llamadas, microtareas y temporizadores con ejemplos
El bucle de eventos de JavaScript explicado con el código que ejecutamos: pila de llamadas, colas de tareas y microtareas, por qué setTimeout 0 no e…
Leer artículo →Desarrollo de softwareExplicación de los generadores y el rendimiento de Python: ahorre memoria con ejemplos reales
Los generadores de Python y el rendimiento se explican con el código que ejecutamos: cómo se pausan y reanudan, midieron el ahorro de memoria, canal…
Leer artículo →Inteligencia artificial¿Qué son las incrustaciones? Los vectores y la similitud del coseno se explican de forma sencilla
¿Qué son las incrustaciones? Una guía en lenguaje sencillo sobre vectores, similitud de cosenos y búsqueda semántica, con un pequeño ejemplo que eje…
Leer artículo →DevOpsImplementación azul-verde versus implementación canaria: diferencias, compensaciones y cómo elegir
Explicación de la implementación azul-verde versus canaria: cómo funciona cada uno, costo, riesgo y reversión, agrupación estable de usuarios que pr…
Leer artículo →Automatización de pruebasPruebas inestables: qué las causa y cómo solucionarlas para siempre
Pruebas inestables explicadas con demostraciones que realizamos: las causas principales, cómo confirmar y diagnosticar una, correcciones adecuadas p…
Leer artículo →Pruebas de softwarePruebas de carga frente a pruebas de estrés: diferencias, métricas y ejemplos de k6
Explicación de las pruebas de carga frente a las pruebas de estrés: qué prueba cada una, pruebas de picos y de absorción, las métricas que importan,…
Leer artículo →Pruebas de softwareCaso de prueba frente a escenario de prueba: diferencias clave, ejemplos y cuándo utilizar cada uno
Caso de prueba versus escenario de prueba explicado: definiciones claras, una tabla en paralelo, una función de restablecimiento de contraseña tomad…
Leer artículo →Bases de datos y SQLOptimización de consultas SQL: lea planes de ejecución y corrija consultas lentas, con tiempos
Optimización de consultas SQL probada en un millón de filas: lea planes de ejecución, agregue los índices correctos, corrija filtros no sargables, u…
Leer artículo →Bases de datos y SQLSQL UNION vs UNION ALL: diferencias, ejemplos y rendimiento probado
SQL UNION vs UNION ALL explicado con consultas que ejecutamos: duplicados, las reglas que siguen ambos, un error de datos real, rendimiento medido, …
Leer artículo →Desarrollo de softwareTutorial de JavaScript Fetch API: GET, POST, manejo de errores y tiempos de espera
Un tutorial de JavaScript Fetch API probado en un servidor local: GET y POST, por qué 404 no arroja, un asistente reutilizable, cadenas de consulta,…
Leer artículo →Desarrollo de softwareManejo de archivos Python: lectura, escritura, CSV, JSON y pathlib con ejemplos
El manejo de archivos Python se explica con el código que ejecutamos: modos abiertos, lectura de archivos grandes, CSV, JSON, pathlib, archivos bina…
Leer artículo →Desarrollo de softwareClases y objetos de Python: tutorial de programación orientada a objetos con ejemplos reales
Clases y objetos de Python explicados con el código que ejecutamos: __init__, métodos, atributos de clase versus instancia, herencia, propiedades, m…
Leer artículo →Inteligencia artificial¿Qué es un modelo de lenguaje grande? Cómo funcionan los LLM, explicados sin exageraciones
¿Qué es un modelo de lenguaje grande? Los LLM explicados en lenguaje sencillo: tokens, incrustaciones, atención, entrenamiento, ventanas de temperat…
Leer artículo →DevOpsComandos de Linux para principiantes: la guía esencial de terminal con ejemplos
Comandos de Linux para principiantes, agrupados por tarea: navegación, archivos, visualización de registros, grep, canalizaciones, permisos, proceso…
Leer artículo →Automatización de pruebasSelenium vs Playwright: diferencias clave, comparación de códigos y cuál elegir
Comparación de Selenium vs Playwright: arquitectura, espera, soporte de navegador e idioma, depuración, la misma prueba en ambas herramientas, conse…
Leer artículo →Pruebas de softwarePartición de equivalencia: técnica de diseño de pruebas explicada con ejemplos resueltos
Explicación de la partición de equivalencia: cómo encontrar particiones válidas e inválidas, ejemplos prácticos para edades, contraseñas y cupones, …
Leer artículo →Pruebas de softwareSeveridad vs Prioridad en Pruebas de Software: Diferencias, Matriz y Ejemplos Reales
Explicación de gravedad versus prioridad en las pruebas de software: definiciones claras, una matriz de cuatro vías con ejemplos, escalas de niveles…
Leer artículo →Bases de datos y SQLTutorial de subconsultas SQL: tipos, subconsultas correlacionadas y EXISTENCIAS con ejemplos
Un tutorial de subconsultas SQL con consultas que ejecutamos: subconsultas escalares, IN, FROM y SELECT, subconsultas correlacionadas, EXISTS, la tr…
Leer artículo →Bases de datos y SQLTransacciones SQL y ACID explicados: COMMIT, ROLLBACK y niveles de aislamiento
Transacciones SQL y ACID explicadas con una transferencia bancaria que ejecutamos: COMMIT, ROLLBACK, puntos de guardado, bloqueos, niveles de aislam…
Leer artículo →Desarrollo de softwareGit merge vs rebase: diferencias, ejemplos y cuándo usar cada uno
Git merge vs rebase explicado con salida real del log, una tabla comparativa, la regla de oro del rebase y un flujo de trabajo seguro para equipos.
Leer artículo →Desarrollo de softwareList comprehension en Python: sintaxis, ejemplos y cuándo evitarla
List comprehension en Python explicada: sintaxis, filtros, if-else, bucles anidados, tiempos medidos y cuándo es mejor un bucle for normal.
Leer artículo →