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.