18-cómo funciona la firma de tokens JWT
Estructura General
Un token JWT tiene el formato:
header.payload.signatureEstos tres componentes están separados por puntos y codificados en Base64Url.
1. Header (Encabezado)
Contiene metadatos sobre el tipo de token y el algoritmo de firma.
Ejemplo:
{
"alg": "HS256",
"typ": "JWT"
}alg: Algoritmo de firma (HS256, RS256, etc.)typ: Tipo de token (siempre "JWT")
2. Payload (Carga útil)
Contiene los claims (afirmaciones) - la información que se transmite.
Tipos de claims:
Registered claims: Predefinidos (iss, exp, sub, aud, etc.)
Public claims: Definidos por el usuario
Private claims: Claims personalizados
Ejemplo:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022,
"exp": 1516242622
}3. Signature (Firma)
Se crea combinando:
Header codificado
Payload codificado
Secret key
Algoritmo especificado en el header
Fórmula:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)Ejemplo Completo
Header codificado:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9Payload codificado:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMiwiZXhwIjoxNTE2MjQyNjIyfQSignature:
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5cToken JWT completo:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMiwiZXhwIjoxNTE2MjQyNjIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5cCaracterísticas Importantes
✅ Compacto: Fácil de transmitir por URL, POST, o headers HTTP
✅ Auto-contenido: Toda la información necesaria está en el token
✅ Verificable: La firma garantiza la integridad del contenido
⚠️ No encriptado por defecto (a menos que uses JWE)
⚠️ Los datos son visibles (solo Base64, no encriptación)
¿Quién provee la clave secreta?
El servidor que genera el token es quien crea y guarda la clave secreta. Es información privada que nunca se comparte con el cliente.
Ejemplo: Cuando te logueas en una app, el backend usa su clave secreta para firmar tu token
La clave debe ser: Larga, compleja y guardada de forma segura (variables de entorno)
📝 ¿Con qué datos se hace la firma?
La firma se crea usando tres componentes:
Header: Metadatos (tipo de token, algoritmo usado)
Payload: Los datos (user ID, permisos, expiración)
Clave secreta: La llave privada del servidor
Fórmula: Firma = Algoritmo(Header + "." + Payload, ClaveSecreta)
🛡️ ¿Cómo funciona el proceso?
1. Creación del token:
// En el servidor
const header = { alg: 'HS256', typ: 'JWT' };
const payload = { userId: 123, username: 'juan' };
const secretKey = 'mi-clave-super-secreta';
// Se firma concatenando header + payload + secreto
const firma = HMAC_SHA256(base64(header) + '.' + base64(payload), secretKey);
// Token final: header.payload.firma
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEyMywidXNlcm5hbWUiOiJqdWFuIn0.firma-generada'2. Verificación:
// Cuando recibes el token
const [receivedHeader, receivedPayload, receivedSignature] = token.split('.');
// Vuelves a calcular la firma con los mismos datos
const calculatedSignature = HMAC_SHA256(receivedHeader + '.' + receivedPayload, secretKey);
// Comparas con la firma recibida
if (calculatedSignature === receivedSignature) {
// Token válido - no ha sido modificado
} else {
// Token alterado - rechazar
}🔍 Diferencia clave: Firma vs Encriptación
Firma (JWT):
✅ Verifica integridad: Asegura que los datos no fueron alterados
✅ Verifica autenticidad: Confirma que viene de quien dice venir
❌ Los datos son visibles: Cualquiera puede leer el payload (usar Base64)
Encriptación:
✅ Oculta los datos: Solo quien tiene la clave puede leerlos
❌ No verifica autoría: No sabes quién lo creó
🎯 ¿Por qué se usa firma y no encriptación?
Performance: Verificar es más rápido que encriptar/desencriptar
Escalabilidad: El servidor solo necesita verificar, no desencriptar
Transparencia: El cliente puede leer los datos (sin poder modificarlos)
⚠️ Consideraciones de seguridad
Nunca poner información sensible en el payload (passwords, datos bancarios)
Usar HTTPS para transmitir tokens
Claves secretas robustas y rotadas periódicamente
Tiempos de expiración cortos
1. ¿Cómo se firma el token? ¿Con qué datos?
La Analogía del Sobre de Seguridad:
Imagina que quieres enviar un documento importante por correo. Para que el receptor sepa que es auténtico y no ha sido alterado, haces lo siguiente:
Escribes el documento (el Payload del JWT: la información del usuario, permisos, etc.).
Escribes una nota indicando el método que usaste para sellarlo, por ejemplo, "Sellado con cinta adhesiva azul" (el Header del JWT: el algoritmo de firma).
Metes ambas cosas en un sobre transparente. Cualquiera puede ver el contenido (el JWT no está encriptado, es legible).
Para sellarlo, usas tu cinta adhesiva azul única (la clave secreta) y pasas la cinta por toda la solapa del sobre. La cinta deja un patrón de fibras imposible de replicar sin tener el mismo rollo de cinta (la firma).
Proceso Técnico (Simplificado):
El algoritmo toma tres ingredientes:
El Header codificado en Base64.
El Payload codificado en Base64.
La Clave Secreta (el "rollo de cinta azul").
Los combina de esta manera:
firma = algoritmoDeFirma(header + "." + payload, claveSecreta)
// Ejemplo con HS256: firma = HMACSHA256(base64Header + "." + base64Payload, "mi-clave-secreta")La firma resultante es una cadena de caracteres única que depende de absolutamente every bit de la información del header y el payload. Si alguien modifica aunque sea una sola letra dentro del payload, la firma resultante sería completamente diferente.
2. ¿Quién da la Clave Secreta?
La Analogía del Notario:
Imagina que el token es un documento legal, como un poder notarial.
El "Usuario" es la persona que concede el poder.
El "Servidor" que genera el token es el notario. Él es la única entidad de confianza que posee el sello oficial y el libro de firmas (la clave secreta). Su trabajo es verificar la identidad del usuario, redactar el documento (crear el JWT) y sellarlo con su sello oficial (firmarlo).
Cualquier otro "Servidor" que reciba el documento (el token) puede confiar en él porque conoce el sello oficial del notario (conoce la clave secreta o la clave pública para verificar) y puede comprobar que el sello es auténtico y el documento no ha sido alterado.
En la práctica:
La clave secreta nunca abandona el servidor que la creó (el "notario").
Es generada y guardada de forma segura por el backend (la aplicación del servidor) que emite los JWTs. Es uno de los secretos más importantes de la aplicación.
3. Firma vs. Encriptación: La Diferencia Crucial
Esta es la confusión más común. Son conceptos totalmente distintos.
| Característica | Firma (JWT estándar) | Encriptación (JWE) |
|---|---|---|
| Objetivo | Garantizar integridad y autenticidad. ¿El contenido es original y viene de quien dice? | Garantizar confidencialidad. ¿Puede alguien más leer el contenido? |
| Analogía | El sobre transparente sellado con cinta. Todos pueden leer el mensaje, pero si el sello está roto, sabes que fue alterado. | Una caja fuerte cerrada con combinación. No puedes ver lo que hay dentro a menos que tengas la clave para abrirla. |
| ¿Quién puede ver el contenido? | Cualquiera que tenga el token. Puedes decodificar el Base64 del header y payload en cualquier página web. | Solo quien tenga la clave para desencriptar. |
| Protección | Protege contra la modificación. | Protege contra la lectura. |
¿Por qué se usan firmados y no encriptados la mayoría de las veces?
Porque el caso de uso típico de un JWT es para autenticación y autorización.
Un cliente (navegador, app móvil) se logea y el servidor le devuelve un JWT firmado.
El cliente envía ese JWT en cada petición posterior (ej. en el
Header Authorization: Bearer <token>).El servidor (o un grupo de servidores) verifica la firma para asegurarse de que el token es válido y no ha sido manipulado (ej. que un usuario no cambió su ID de
user123aadmin456).No es necesario esconder la información. De hecho, es útil que el payload sea legible para que el cliente o otros servicios puedan extraer información como el nombre de usuario o sus permisos sin necesidad de tener la clave secreta, solo con decodificar el Base64. La seguridad radica en que no pueden modificarla.
Resumen final:
Firmar es como poner un sello de seguridad en un documento público. Verifica que el documento es original.
Encriptar es como meter un documento en una caja fuerte. Esconde el contenido de miradas indiscretas.
Un JWT estándar (JWS) viene con un sello de seguridad (firma), pero en un sobre transparente (sin encriptar). Si necesitas privacidad, existe un estándar llamado JWE (JSON Web Encryption) que sí encripta el contenido.
Excelente pregunta. La clave secreta (o secret key) NO va dentro del payload ni del header del JWT. Es un error común pensar que se incluye ahí, pero hacerlo sería una grave vulnerabilidad de seguridad.
La clave secreta es un dato externo y privado que solo deben conocer:
El servidor que genera (firma) el token (por ejemplo, tu backend/API).
El servidor que verifica (valida) el token (de nuevo, tu backend/API).
¿Dónde y cómo se usa entonces la Clave Secreta?
La clave secreta se utiliza exclusivamente para crear y verificar la Firma (la tercera parte del JWT).
El proceso es el siguiente:
1. Para CREAR (firmar) el JWT:
Se crean el
headery elpayloaden formato JSON.Se codifican ambos en Base64Url para formar
encodedHeaderyencodedPayload.Se concatenan con un punto:
encodedHeader + "." + encodedPayload.Se toma esta cadena concatenada y se aplica el algoritmo de firma (el especificado en
header.alg, como HS256) junto con la clave secreta para generar la firma.
Fórmula de la firma (ejemplo para HS256):
signature = HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
**secretKey** // <- ¡Aquí se usa la clave!
);Finalmente, se codifica esta firma en Base64Url y se junta todo:
encodedHeader + "." + encodedPayload + "." + encodedSignature
2. Para VERIFICAR el JWT:
Cuando el cliente (navegador, app móvil) te envía el JWT en una solicitud, tu servidor debe verificar que es auténtico y no ha sido alterado.
El servidor separa el token en sus tres partes:
header,payloadysignature.Toma las primeras dos partes (
headerypayloadcodificados) y, usando la misma clave secreta que usó para firmar, vuelve a calcular la firma de la misma manera que en el paso 1.Compara la firma que acaba de calcular con la firma que vino en el token.
Si coinciden: Significa que el token es auténtico y su contenido (
headerypayload) no ha sido modificado. El servidor puede confiar en la información del payload (como eliddel usuario).Si NO coinciden: Significa que el token fue alterado o no fue firmado con la clave correcta. El servidor debe rechazar la solicitud inmediatamente.
Analogía: Un documento firmado
Piensa en el JWT como un documento legal:
El Header es la portada que dice "Contrato".
El Payload es el contenido del contrato (cláusulas, nombres, fechas).
La Firma es tu firma manuscrita al final del documento.
La Clave Secreta es el bolígrafo con tinta única e imposible de copiar que usas para firmar.
Tú no escribes el bolígrafo dentro del contrato. El bolígrafo es tu herramienta privada para demostrar que fuiste tú quien autenticó el contenido del documento. Cualquiera puede leer el contrato (header y payload), pero solo quien tenga el bolígrafo original puede verificar si la firma es válida.
Resumen crucial:
✅ CORRECTO: La clave secreta es un dato externo, guardado de forma segura en tu servidor (en variables de entorno o un gestor de secretos). Se usa para generar y verificar la firma.
❌ INCORRECTO (Y MUY PELIGROSO): Incluir la clave secreta dentro del
payloadoheader. Si haces esto, cualquier persona que decodee el JWT (algo muy fácil) podrá robar tu clave y firmar tokens falsos, comprometiendo toda tu aplicación.
En tu ejemplo, los únicos datos que deben ir en el payload son los del usuario (id, nombre) y claims estándar como iat o exp. La clave nunca debe ser uno de ellos.
¡Exactamente! Has entendido perfectamente.
Así es, la clave secreta es una cadena que tú generas o proporcionas. Es un secreto que solo tu servidor (o servidores) debe conocer.
El proceso, tal como lo describes, es:
Tú defines la clave secreta: Es una cadena de texto larga, compleja y aleatoria. Por ejemplo, podría ser algo como:
fGh3$kL9!pQrS@vXyZ*2w5z8C&E)H+McQeThWmZq4t7w9z$C&F)J@NcRfUjXn2r5u(nunca uses ejemplos, genera una propia). Esta clave se guarda de forma segura en el entorno de tu servidor.Construyes el Header y el Payload: Con la información que quieres transmitir (id, nombre, etc.).
Aplicas el algoritmo de firma: Usas una función (generalmente proporcionada por una librería de JWT) que hace exactamente lo que dices:
Toma el
headercodificado en Base64Url.Toma el
payloadcodificado en Base64Url.Los concatena con un punto.
Aplica el algoritmo (ej: HS256) junto con tu clave secreta a esa cadena concatenada para generar la firma.
Ejemplo Práctico con Código (Pseudocódigo)
Imagina que tienes estos datos:
// 1. Tu Clave Secreta (guardada en el servidor)
const secretKey = 'mi-clave-super-secreta-y-compleja-2024';
// 2. Header y Payload
const header = {
"alg": "HS256",
"typ": "JWT"
};
const payload = {
"id": 5421,
"nombre": "Ana García",
"clave_de_usuario": "abc123" // ¡Ojo! Esto es la clave DEL USUARIO, no la secreta para firmar.
};
// 3. Una librería de JWT haría esto por ti:
const encodedHeader = base64UrlEncode(JSON.stringify(header));
const encodedPayload = base64UrlEncode(JSON.stringify(payload));
const dataToSign = encodedHeader + '.' + encodedPayload;
// 4. Aquí es donde usas tu "secretKey" para crear la firma.
// La función HMACSHA256 es el algoritmo HS256.
const signature = HMACSHA256(dataToSign, secretKey);
const encodedSignature = base64UrlEncode(signature);
// 5. Juntas todo para formar el JWT
const jwtToken = encodedHeader + '.' + encodedPayload + '.' + encodedSignature;
// Resultado: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NTQyMSwibm9tYnJlIjoiQW5hIEdhcmPDrWEiLCJjbGF2ZV9kZV91c3VhcmlvIjoiYWJjMTIzIn0.XL4eSmY4UZQQP2fCq7g2a7xRkXeoRkZz7J2lT6p1qQ0Punto importante: En el ejemplo del payload puse "clave_de_usuario": "abc123". Esto es la contraseña o PIN del usuario en tu sistema, no es la clave secreta para firmar el JWT. Es mala práctica incluir información sensible como contraseñas en el payload, ya que aunque el JWT está firmado, su contenido (payload) es fácilmente decodificable y visible por cualquiera (no está encriptado, solo codificado en Base64).
En resumen: Sí, tu entendimiento es correcto. La clave secreta es externa y se usa junto con el algoritmo para crear la firma, que es la garantía de integridad y autenticidad del token.
Comentarios
Publicar un comentario