Managementsamenvatting
| Waarom we dit deden | Accountherstel moet snel, leesbaar en rustig zijn zonder permissief te worden |
| Opgeslagen factor | Recovery Code: XXXXXX-XXXXXX-CC, waarbij 12 tekens geheim zijn en 2 checksum zijn |
| E-mailfactor | Verification Code: XXXXCC, waarbij 4 tekens geheim zijn en 2 checksum zijn |
| Alfabet | ABCDEFGHJKLMNPQRTUWXYZ346789, gekozen om visueel verwarrende tekens te vermijden |
| Serverlimieten | E-mailcodevenster van 10 minuten, eenmalig gebruik, poginglimieten, resend-cooldown, transactie-TTL en misbruiktelemetrie |
| Beveiligingsgrens | De e-mailcode bewijst mailbox-toegang. De Recovery Code bewijst bezit van het opgeslagen accountgeheim. Geen van beide is alleen genoeg voor beschermd herstel. |
Het probleem dat we oplosten
Herstel is geen normale login. Mensen komen er wanneer er al iets mis is gegaan: een verloren apparaat, ontbrekende passkey, nieuwe telefoon of deadline. Een herstelontwerp kan cryptografisch degelijk zijn en gebruikers toch laten falen als de code te lang, moeilijk leesbaar, moeilijk te kopiëren of makkelijk verkeerd te typen is.
Het doel was niet om herstel vrijblijvend te maken. Het doel was het eerlijke pad minder pijnlijk te maken terwijl de server strikt blijft. Dat betekende minder overschrijffouten, alle belangrijke velden op één scherm, vergevingsgezind plakken en duidelijke typo’s valideren voordat de gebruiker op een serverantwoord wacht.
De nieuwe codes
Beide codes gebruiken hetzelfde PayCal-toegankelijkheidsalfabet:
ABCDEFGHJKLMNPQRTUWXYZ346789
We sluiten bewust tekens uit die vaak worden verward bij lezen, kopiëren, printen of typen. Invoer wordt genormaliseerd door hoofdletters te gebruiken en spaties en koppeltekens te verwijderen, zodat gebruikers codes gegroepeerd of ongegroepeerd kunnen plakken.
| Code | Weergaveformaat | Geheime tekens | Checksumtekens |
|---|---|---|---|
| Recovery Code | XXXXXX-XXXXXX-CC |
12 | 2 |
| Verification Code | XXXXCC |
4 | 2 |
De checksum is geen geheim en voegt geen beveiligingsentropie toe. Hij bestaat om eerlijke fouten te vangen. Als de laatste twee tekens niet overeenkomen met het geheime deel, kan de browser meteen zeggen: “Check the last two characters,” voordat de gebruiker een serverpoging verbruikt.
De wiskunde
De Recovery Code heeft 12 geheime tekens uit 28 mogelijke symbolen. Dat geeft:
28^12 = 232,218,265,089,212,416 possibilities
log2(28^12) ~= 57.7 bits
De e-mail Verification Code heeft 4 geheime tekens:
28^4 = 614,656 possibilities
log2(28^4) ~= 19.2 bits
Wanneer beide factoren samen worden bekeken, is de geheime zoekruimte:
28^16 = 142,734,349,946,674,946,768,896 possibilities
log2(28^16) ~= 76.9 bits
Eén willekeurige gok tegen de opgeslagen Recovery Code is ongeveer 1 op 232 quadriljoen. Eén willekeurige gok tegen beide factoren samen is ongeveer 1 op 142 sextiljoen. Met vijf gecombineerde gokken blijft de kans ongeveer 3,5 x 10^-23, of ruwweg 1 op 28,5 sextiljoen.
Waarom online brute force zo lang duurt
De belangrijke term is online herstel. Aanvallers kunnen niet onbeperkt pogingen door PayCal draaien zo snel als hun hardware kan rekenen. De server bepaalt het tempo, telt fouten, laat codes verlopen, registreert misbruiktelemetrie en vereist dat transactiestatus klopt.
Met de huidige conservatieve standaardwaarden wordt herstel begrensd door controles zoals:
- e-mail Verification Codes verlopen na 10 minuten;
- e-mail Verification Codes zijn eenmalig bruikbaar;
- verificatie- en proof-endpoints hebben poginglimieten;
- herstelstarts zijn per dag beperkt;
- resends zijn per uur beperkt en hebben een cooldown;
- door de server geziene checksumfouten tellen mee voor misbruiktelemetrie en pogingbeleid;
- hersteltransacties en bootstrapvensters verlopen;
- de server, niet de browser, blijft gezaghebbend.
Bij vijf pogingen per uur zou de helft van de Recovery Code-zoekruimte proberen ongeveer 2,65 biljoen jaar duren. De helft van de gecombineerde Recovery Code plus e-mailcode-zoekruimte proberen in hetzelfde online tempo zou ongeveer 1,63 quintiljoen jaar duren. Dat is de “echt, echt lange tijd” die we bedoelen: niet omdat de gebruiker een enorme code moet dragen, maar omdat online herstel meerdere factoren heeft en de server pogingen niet vrij laat lopen.
Het venster van 10 minuten
De e-mailcode is bewust kort omdat hij niet bedoeld is om het account alleen te beschermen. Hij is tijdsbeperkt, wordt naar de inbox van de gebruiker gestuurd, is pogingbeperkt en wordt bij succes verbruikt.
Met 614.656 mogelijke e-mailcodegeheimen geven vijf pogingen binnen één codevenster van 10 minuten maximaal 0,000813% kans om de e-mailcode toevallig te raden, of ongeveer 1 op 122.931. Dat zou beschermd herstel nog steeds niet voltooien, omdat de opgeslagen Recovery Code en recovery proof ook moeten slagen.
Wat veranderde in de gebruikerservaring
We brachten het herstelscherm terug tot één gecentreerd formulier. De gebruiker ziet het e-mailveld, het Verification Code-veld, het Recovery Code-veld en één primaire actie: Verify and continue.
- De gebruiker kan de opgeslagen Recovery Code plakken terwijl hij op e-mail wacht.
- Spaties en koppeltekens worden geaccepteerd en genormaliseerd.
- Recovery Codes worden automatisch geformatteerd als
XXXXXX-XXXXXX-CC. - Verification Codes worden automatisch geformatteerd als
XXXXCC. - Ongeldige alfabettekens tonen een duidelijke inline melding.
- Checksumfouten worden lokaal gevangen vóór het serververzoek.
- Wanneer beide formaten goed lijken, toont het formulier “Checking...” en verzendt het automatisch na een korte debounce.
- De knop Verify and continue blijft beschikbaar als fallback.
Het resultaat is sneller voor de normale gebruiker: minder stappen, minder verrassingen, minder verborgen toestand en minder straf voor het kopiëren van een code met spaties of koppeltekens.
Wat veranderde in het beveiligingsmodel
Het herontwerp heeft ook meerdere herstelgrenzen aangescherpt:
- Ruwe Recovery Codes worden niet gemaild. Ze worden één keer getoond bij aanmaak en moeten door de gebruiker worden opgeslagen.
- PayCal bewaart recovery-wrapped materiaal en verifier-status, nooit de ruwe Recovery Code.
- Magic links kunnen een bestaand beschermd account niet op zichzelf passkey-ready maken.
- Email-only bootstrap is alleen toegestaan voor eerste-passkey setup wanneer er geen bestaand beschermd cryptomateriaal en geen bestaande passkey is.
- Herstel afronden vereist dat de passkey credential ID die in de hersteltransactie is geregistreerd overeenkomt met het afrondingsverzoek.
- Oude passkeys worden ingetrokken na succesvol accountherstel zodat de nieuwe passkey de vertrouwde credential wordt.
Wat dit niet betekent
Dit is geen bewering dat een 12-teken menselijk leesbare code een 256-bit offline geheim is. PayCal-accountherstel is een online, servergemedieerde, twee-factor herstelstroom. De Recovery Code is één factor; inbox-toegang en transactiestatus zijn een andere; passkey-registratie is de laatste apparaatgebonden stap.
Dat onderscheid is belangrijk. We maakten de code leesbaar omdat mensen hem moeten opslaan en correct typen. We hielden de server strikt omdat leesbaar herstel geen zwak herstel mag betekenen.
De regel die we nu afdwingen
De e-mailcode bewijst inbox-toegang.
De Recovery Code bewijst bezit van het opgeslagen accountgeheim.
Geen van beide is alleen genoeg voor beschermd accountherstel.
Dat is de balans die we wilden: herstel dat makkelijk, snel en simpel voelt voor de accounteigenaar, maar strikt genoeg blijft zodat raden via de server geen praktisch pad is.