Códigos de recuperación legibles sin debilitar la recuperación de cuenta

En junio de 2026 rediseñamos la recuperación de cuenta de PayCal para que el proceso sea más fácil para usuarios reales bajo estrés: Recovery Codes guardados más cortos, un Verification Code por email claro, checksums resistentes a errores, pegado tolerante y un formulario tranquilo. La regla de seguridad no cambió: la recuperación debe probar acceso al buzón y posesión del secreto guardado de la cuenta.

Resumen ejecutivo

Por qué lo hicimos La recuperación de cuenta debe ser rápida, legible y tranquila sin volverse permisiva
Factor guardado Recovery Code: XXXXXX-XXXXXX-CC, donde 12 caracteres son secretos y 2 son checksum
Factor email Verification Code: XXXXCC, donde 4 caracteres son secretos y 2 son checksum
Alfabeto ABCDEFGHJKLMNPQRTUWXYZ346789, elegido para evitar caracteres visualmente confusos
Límites del servidor Ventana de 10 minutos para el código de email, uso único, límites de intentos, enfriamiento de reenvío, TTL de transacción y telemetría de abuso
Límite de seguridad El código de email prueba acceso al buzón. El Recovery Code prueba posesión del secreto guardado de la cuenta. Ninguno basta por sí solo para recuperación protegida.

El problema que estábamos resolviendo

La recuperación no es un inicio de sesión normal. La gente llega ahí cuando algo ya salió mal: un dispositivo perdido, un passkey faltante, un teléfono nuevo o una fecha límite. Un diseño de recuperación puede ser criptográficamente sólido y aun así fallar a los usuarios si el código es demasiado largo, difícil de leer, difícil de copiar o fácil de escribir mal.

El objetivo no era hacer casual la recuperación. El objetivo era hacer menos doloroso el camino honesto mientras manteníamos estricto al servidor. Eso significaba reducir errores de transcripción, mantener todos los campos importantes en una pantalla, permitir pegado flexible y validar errores obvios antes de que el usuario espere una respuesta del servidor.

Los nuevos códigos

Ambos códigos usan el mismo alfabeto de accesibilidad de PayCal:

ABCDEFGHJKLMNPQRTUWXYZ346789

Excluimos intencionalmente caracteres que suelen confundirse al leer, copiar, imprimir o escribir. Las entradas se normalizan convirtiéndolas a mayúsculas y quitando espacios y guiones, para que los usuarios puedan pegar códigos agrupados o sin agrupar.

Código Formato mostrado Caracteres secretos Caracteres checksum
Recovery Code XXXXXX-XXXXXX-CC 12 2
Verification Code XXXXCC 4 2

El checksum no es secreto y no añade entropía de seguridad. Existe para detectar errores honestos. Si los dos últimos caracteres no coinciden con la parte secreta, el navegador puede decir de inmediato: “Revisa los dos últimos caracteres”, antes de que el usuario consuma un intento de servidor.

La matemática

El Recovery Code tiene 12 caracteres secretos elegidos entre 28 símbolos posibles. Eso da:

28^12 = 232,218,265,089,212,416 possibilities
log2(28^12) ~= 57.7 bits

El Verification Code por email tiene 4 caracteres secretos:

28^4 = 614,656 possibilities
log2(28^4) ~= 19.2 bits

Cuando se consideran ambos factores juntos, el espacio secreto de búsqueda es:

28^16 = 142,734,349,946,674,946,768,896 possibilities
log2(28^16) ~= 76.9 bits

Un intento aleatorio contra el Recovery Code guardado es aproximadamente 1 entre 232 cuatrillones. Un intento aleatorio contra ambos factores juntos es aproximadamente 1 entre 142 sextillones. Con cinco intentos combinados, la probabilidad sigue siendo aproximadamente 3,5 x 10^-23, o cerca de 1 entre 28,5 sextillones.

Por qué la fuerza bruta online tarda tanto

La frase importante es recuperación online. Los atacantes no pueden ejecutar intentos ilimitados por PayCal tan rápido como calcule su hardware. El servidor controla el ritmo, cuenta fallos, expira códigos, registra telemetría de abuso y exige que el estado de la transacción coincida.

Con los valores conservadores actuales, la recuperación queda limitada por controles como:

  • los Verification Codes por email expiran después de 10 minutos;
  • los Verification Codes por email son de un solo uso;
  • los endpoints de verificación y prueba tienen límites de intentos;
  • los inicios de recuperación están limitados por día;
  • los reenvíos están limitados por hora y tienen enfriamiento;
  • los fallos de checksum vistos por el servidor cuentan para telemetría de abuso y política de intentos;
  • las transacciones de recuperación y ventanas bootstrap expiran;
  • el servidor, no el navegador, conserva la autoridad.

A cinco intentos por hora, probar la mitad del espacio del Recovery Code tomaría unos 2,65 billones de años. Probar la mitad del espacio combinado de Recovery Code más código de email al mismo ritmo online tomaría unos 1,63 trillones de años. Esa es la “cantidad realmente, realmente larga de tiempo” a la que nos referimos: no porque el usuario deba cargar un código enorme, sino porque la recuperación online tiene múltiples factores y el servidor no deja que los intentos corran libremente.

La ventana de 10 minutos

El código de email es intencionalmente corto porque no está destinado a proteger la cuenta por sí solo. Tiene límite de tiempo, se entrega al buzón del usuario, tiene límite de intentos y se consume al tener éxito.

Con 614.656 posibles secretos de código de email, cinco intentos dentro de una ventana de 10 minutos dan como máximo una probabilidad de 0,000813 % de adivinarlo al azar, o alrededor de 1 entre 122.931. Eso todavía no completaría una recuperación protegida, porque también deben pasar el Recovery Code guardado y la prueba de recuperación.

Qué cambió en la experiencia de usuario

Reducimos la pantalla de recuperación a un único formulario centrado. El usuario ve el campo de email, el campo Verification Code, el campo Recovery Code y una acción primaria: Verify and continue.

  • El usuario puede pegar el Recovery Code guardado mientras espera el email.
  • Se aceptan y normalizan espacios y guiones.
  • Los Recovery Codes se formatean automáticamente como XXXXXX-XXXXXX-CC.
  • Los Verification Codes se formatean automáticamente como XXXXCC.
  • Los caracteres inválidos del alfabeto muestran un mensaje claro en línea.
  • Los fallos de checksum se detectan localmente antes de la solicitud al servidor.
  • Cuando ambos formatos se ven correctos, el formulario muestra “Checking...” y envía automáticamente tras un pequeño debounce.
  • El botón Verify and continue sigue disponible como respaldo.

El resultado es más rápido para el usuario normal: menos pasos, menos sorpresas, menos estado oculto y menos castigo por copiar un código con espacios o guiones.

Qué cambió en el modelo de seguridad

El rediseño también reforzó varios límites de recuperación:

  • Los Recovery Codes sin procesar no se envían por email. Se muestran una vez al crearse y el usuario debe guardarlos.
  • PayCal almacena material recovery-wrapped y estado verificador, nunca el Recovery Code sin procesar.
  • Los magic links no pueden volver passkey-ready una cuenta protegida existente por sí solos.
  • El bootstrap solo por email se permite únicamente para la configuración del primer passkey cuando no existe material criptográfico protegido ni passkey existente.
  • Completar la recuperación exige que el ID de credencial passkey registrado en la transacción coincida con la solicitud de finalización.
  • Los passkeys antiguos se revocan después de una recuperación exitosa para que el nuevo passkey sea la credencial confiable.

Lo que esto no significa

Esto no afirma que un código legible de 12 caracteres sea un secreto offline de 256 bits. La recuperación de cuenta de PayCal es un flujo online de dos factores mediado por servidor. El Recovery Code es un factor; el acceso al buzón y el estado de transacción son otro; el registro de passkey es el paso final ligado al dispositivo.

Esa distinción importa. Hicimos el código legible porque las personas necesitan guardarlo y escribirlo correctamente. Mantuvimos estricto al servidor porque recuperación legible no debe significar recuperación débil.

La regla que ahora aplicamos

El código de email prueba acceso al buzón.
El Recovery Code prueba posesión del secreto guardado de la cuenta.
Ninguno basta por sí solo para recuperación protegida de cuenta.

Ese es el equilibrio que queríamos: recuperación que se siente fácil, rápida y simple para el propietario de la cuenta, pero sigue siendo lo bastante estricta para que adivinar a través del servidor no sea un camino práctico.