Tema representativo de entrevista

Entrevista de Backend: ¿Cómo rotar las claves de firma de JWT sin causar interrupciones en la autenticación?

BackendDifícil
Equipo editorial de Offer.ccPublicado Actualizado

Pregunta

Un servicio de autenticación firma tokens de acceso con RS256. Los tokens se pueden aceptar durante 15 minutos, mientras que 200 servicios backend almacenan en caché el JWKS durante 10 minutos y realizan la verificación de forma local. Diseña una rotación de claves planificada sin tiempo de inactividad en la autenticación y, a continuación, gestiona kids desconocidos, fallas en JWKS y el compromiso de claves.

Consigna y contexto aplicable

Un servicio de autenticación firma tokens de acceso con RS256. Un token puede aceptarse como máximo durante 15 minutos, con 1 minuto de desviación de reloj (clock skew). Doscientos servicios de backend leen un jwks_uri fijo del documento OpenID Discovery del emisor, almacenan en caché el JWKS durante 10 minutos y verifican los tokens localmente.

Diseña una rotación de claves de firma que no genere una oleada de respuestas 401 ante solicitudes válidas. Cubre estos casos:

  • llegan tokens antiguos y nuevos durante una rotación normal;
  • el encabezado del token contiene un kid ausente de la caché local;
  • muchos valores aleatorios de kid intentan provocar una tormenta de refrescos (refresh storm) de JWKS;
  • el endpoint de JWKS agota el tiempo de espera o falla durante la rotación;
  • la clave privada actual puede estar comprometida y requiere una acción de emergencia;
  • el operador debe demostrar que cada verificador acepta la nueva clave antes de retirar la antigua.

La vigencia de 15 minutos, la caché de 10 minutos, la desviación de 1 minuto y los 200 servicios son restricciones del escenario, no recomendaciones universales. La habilidad principal radica en el diseño de protocolos de autenticación backend, consistencia de caché, semántica ante fallas y operaciones de seguridad, por lo que la categoría es backend.

Qué evalúa el entrevistador

Primero, ¿puede el candidato indicar el orden seguro?: ¿publicar la nueva clave pública, permitir que los verificadores la observen, comenzar a firmar con la nueva clave privada, esperar a que expiren los tokens antiguos y solo entonces eliminar la clave pública antigua? Cambiar primero el firmante garantiza que los servicios con una caché de JWKS antigua rechacen los tokens nuevos.

Segundo, ¿comprenden que un JWKS es un conjunto de claves públicas? La RFC 7517 define un arreglo de keys, y el kid solo selecciona una clave de un conjunto confiable. Una clave privada nunca debe publicarse en un JWKS. Un kid tampoco es prueba de confianza; debe estar vinculado al emisor de confianza, a un algoritmo fijado (pinned) y a una verificación de firma exitosa.

Tercero, ¿pueden equilibrar el almacenamiento en caché y la seguridad? Obtener el JWKS en cada solicitud sobrecarga al emisor, mientras que almacenarlo en caché para siempre retrasa la aceptación de una clave nueva y el retiro de una antigua. Un kid desconocido puede desencadenar una actualización controlada, pero las actualizaciones concurrentes deben agruparse (coalesce), limitarse en tasa (rate-limited) y seguirse de una caché negativa breve cuando se confirme la ausencia del identificador.

Cuarto, ¿separan la rotación planificada del compromiso de la clave privada? La rotación planificada superpone las claves públicas antiguas y nuevas para mantener la disponibilidad. Durante un compromiso, mantener la confianza en la clave antigua permite a un atacante generar tokens. La ruta de emergencia requiere invalidación explícita de caché, controles de revocación y una prioridad de seguridad más estricta.

Quinto, ¿extienden la comprobación de firmas a una validación completa? Un verificador fija los algoritmos permitidos y luego valida la firma, iss, aud, exp y nbf. No sigue un jku no confiable ni una URL de clave arbitraria proveniente del token, lo que podría permitir confusión de algoritmos o SSRF.

Preguntas para aclarar antes de responder

  • ¿Cuál es el tiempo máximo de aceptación del token? Utiliza la vida útil de cada token antiguo que aún podría ser aceptado, no solo un TTL nominal configurado, e incluye la desviación de reloj.
  • ¿Cuál es la obsolescencia máxima del JWKS? Los navegadores, CDN, proxies y cachés dentro del proceso pueden agregar cada uno una capa; la rotación necesita el límite superior real.
  • ¿Se pueden actualizar todos los verificadores de forma proactiva? El envío de configuraciones (push), las confirmaciones de versión o las sondas canary pueden sustituir el hecho de esperar únicamente la expiración natural.
  • ¿Qué sucede cuando el JWKS no está disponible? Define si una clave conocida puede usar una caché obsoleta acotada y si una clave desconocida falla en modo cerrado (fail-closed).
  • ¿Deben revocarse de inmediato los tokens ya emitidos? Un JWT autocontenido no puede admitir una revocación precisa por token solo mediante rotación; es posible que se requiera una lista de denegación, versiones de token o introspección.
  • ¿Quién controla el emisor y los verificadores? Una sola organización puede recopilar confirmaciones de actualización; los verificadores de terceros suelen depender de una ventana de compatibilidad documentada.
  • ¿Dónde se guardan las claves privadas? Genéralas y úsalas en un KMS, HSM o servicio de firma restringido; en el plano de publicación solo debe haber material público.
  • ¿Qué significa el éxito? La rotación planificada mantiene en funcionamiento los tokens válidos antiguos y nuevos. La rotación de emergencia puede aceptar una reautenticación controlada para dejar de confiar rápidamente en una clave comprometida.

Estructura de respuesta en 30 segundos

"Divido la rotación en publicar, precalentar, alternar, superponer y retirar. Genero una clave con un kid nuevo y luego publico tanto la clave pública nueva como la antigua en el JWKS. Espero la ventana máxima de caché de 10 minutos o actualizo proactivamente los 200 verificadores y recopilo su nueva versión de JWKS. Solo después de que la nueva clave pública sea verificable, el emisor comienza a firmar con la nueva clave privada.

Mantengo la clave pública antigua hasta 15 minutos después de que se haya emitido el último token antiguo, más 1 minuto de desviación de reloj. Los verificadores seleccionan kid únicamente dentro del conjunto de un emisor de confianza, fijan RS256 y validan la firma, iss, aud, exp y nbf. Un kid desconocido desencadena una actualización agrupada y con límite de tasa; si aún sigue ausente, se rechaza. Durante una interrupción de JWKS, una clave conocida puede usar una caché obsoleta explícitamente acotada, mientras que una clave desconocida falla en modo cerrado.

Si la clave privada se ve comprometida, detengo la emisión con la clave antigua, publico y cambio a una clave nueva, transmito la invalidación de caché y revoco los tokens firmados por la clave antigua. No utilizo la ventana de superposición normal. Demuestro la finalización a través de resultados por kid, antigüedad de la caché, volumen de peticiones de JWKS y la última observación del kid antiguo".

Análisis detallado paso a paso

Paso 1: Fijar la raíz de confianza y las invariantes de verificación

Un verificador parte de un emisor de confianza configurado, lee su documento OpenID Discovery y utiliza el jwks_uri HTTPS de ese documento. El kid del encabezado del token solo selecciona una clave pública candidata de este JWKS confiable. El token no puede redirigir al verificador a través de jku, y la aplicación no debe concatenar el kid directamente en búsquedas de archivos, bases de datos o URL.

Cada verificación mantiene estas invariantes:

  1. aceptar únicamente el RS256 configurado; el token no puede negociar su algoritmo;
  2. exigir que el kid coincida de forma única con una clave de firma en el JWKS del emisor de confianza;
  3. tras la verificación de la firma, exigir el iss esperado, el aud de esta API, exp y nbf;
  4. rechazar todo el token si falla alguna comprobación;
  5. publicar únicamente material de clave pública mientras las claves privadas permanecen dentro del límite de firma controlado.

Un conjunto mínimo de claves durante la rotación puede verse así:

json
{
  "keys": [
    { "kty": "RSA", "use": "sig", "alg": "RS256", "kid": "2026-07-a", "n": "...", "e": "AQAB" },
    { "kty": "RSA", "use": "sig", "alg": "RS256", "kid": "2026-07-b", "n": "...", "e": "AQAB" }
  ]
}

El orden del arreglo no indica preferencia; el verificador selecciona un kid exacto. Una nueva generación obtiene un kid nuevo y nunca reutilizado, porque una caché no puede distinguir material de clave modificado si está oculto tras el mismo identificador.

Paso 2: Ejecutar la rotación planificada publicando antes de firmar

Modela la rotación planificada como estados explícitos:

text
GENERATED
  -> PUBLISHED(old + new)
  -> VERIFIER_READY
  -> SIGNING_WITH_NEW
  -> OLD_TOKEN_DRAINED
  -> OLD_KEY_RETIRED

El protocolo es:

  1. generar 2026-07-b en el sistema de claves controlado, pero no firmar con él todavía;
  2. publicar un JWKS que contenga 2026-07-a y 2026-07-b, con un nuevo ETag o versión del conjunto;
  3. esperar la ventana de obsolescencia máxima de caché de 10 minutos, o actualizar los 200 verificadores y recopilar confirmaciones de que reconocen el nuevo kid;
  4. verificar la firma, el emisor, la audiencia y los claims de tiempo con tokens canary en cada entorno;
  5. solo después de que los verificadores acepten la nueva clave pública, comenzar a firmar con 2026-07-b;
  6. registrar T_last_old, el momento de emisión del último token con la clave antigua;
  7. no antes de T_last_old + 15 minutes + 1 minute, y después de que el tráfico con el kid antiguo coincida con las expectativas, eliminar la clave pública antigua del JWKS;
  8. continuar monitoreando si hay tráfico anómalo con el kid antiguo y luego deshabilitar y destruir el material privado antiguo.

"Publicar antes de firmar" protege los tokens nuevos. "Detener la firma antigua, drenar los tokens antiguos y luego eliminar" protege los tokens antiguos. La propagación de la caché controla el cambio de firma más temprano posible; la vida útil de aceptación de los tokens antiguos y la desviación de reloj controlan la eliminación más temprana posible de la clave pública.

Paso 3: Gestionar un kid desconocido con una actualización controlada

Un kid desconocido puede ser una nueva generación legítima o ruido generado por un atacante. El verificador no puede rechazar de inmediato cada primera aparición, ni puede convertir cada aparición en tráfico ilimitado hacia el emisor. Un flujo adecuado es:

text
verify(token):
  header = parse_bounded_header(token)
  require header.alg == "RS256"
  key = trusted_cache.find(header.kid)
  if key is missing:
    refresh trusted_issuer_jwks once through single-flight
    key = trusted_cache.find(header.kid)
  if key is missing:
    short_negative_cache.add(header.kid)
    reject "unknown kid"
  verify signature and require iss, aud, exp, nbf

Las ausencias concurrentes (cache misses) para un emisor comparten una actualización de vuelo único (single-flight). Las actualizaciones tienen un tiempo de enfriamiento (cooldown) y un tiempo de espera globales. Una vez que un conjunto actualizado confirma que el kid está ausente, una breve caché negativa evita que identificadores aleatorios repetidos lleguen al emisor. El TTL negativo no puede ser tan largo como para bloquear una rotación real, y el verificador debe limitar la longitud y el formato del kid para evitar ataques a la memoria por alta cardinalidad.

La actualización accede únicamente al jwks_uri configurado, sobre TLS, con límites de tamaño de respuesta, de conexiones y de lectura. Puede utilizar solicitudes condicionales con ETag. Las ubicaciones como jku, x5u o similares provistas por un atacante nunca reemplazan la raíz de confianza.

Paso 4: Definir el límite de disponibilidad durante una falla de JWKS

Las solicitudes normales utilizan la caché local; el endpoint de JWKS no se encuentra en la ruta sincrónica de cada solicitud de autenticación. Si una actualización falla:

  • un kid conocido que coincida puede seguir utilizándose dentro de una ventana de obsolescencia acotada y predefinida;
  • un kid desconocido debe ser rechazado, nunca verificado sin firma ni contra claves no relacionadas;
  • una vez expirada la ventana de obsolescencia acotada, la imposibilidad de actualizar genera una alerta y sigue la política de seguridad, que normalmente es el rechazo;
  • el emisor no debe cambiar a una nueva clave de firma cuando no pueda probar que los verificadores tienen la nueva clave pública.

Un stale-if-error breve absorbe una interrupción temporal del plano de control, pero también extiende la confianza local en una clave eliminada del conjunto actual. Esa duración pertenece al modelo de seguridad y debe anularse mediante invalidación explícita durante un compromiso. La disponibilidad no puede depender de claves obsoletas indefinidamente.

Paso 5: Usar una ruta de emergencia separada para el compromiso de la clave privada

El compromiso de la clave cambia el objetivo: detener la aceptación de tokens generados por atacantes lo más rápido posible. Detén la emisión con la clave antigua, genera y publica una nueva clave pública, fuerza la actualización en los verificadores, cambia el firmante y marca la clave antigua como revocada. No preserves la superposición normal de 16 minutos solo por mantener una experiencia sin interrupciones.

Eliminar la clave pública antigua del JWKS central es insuficiente porque un verificador puede conservar su caché de 10 minutos. Utiliza una transmisión (broadcast) del plano de control debidamente probada, envío forzado de versiones de caché, reinicio de servicios u otro canal de invalidación. Si no se puede contactar a verificadores de terceros, el emisor está limitado por el tope máximo de sus cachés y debe declarar esa exposición de forma explícita.

La rotación tampoco revoca de forma precisa los tokens autocontenidos ya emitidos. Para una revocación inmediata, aplica una regla temporal basada en el kid antiguo o en un límite de iat, acorta la vida útil del token de acceso o utiliza introspección/estado de sesión para las API de alto riesgo. La respuesta de emergencia puede obligar a los usuarios a autenticarse nuevamente; este es un impacto comercial aceptable cuando la seguridad tiene prioridad.

Paso 6: Demostrar la finalización mediante versiones, métricas y auditoría

La respuesta del JWKS debe exponer una versión del conjunto o un ETag observables. Los verificadores informan la versión actual, la antigüedad de la caché, el resultado de la actualización y los valores de kid reconocidos. El emisor registra su kid de firma activo, sin registrar cuerpos de tokens ni material privado.

Las métricas clave incluyen los resultados de verificación por emisor, kid y error; la cardinalidad de kid desconocidos; el recuento de peticiones, latencia y tasa de fallas del JWKS; la coalescencia de consultas concurrentes (single-flight); la antigüedad de la caché; el éxito de los canaries con la nueva clave; y el último uso legítimo del kid antiguo. Un aumento en los errores de clave desconocida para el nuevo kid tras el cambio debería pausar o revertir la modificación de la firma antes de que los usuarios lo reporten.

Los registros de auditoría responden quién generó, publicó, activó y retiró cada clave; qué versión de JWKS estaba activa; qué verificadores confirmaron su preparación; y qué marca de tiempo de la última emisión antigua justificó el retiro. La aprobación dual y el principio de mínimo privilegio hacen que las operaciones del ciclo de vida de las claves sean más seguras.

Paso 7: Probar transiciones y entradas adversarias

No pruebes únicamente un solo token válido estático. Cubre al menos lo siguiente:

  1. con solo la clave pública antigua publicada, el token antiguo pasa y un token con la clave nueva falla;
  2. durante la superposición, ambas generaciones de tokens pasan y las comprobaciones repetidas no vuelven a consultar el JWKS;
  3. con solo la clave antigua en caché, un nuevo kid pasa tras exactamente una actualización controlada;
  4. muchos valores de kid desconocidos, idénticos o aleatorios, provocan actualizaciones acotadas y son rechazados;
  5. tiempos de espera del JWKS, errores 500, respuestas sobredimensionadas y JSON inválido preservan la política de claves conocidas mientras que las claves desconocidas fallan;
  6. algoritmo incorrecto, iss, aud, token expirado y token aún no válido deben fallar;
  7. un jku provisto por un atacante no hace que el verificador contacte una ubicación diferente;
  8. tras eliminar la clave antigua, un proceso nuevo rechaza el token antiguo, mientras que una caché antigua lo acepta solo dentro de su ventana declarada;
  9. un simulacro de compromiso invalida el kid antiguo en cada verificador controlado;
  10. la compuerta de firma impide la activación de la nueva clave mientras los verificadores no estén listos.

La aceptación comprueba tanto los resultados de autorización como los recuentos de solicitudes de JWKS. Un token que pasa mientras cada solicitud impacta en el emisor no constituye una implementación correcta. Tampoco lo es un volumen normal de peticiones con tokens nuevos válidos siendo rechazados.

Ejemplo de respuesta de alta calidad

"Defino el kid como un selector dentro del JWKS de un emisor confiable, no como una fuente de confianza. Cada verificador acepta únicamente el RS256 configurado, obtiene las claves públicas a partir del jwks_uri fijo del Discovery, selecciona por kid y valida la firma, iss, aud, exp y nbf. El JWKS contiene únicamente claves públicas; las claves privadas permanecen en un entorno de firma delimitado por KMS o HSM.

Para una rotación planificada, genero una clave con un nuevo kid, publico tanto la clave pública antigua como la nueva y actualizo el ETag. Luego, espero la ventana máxima de caché de 10 minutos del escenario o actualizo proactivamente los 200 verificadores y confirmo su preparación. Los tokens canary demuestran que la nueva clave funciona antes de cambiar el firmante.

Registro el momento de emisión del último token antiguo. La clave pública antigua permanece durante al menos el tiempo de vida de aceptación de 15 minutos más 1 minuto de desviación de reloj, y la elimino solo después de que el tráfico con el kid antiguo haya drenado. Ambas compuertas de transición son explícitas: no firmar tokens nuevos hasta que la nueva clave pública se haya propagado y no eliminar la clave pública antigua hasta que los tokens antiguos sean inválidos.

Ante un fallo de caché (cache miss), cada emisor obtiene una única actualización combinada (single-flight) con tiempo de espera, período de enfriamiento y ETag. Si la clave sigue ausente, se rechaza y se almacena brevemente en una caché negativa. Los valores aleatorios de kid no pueden amplificarse en una avalancha hacia el upstream. Durante una falla temporal del JWKS, una clave conocida puede continuar solo dentro de una ventana de obsolescencia acotada declarada, mientras que una clave desconocida siempre falla en modo cerrado.

Si la clave privada antigua se ve comprometida, detengo la firma antigua, publico y cambio a la clave nueva, transmito la invalidación de caché y revoco los tokens en el rango del kid antiguo o del iat antiguo. No conservo la superposición normal porque el atacante podría estar firmando. Finalmente, demuestro la finalización con versiones de JWKS, antigüedad de caché, errores de verificación por kid, un canary con la nueva clave y la última observación del kid antiguo, y realizo simulacros periódicos de transiciones y fallas del emisor".

Errores comunes

  • Cambiar el firmante antes de publicar la clave pública → los servicios con un JWKS obsoleto rechazan los tokens nuevos → publicar, comprobar la propagación y luego comenzar a firmar con la nueva.
  • Publicar solo la clave nueva → los tokens antiguos válidos fallan inmediatamente → publicar ambas generaciones durante la ventana de aceptación.
  • Esperar una duración arbitraria → puede ser menor que la vigencia real de la caché o del token → derivar las compuertas a partir de la obsolescencia máxima, la última emisión antigua, la vida útil del token y la desviación de reloj.
  • Descargar el JWKS en cada solicitud → una falla del emisor interrumpe toda la autenticación y amplifica la carga → usar almacenamiento en caché local, actualizaciones condicionales y una política de obsolescencia acotada.
  • Actualizar por cada kid desconocido → los identificadores aleatorios crean una tormenta de actualizaciones → usar single-flight, límites de tasa, caché negativa y límites en los datos de entrada.
  • Probar todas las claves ante un kid desconocido → la selección de claves se vuelve ambigua y amplía la superficie de ataque → actualizar una vez y luego rechazar si no hay coincidencia exacta.
  • Confiar en alg o jku del token → puede provocar confusión de algoritmos o SSRF → fijar los algoritmos y la ubicación del JWKS en la configuración del verificador.
  • Comprobar únicamente la firma → se puede aceptar un token destinado a otro emisor, audiencia o momento → validar también iss, aud, exp y nbf.
  • Reutilizar kid con nuevo material de clave → las cachés no pueden saber que un identificador cambió de contenido → asignar un identificador nuevo a cada generación de claves.
  • Tratar la eliminación central del JWKS como revocación inmediata → los verificadores aún pueden retener la caché antigua → proporcionar invalidación explícita de caché y revocación de tokens.
  • Mantener la superposición normal tras un compromiso → un atacante puede emitir tokens durante toda esa ventana → utilizar la ruta de emergencia y aceptar la reautenticación necesaria.

Preguntas de seguimiento y respuestas

Pregunta de seguimiento 1: ¿Exactamente cuánto tiempo debe permanecer la clave pública antigua?

Comenzando en el momento de emisión del último token con la clave antigua, consérvala durante al menos "vida útil máxima de aceptación + desviación de reloj". En este escenario, son 15 + 1 = 16 minutos. Agrega retrasos de encolamiento, emisión fuera de línea o cualquier ventana de aceptación implícita más extensa si existiera. Observa el tráfico del kid antiguo antes de la eliminación para que la configuración y la realidad del tiempo de ejecución coincidan.

Pregunta de seguimiento 2: ¿Por qué no devolver 401 de inmediato ante un kid desconocido?

Tras una rotación legítima, un nuevo token puede llegar antes de la actualización natural de la caché de ese proceso. Una actualización controlada cierra esa brecha. Debe ser agrupada, con límite de tasa y restringida a la URL de confianza; rechaza solo cuando el conjunto más reciente aún carezca del kid, equilibrando la compatibilidad con la resistencia a la amplificación de ataques.

Pregunta de seguimiento 3: ¿Debería la verificación fallar en modo abierto (fail-open) cuando el JWKS está caído?

Nunca omitas la verificación de firma. Una clave coincidente que ya sea de confianza en la caché puede completar la validación total dentro de una ventana de obsolescencia acotada y explícita. Rechaza claves desconocidas o solicitudes después de dicha ventana. Esto mantiene una interrupción breve del plano de control fuera de cada solicitud del plano de datos, conservando al mismo tiempo un límite de confianza cuantificable.

Pregunta de seguimiento 4: ¿Por qué pueden funcionar los tokens antiguos tras eliminar una clave comprometida?

Los verificadores aún pueden tener una caché antigua de 10 minutos, y los JWT autocontenidos no consultan a una autoridad central. La ruta de emergencia debe invalidar las cachés de forma proactiva y aplicar una regla de rechazo temporal basada en el kid antiguo, el momento de emisión o la versión del token. Los sistemas de alto riesgo pueden utilizar introspección o sesiones del lado del servidor para una revocación más rápida.

Pregunta de seguimiento 5: ¿Cómo evitar una tormenta de actualizaciones durante la rotación?

La prepublicación permite que la mayoría de los procesos obtengan la clave durante su actualización natural, y las actualizaciones proactivas se pueden escalonar con fluctuación (jitter). Para fallos reales de caché, utiliza un único proceso concurrente (single-flight) por emisor, además de tiempo de enfriamiento, ETag, tiempo de espera y una breve caché negativa. Monitorea el volumen de solicitudes de JWKS y la cardinalidad de kid desconocidos; limita la tasa ante anomalías en lugar de reintentar con mayor intensidad.

Pregunta de seguimiento 6: ¿Cómo se puede automatizar la compuerta de firma?

Asigna una versión a cada JWKS. Los verificadores informan periódicamente la versión cargada y el kid reconocido. El controlador de lanzamientos requiere un nivel de preparación objetivo y tokens canary exitosos en las rutas críticas antes de activar la clave privada. Cualquier aumento en las fallas de verificación del nuevo kid pausa o revierte el cambio de firma; dejar la nueva clave pública publicada no perjudica a los tokens antiguos.

Pregunta de seguimiento 7: ¿Qué sucede si los verificadores de terceros no pueden confirmar su preparación?

Publica un contrato de superposición estable: expón la nueva clave pública al menos con un período de caché máximo de anticipación, conserva la clave antigua hasta que todos los tokens antiguos expiren y envía los encabezados de almacenamiento en caché adecuados. El emisor no puede forzar a terceros a actualizar, por lo que las ventanas de compatibilidad, los avisos de cambio y las comprobaciones mediante canary reemplazan las confirmaciones internas. Un compromiso de emergencia aún puede dejar una ventana de exposición inevitable.

Fuentes públicas

Preguntas relacionadas