Cómo verificar la firma de un JWT (HS256) online: Guía práctica para la solución de problemas en producción
¿Recibes errores "invalid signature", "jwt malformed" o "token expired" en tus aplicaciones, especialmente en entornos con microservicios, múltiples entornos (dev/staging/prod) y rotación de secretos? La verificación de JWT (JSON Web Tokens) puede ser un dolor de cabeza sin un método claro. Esta guía te proporciona un proceso paso a paso para verificar la firma HS256 de tus JWT, identificar las causas de fallo y resolver los problemas rápidamente.
Aprenderás a aislar problemas relacionados con el secret, el algoritmo, el payload y la codificación Base64URL. Al final, tendrás una guía práctica para solucionar incidentes en producción de forma eficiente.
¿Qué significa "verificar la firma" de un JWT?
Un JWT se compone de tres partes: header.payload.signature. En el algoritmo HS256, la firma se genera
usando HMAC SHA-256 con un secret compartido. Verificar la firma implica volver a calcularla con el mismo secret y comparar
el resultado con la firma original del token. Si no coinciden, el token se considera inválido, lo que puede indicar
que el token fue alterado o firmado con un secret incorrecto.
Paso 1: Comprueba la estructura del token
El primer paso, aunque parezca obvio, es crucial. Asegúrate de que el token JWT tenga la estructura correcta. Un token válido debe constar de exactamente tres segmentos separados por puntos.
- Si hay 1 segmento: Podrías haber copiado solo el payload o una cadena Base64.
- Si hay 2 segmentos: Falta la firma. Puede ser un token "unsigned" o incompleto.
- Si hay más de 3 segmentos: Podría haber concatenaciones, espacios inesperados o el prefijo "Bearer " mal recortado.
Para una inspección rápida, utiliza un validador de JWT online, como el que se incluye en nuestra herramienta de análisis de JWT. Verifica que el validador indique "Estructura OK". Si el token está dentro de otro envoltorio (por ejemplo, dentro de una respuesta HTTP), nuestra herramienta de conversión Base64 te ayudará a extraer el contenido.
Paso 2: Decodifica el header y confirma el algoritmo
El header del JWT contiene información crucial, incluido el claim alg, que especifica el algoritmo
utilizado para firmar el token. Un error común es que un servicio firme con HS256 mientras otro intenta
validarlo con un algoritmo diferente, o que un gateway re-firme los tokens con otro algoritmo.
Siempre verifica el algoritmo: Nunca confíes ciegamente en el valor de alg. Tu validador
debe usar una lista blanca de algoritmos permitidos. Por ejemplo, solo permitir "HS256".
Si el header dice alg=HS256, tu verificación debe usar HS256. Si aparece none, desconfía. La
mayoría de las librerías modernas bloquean este algoritmo por razones de seguridad, pero aún existen sistemas legacy
que lo utilizan y pueden ser vulnerables.
Paso 3: Verifica la expiración (exp) y considera el "clock skew"
Muchos errores de "firma inválida" son, en realidad, errores relacionados con la expiración del token (claim
exp) mal interpretados o problemas de sincronización horaria.
La claim exp representa la fecha y hora de expiración en segundos desde la época Unix (UTC). Dos puntos
clave a considerar:
- Clock Skew (Desfase de reloj): Si los servidores que generan y validan los tokens no están sincronizados, un token puede "parecer" expirado en un servidor y ser válido en otro.
- Timezones (Zonas horarias):
expno incluye información de zona horaria; siempre es UTC. Un error común es convertir incorrectamente este valor en los logs.
Utiliza el analizador de JWT para ajustar la tolerancia de tiempo (por ejemplo, 60 segundos) y verificar si el problema es el desfase horario. Si se confirma, revisa la configuración NTP (Network Time Protocol) o el reloj del contenedor/VM.
Paso 4: Recalcula la firma HS256 (conceptualmente)
Para comprender mejor cómo funciona la verificación, es útil entender cómo se calcula la firma HS256.
La firma se calcula de la siguiente manera:
signature = HMAC_SHA256(secret, base64url(header) + "." + base64url(payload))
jwt = base64url(header) + "." + base64url(payload) + "." + base64url(signature)
Si la verificación falla, responde estas preguntas para diagnosticar el problema:
- ¿El secret es exactamente el mismo en ambos servicios (el que firma y el que valida)?
- ¿Hay un secret diferente por entorno (dev, staging, prod) o por "tenant"?
- ¿El secret incluye espacios, saltos de línea o caracteres no visibles?
- ¿El token fue generado con HS256, pero el validador solo permite HS512 (o viceversa)?
Paso 5: Errores comunes en producción y cómo solucionarlos
Identificar los errores más frecuentes te permitirá solucionar los problemas más rápido.
1. Secret incorrecto o rotado
Este es el error más común. Si rotas los secrets, implementa una estrategia:
- Acepta el secret anterior durante un período de "gracia" para dar tiempo a la propagación de la nueva clave.
- Firma los tokens con la claim
kid(Key ID) y mantén un almacén de claves para identificar qué secret se utilizó.
Si tu token no incluye kid, la depuración será más manual, pero los pasos anteriores te ayudarán a
identificar la causa.
2. Doble codificación Base64 o Base64URL incorrecto
Base64URL reemplaza + por -, / por _ y elimina el padding
=. Un error común es usar codificación Base64 estándar en un segmento.
Si sospechas de este error, decodifica el segmento sospechoso manualmente usando nuestra herramienta de conversión Base64. Revisa si el JSON decodificado "se ve raro" o contiene bytes extraños.
3. Payload modificado por un proxy o gateway
Cualquier cambio en el header o payload invalida la firma. A veces, un middleware reescribe claims, normaliza campos o "inyecta" información en el payload.
En este caso, el token se decodificará correctamente, pero la firma nunca coincidirá.
Solución: Asegúrate de que solo un componente firme el token, y el resto solo lo verifique. Revisa la configuración de tus proxies y gateways.
4. Validación incompleta (no se valida aud/iss)
Aunque la firma sea correcta, un token podría ser inadecuado para tu servicio. En producción, siempre debes validar:
iss(emisor)aud(audiencia)expnbf(si aplica)
Sin estas validaciones, un token emitido para otro servicio podría ser aceptado y causar problemas de seguridad.
Paso 6: Ejemplos de verificación HS256 en código
A continuación, te mostramos ejemplos prácticos de cómo verificar tokens HS256 en Node.js y PHP.
Node.js (jsonwebtoken) con lista blanca
import jwt from "jsonwebtoken";
export function verifyToken(token, secret) {
try {
const decoded = jwt.verify(token, secret, {
algorithms: ["HS256"],
clockTolerance: 60, // segundos
issuer: "auth.miempresa",
audience: "api.servicio-x",
});
return decoded;
} catch (error) {
// Maneja los errores de verificación (e.g., TokenExpiredError, JsonWebTokenError)
console.error("Error al verificar el token:", error.message);
return null; // O lanza una excepción, según tu lógica
}
}
PHP con firebase/php-jwt
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
function verifyToken(string $token, string $secret) {
try {
$decoded = JWT::decode($token, new Key($secret, "HS256"));
// Valida issuer/audience en tu lógica si tu versión no lo hace automáticamente
return $decoded;
} catch (\Exception $e) {
// Maneja las excepciones (e.g., ExpiredException, SignatureInvalidException)
error_log("Error al verificar el token: " . $e->getMessage());
return null; // O lanza una excepción, según tu lógica
}
}
Importante: Estos ejemplos muestran la verificación básica. Recuerda manejar las excepciones adecuadamente en tu código para una gestión robusta de errores.
Si necesitas comparar resultados entre diferentes entornos (por ejemplo, firmas distintas), puedes generar un hash del "signing input" (header + payload) con nuestra herramienta de generación de Hash. Esto te ayudará a detectar si estás verificando el mismo contenido en cada entorno.
Paso 7: Checklist rápido para la resolución de problemas
Usa esta lista para agilizar el proceso de diagnóstico durante incidentes.
- Token tiene 3 partes y no está truncado.
- Header decodifica y
algcoincide con lo esperado (lista blanca). - Payload decodifica y
expno está en el pasado (considera clock skew). - Secret correcto: sin espacios, misma codificación, mismo entorno/tenant.
- No hay middlewares reescribiendo header/payload.
- Se valida
iss,audynbf(si aplica).
Preguntas frecuentes sobre la verificación de JWT HS256
Respondemos a las preguntas más comunes para que tengas una comprensión completa.
¿Por qué recibo "invalid signature" aunque el secret es correcto?
Las causas más comunes son:
- El secret tiene un formato incorrecto (espacios, saltos de línea).
- El algoritmo especificado en el header (
alg) no coincide con el utilizado para firmar. - El payload ha sido modificado.
- Problemas de clock skew.
¿Cómo puedo rotar los secrets sin afectar a los usuarios?
Implementa una estrategia de rotación gradual. Las opciones incluyen:
- Permitir el uso del secret anterior durante un período de gracia.
- Usar la claim
kid(Key ID) y mantener un almacén de claves para facilitar el cambio de secrets.
¿Es seguro guardar datos sensibles en el payload de un JWT?
No, un JWT no está encriptado, solo firmado (a menos que uses JWE). La información en el payload es fácilmente decodificable. Nunca guardes datos sensibles (contraseñas, información personal identificable) en el payload.
¿Qué herramientas puedo usar para depurar JWT?
Te recomendamos la combinación de:
- Nuestro Validador / Analizador de JWT para ver claims y expiración.
- Nuestro Conversor Base64 para detectar problemas de codificación.
- Nuestro Generador de Hash para comparar entradas.
Recomendación final según el caso de uso
Este artículo proporciona una guía general para la verificación de JWT HS256. Sin embargo, si buscas una solución
más robusta para entornos empresariales y aplicaciones con alta seguridad, te sugerimos considerar RS256, que utiliza
claves asimétricas. Implementa la rotación de claves y usa la claim kid.
Para la generación de secrets, utiliza nuestro Generador de Password Segura y guárdalos de forma segura, como en un vault.