Codici di recupero leggibili senza indebolire il recupero account

Nel giugno 2026 abbiamo riprogettato il recupero account PayCal per renderlo più semplice per utenti reali sotto stress: Recovery Code salvati più brevi, un Verification Code email chiaro, checksum resistenti ai typo, comportamento di incolla permissivo e un unico form calmo. La regola di sicurezza non è cambiata: il recupero deve provare sia l’accesso alla casella email sia il possesso del segreto account salvato.

Sintesi esecutiva

Perché lo abbiamo fatto Il recupero account deve essere rapido, leggibile e calmo senza diventare permissivo
Fattore salvato Recovery Code: XXXXXX-XXXXXX-CC, dove 12 caratteri sono segreti e 2 sono checksum
Fattore email Verification Code: XXXXCC, dove 4 caratteri sono segreti e 2 sono checksum
Alfabeto ABCDEFGHJKLMNPQRTUWXYZ346789, scelto per evitare caratteri visivamente confondibili
Limiti server Finestra email-code di 10 minuti, uso singolo, limiti di tentativi, cooldown di reinvio, TTL di transazione e telemetria abuso
Confine di sicurezza Il codice email prova l’accesso alla casella. Il Recovery Code prova il possesso del segreto account salvato. Nessuno dei due basta da solo per il recupero protetto.

Il problema che stavamo risolvendo

Il recupero non è un login normale. Le persone ci arrivano quando qualcosa è già andato storto: dispositivo perso, passkey mancante, nuovo telefono o una scadenza. Un design di recupero può essere crittograficamente solido e comunque fallire gli utenti se il codice è troppo lungo, difficile da leggere, difficile da copiare o facile da digitare male.

L’obiettivo non era rendere il recupero casuale. L’obiettivo era rendere il percorso onesto meno doloroso mantenendo il server rigoroso. Questo significava ridurre errori di trascrizione, mantenere tutti i campi importanti su una sola schermata, rendere l’incolla tollerante e validare typo evidenti prima che l’utente aspetti una risposta server.

I nuovi codici

Entrambi i codici usano lo stesso alfabeto di accessibilità PayCal:

ABCDEFGHJKLMNPQRTUWXYZ346789

Escludiamo intenzionalmente caratteri spesso confusi quando vengono letti, copiati, stampati o digitati. Gli input vengono normalizzati in maiuscolo e rimuovendo spazi e trattini, così gli utenti possono incollare codici in forma raggruppata o non raggruppata.

Codice Formato visualizzato Caratteri segreti Caratteri checksum
Recovery Code XXXXXX-XXXXXX-CC 12 2
Verification Code XXXXCC 4 2

Il checksum non è un segreto e non aggiunge entropia di sicurezza. Serve a intercettare errori onesti. Se gli ultimi due caratteri non corrispondono alla parte segreta, il browser può dire subito “Check the last two characters,” prima che l’utente consumi un tentativo server.

La matematica

Il Recovery Code ha 12 caratteri segreti scelti tra 28 simboli possibili. Questo dà:

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

Il Verification Code email ha 4 caratteri segreti:

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

Quando i due fattori sono considerati insieme, lo spazio segreto di ricerca è:

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

Un singolo guess casuale contro il Recovery Code salvato è circa 1 su 232 quadrilioni. Un singolo guess casuale contro entrambi i fattori insieme è circa 1 su 142 sestilioni. Con cinque guess combinati, la probabilità resta circa 3,5 x 10^-23, cioè circa 1 su 28,5 sestilioni.

Perché la forza bruta online richiede così tanto tempo

La frase importante è recupero online. Gli attaccanti non possono eseguire guess illimitati attraverso PayCal alla velocità del loro hardware. Il server controlla il ritmo, conta i fallimenti, fa scadere i codici, registra telemetria abuso e richiede che lo stato della transazione sia coerente.

Con i default conservativi attuali, il recupero è limitato da controlli come:

  • i Verification Code email scadono dopo 10 minuti;
  • i Verification Code email sono monouso;
  • gli endpoint di verifica e proof sono limitati nei tentativi;
  • gli avvii di recupero sono limitati per giorno;
  • i reinvii sono limitati per ora e hanno un cooldown;
  • i fallimenti checksum visti dal server contano nella telemetria abuso e nella policy tentativi;
  • le transazioni di recupero e le finestre bootstrap scadono;
  • il server, non il browser, resta autorevole.

A cinque guess all’ora, provare metà dello spazio del Recovery Code richiederebbe circa 2,65 trilioni di anni. Provare metà dello spazio combinato Recovery Code più codice email allo stesso ritmo online richiederebbe circa 1,63 quintilioni di anni. Questo è il “tempo davvero, davvero lungo” che intendiamo: non perché l’utente debba portare un codice enorme, ma perché il recupero online ha più fattori e il server non lascia correre liberamente i guess.

La finestra di 10 minuti

Il codice email è intenzionalmente breve perché non deve proteggere l’account da solo. È limitato nel tempo, consegnato alla casella dell’utente, limitato nei tentativi e consumato al successo.

Con 614.656 possibili segreti email-code, cinque guess dentro una finestra di 10 minuti danno al massimo una probabilità dello 0,000813% di indovinare il codice email per caso, circa 1 su 122.931. Questo non completerebbe comunque il recupero protetto, perché devono passare anche il Recovery Code salvato e il recovery proof.

Cosa è cambiato nell’esperienza utente

Abbiamo ridotto la schermata di recupero a un unico form centrato. L’utente vede il campo email, il campo Verification Code, il campo Recovery Code e un’azione primaria: Verify and continue.

  • L’utente può incollare il Recovery Code salvato mentre aspetta l’email.
  • Spazi e trattini sono accettati e normalizzati.
  • I Recovery Code vengono formattati automaticamente come XXXXXX-XXXXXX-CC.
  • I Verification Code vengono formattati automaticamente come XXXXCC.
  • Caratteri alfabetici non validi mostrano un messaggio inline chiaro.
  • I fallimenti checksum sono intercettati localmente prima della richiesta server.
  • Quando entrambi i formati sembrano corretti, il form mostra “Checking...” e invia automaticamente dopo un breve debounce.
  • Il pulsante Verify and continue resta disponibile come fallback.

Il risultato è più rapido per l’utente normale: meno passaggi, meno sorprese, meno stato nascosto e meno penalità per aver copiato un codice con spazi o trattini.

Cosa è cambiato nel modello di sicurezza

La riprogettazione ha anche stretto diversi confini di recupero:

  • I Recovery Code grezzi non vengono inviati via email. Sono mostrati una volta alla creazione e devono essere salvati dall’utente.
  • PayCal conserva materiale recovery-wrapped e stato verifier, mai il Recovery Code grezzo.
  • I magic link non possono rendere da soli passkey-ready un account protetto esistente.
  • Il bootstrap solo email è consentito solo per il setup della prima passkey quando non esiste materiale crypto protetto né una passkey esistente.
  • Completare il recupero richiede che l’ID credenziale passkey registrato nella transazione corrisponda alla richiesta di completamento.
  • Le vecchie passkey sono revocate dopo un recupero account riuscito, così la nuova passkey diventa la credenziale fidata.

Cosa non significa

Questo non significa che un codice leggibile di 12 caratteri sia un segreto offline da 256 bit. Il recupero account PayCal è un flusso online, mediato dal server, a due fattori. Il Recovery Code è un fattore; accesso alla casella e stato transazione sono un altro; la registrazione passkey è il passaggio finale legato al dispositivo.

Questa distinzione conta. Abbiamo reso il codice leggibile perché le persone devono salvarlo e digitarlo correttamente. Abbiamo mantenuto rigoroso il server perché recupero leggibile non deve significare recupero debole.

La regola che ora applichiamo

Il codice email prova l’accesso alla casella.
Il Recovery Code prova il possesso del segreto account salvato.
Nessuno dei due basta da solo per il recupero account protetto.

Questo è l’equilibrio che volevamo: un recupero che sembri facile, rapido e semplice per il proprietario dell’account, pur restando abbastanza rigoroso da rendere impraticabile il guessing attraverso il server.