In this article
CVE-2026-29000: cómo una clave pública rompe la autenticación en pac4j-jwt
Una vulnerabilidad crítica de omisión de autenticación en la biblioteca de seguridad para Java pac4j-jwt permite a los atacantes suplantar a cualquier usuario, incluso a administradores, usando únicamente la clave pública RSA de un servidor. La vulnerabilidad, registrada como CVE-2026-29000, tiene una puntuación CVSS de 10.0 y afecta a todas las versiones de org.pac4j:pac4j-jwt anteriores a 4.5.9, 5.7.9 y 6.3.3.
La falla es fácil de explotar. Hay una prueba de concepto pública y ya están disponibles las versiones corregidas. Si tu aplicación Java usa pac4j para la autenticación basada en JWT, sigue leyendo.
Resumen
Detalle | Valor |
|---|---|
CVE | |
CVSS 3.1 | 10.0 Crítica ( |
CVSS 4.0 | 10.0 Crítica ( |
CWE | CWE-347: verificación incorrecta de la firma criptográfica |
Artefacto de Maven |
|
Afectado | 4.x \< 4.5.9, 5.0.0-RC1 \< 5.7.9, 6.0.4.1 \< 6.3.3 |
Corregido | 4.5.9, 5.7.9, 6.3.3 |
Estado de la explotación | Prueba de concepto pública disponible |
Descubierto por | CodeAnt AI Security Research |
¿Qué es pac4j?
pac4j es un framework de seguridad para Java que ofrece autenticación y autorización mediante varios protocolos: OAuth, SAML, CAS, OpenID Connect y JWT. El módulo pac4j-jwt se encarga específicamente de crear y validar JSON Web Tokens mediante su clase JwtAuthenticator.
pac4j puede usarse como motor de autenticación subyacente en frameworks como Spring Security, JEE, Play, Vert.x y otros. También se implementa ampliamente en entornos CAS (Central Authentication Service), comunes en universidades e instituciones gubernamentales. Si tu aplicación usa autenticación basada en pac4j con JWT, podría estar afectada.
Cómo funciona la vulnerabilidad
Para entender esta vulnerabilidad, debes conocer la diferencia entre dos tipos de JWT:
JWS (JSON Web Signature): un token firmado. La firma demuestra que el token no se modificó y que lo creó alguien con la clave de firma.
JWE (JSON Web Encryption): un token cifrado. El cifrado protege el contenido del token para que no se pueda leer durante la transmisión. JWE puede contener un JWS para ofrecer confidencialidad e integridad.
Cuando JwtAuthenticator de pac4j recibe un JWT cifrado (JWE), sigue este proceso:
Descifrar el envoltorio JWE externo
Analizar la carga útil interna
Verificar la firma del token interno
La vulnerabilidad está en el paso 3. Después de descifrar el JWE, la biblioteca llama a la función toSignedJWT() de com.nimbusds:nimbus-jose-jwt sobre la carga útil interna para extraer el token firmado y verificarlo. Si el token interno es un PlainJWT (un JWT con alg: none, es decir, sin firma), este método devuelve null.
En el código vulnerable, este valor null hacía que se omitiera todo el bloque de verificación de firma. En la práctica, la biblioteca decía: «No encuentro ninguna firma para verificar, así que continuaré».
Esta es una representación simplificada de la lógica vulnerable:
// (after JWE decryption succeeds above)
SignedJWT signedJWT = encryptedJWT.getPayload().toSignedJWT();
// If inner token is PlainJWT, signedJWT is null
if (signedJWT != null) {
jwt = signedJWT;
}
// Signature verification only runs if signedJWT is not null
if (signedJWT != null && !signatureConfigurations.isEmpty()) {
// Verify signature... but this block is NEVER entered
// for a PlainJWT wrapped in JWE
}
// Claims are extracted regardless — attacker is now "authenticated"
createJwtProfile(ctx, credentials, jwt);La corrección agrega un rechazo explícito cuando se encuentra un token sin firmar dentro de un envoltorio cifrado:
if (!signatureConfigurations.isEmpty()) {
if (signedJWT == null) {
throw new CredentialsException(
"A non-signed JWT cannot be accepted...");
}
// Proceed with normal signature verification
}El ataque en la práctica
Un atacante solo necesita una cosa: la clave pública RSA del servidor. Por definición, las claves públicas son… públicas. A menudo se incluyen en endpoints JWKS, certificados o archivos de configuración.
Pasos del ataque:
Obtener la clave pública RSA del servidor (desde un endpoint JWKS, un certificado TLS o cualquier fuente pública)
Crear un PlainJWT con claims arbitrarios:
{
"sub": "admin",
"role": "ADMIN",
"exp": 1772611200
}Incluirlo en un JWE cifrado con la clave pública RSA del servidor
Enviar el token falsificado a la aplicación
La aplicación descifra el JWE (el cifrado con la clave pública es válido), no encuentra ninguna firma que verificar, omite la verificación y acepta los claims falsificados como auténticos
El atacante no necesita la clave privada, un secreto compartido ni credenciales. El único elemento necesario es la clave pública que debía proteger el sistema.
A quiénes afecta
Tu aplicación podría estar afectada si:
Usa cifrado RSA. Esto se puede detectar mediante RSAEncryptionConfiguration en el proyecto.
Tu aplicación usa
org.pac4j:pac4j-jwtpara la autenticación con JWTUsa tanto la configuración de cifrado (JWE) como la de verificación de firmas (JWS) en
JwtAuthenticatorEjecutas una versión anterior a 4.5.9, 5.7.9 o 6.3.3
Las aplicaciones que solo usan JWT firmados (JWS), sin cifrado, no son vulnerables a este ataque específico, ya que el truco de incluir un PlainJWT dentro de un JWE requiere que el cifrado esté configurado.
Verifica con Snyk
Puedes verificar si tu proyecto está afectado con la Snyk CLI:
# For Maven projects
snyk test --maven
# For Gradle projects
snyk test --gradleSnyk registra esta vulnerabilidad como SNYK-JAVA-ORGPAC4J-15428218. Si el paquete vulnerable está en tu árbol de dependencias, Snyk lo señalará y recomendará cómo actualizarlo.
Si usas Maven, el plugin de Snyk para Maven también puede detectarla como parte del ciclo de compilación, sin necesidad de ejecutar la CLI por separado. Para proyectos de Gradle, el plugin de Snyk para Gradle ofrece la misma capacidad de análisis integrado.
También puedes buscar directamente en tus dependencias:
# Check if pac4j-jwt is in your dependency tree
mvn dependency:tree | grep pac4j-jwt
# Or with Gradle
gradle dependencies | grep pac4j-jwtCorrección
Actualiza org.pac4j:pac4j-jwt a la versión corregida correspondiente a tu versión principal:
Versión actual | Actualizar a |
|---|---|
4.x | 4.5.9 o posterior |
5.x | 5.7.9 o posterior |
6.x | 6.3.3 o posterior |
En Maven, actualiza tu archivo pom.xml:
<!-- For pac4j 6.x -->
<dependency>
<groupId>org.pac4j</groupId>
<artifactId>pac4j-jwt</artifactId>
<version>6.3.3</version>
</dependency>En Gradle:
// For pac4j 6.x
implementation 'org.pac4j:pac4j-jwt:6.3.3'Si no puedes actualizar de inmediato
Aunque se recomienda actualizar, puedes reducir la exposición de tu sistema si:
Revisas la configuración de JWT: si tu aplicación solo usa JWT firmados (JWS), sin cifrado, no es vulnerable a esta vía de ataque específica. Sin embargo, actualizar sigue siendo la opción más segura.
Auditas el código que maneja JWT: asegúrate de que tu aplicación exija explícitamente tokens firmados y rechace los que no tengan firma en la capa de la aplicación.
No hay ninguna solución alternativa a nivel de configuración dentro de pac4j para las versiones vulnerables.
Por qué esto importa más allá de pac4j
Esta vulnerabilidad es un ejemplo clásico de una categoría más amplia de fallas de implementación de JWT. El problema central —tratar la ausencia de una propiedad de seguridad (una firma) como algo que se puede omitir en lugar de rechazar— lleva años apareciendo en bibliotecas de JWT de distintos lenguajes.
La lección es clara: la verificación criptográfica debe ser obligatoria, no opcional. Si una biblioteca encuentra un token sin firma cuando espera uno firmado, la única respuesta segura es rechazarlo. Continuar silenciosamente es una vulnerabilidad en potencia. Para conocer más sobre cómo se manifiestan fallas de autenticación como esta, consulta la lección sobre autenticación comprometida en Snyk Learn.
Para los equipos de Java, esto es un recordatorio de que deben:
Configurar explícitamente los algoritmos aceptados en la validación de JWT. Nunca permitas
none.Tratar el cifrado y la firma como aspectos independientes. Descifrar demuestra confidencialidad, no autenticidad. Un token envuelto en un JWE todavía debe tener la firma verificada.
Auditar con regularidad las dependencias transitivas. Un framework de integración que no administras directamente podría incluir pac4j-jwt como dependencia. Consulta Buenas prácticas para administrar dependencias de Java para obtener recomendaciones sobre cómo auditar tu árbol de dependencias.
Para conocer más sobre prácticas seguras de manejo de JWT, consulta Las 3 principales prácticas de seguridad para manejar JWT y ¿Snyk puede detectar problemas de seguridad en JWT? en el blog de Snyk.
Cronología
Fecha | Evento |
|---|---|
2 de marzo de 2026 | el responsable del proyecto pac4j-jwt publicó las correcciones |
3 de marzo de 2026 | el responsable del proyecto pac4j-jwt publicó un aviso y una entrada de blog |
4 de marzo de 2026 | Se publicaron los detalles de la CVE en MITRE |
DOCUMENTO TÉCNICO
La crisis de seguridad de IA en tu entorno de Python
Con el ritmo de desarrollo acelerándose a un ritmo vertiginoso, ¿sabes realmente a qué puede acceder tu entorno de IA?