De volledige gids over TOTP in teams

Hoe TOTP werkt, welke eigenschappen van het algoritme alle problemen met gedeelde accounts veroorzaken, en wat een team moet opzetten.

TOTP neemt twee invoerwaarden en maakt één uitvoer. De invoer is een gedeeld geheim en de huidige tijd; de uitvoer is een zescijferige code die iedereen die hetzelfde geheim heeft kan berekenen. Er is geen netwerkaanroep, geen registratie per toestel, en geen serverspoor van het toestel dat een code maakte.

Alle moeilijkheden die een team met 2FA op gedeelde accounts tegenkomt vloeien voort uit die zin. Deze gids legt het mechanisme uit, en daarna wat het betekent voor een team van meer dan één persoon.

Wat is TOTP?

TOTP staat in RFC 6238. Het is een dunne laag boven HOTP (RFC 4226), dat een code maakt uit een geheim en een teller. HOTP verhoogt de teller bij elk gebruik; TOTP vervangt de teller door de huidige tijd in vaste stappen, zodat beide kanten op dezelfde waarde uitkomen zonder te communiceren.

Hoe werkt TOTP?

Eén generatie ziet er zo uit:

counter = floor(unix_time / period)        # de periode is meestal 30 seconden
digest  = hmac_sha1(secret, counter)       # 20 bytes
offset  = digest[-1] & 0x0F                # dynamische afkapping
number  = int_from(digest[offset:offset+4]) & 0x7FFFFFFF
code    = number % 10 ** digits            # met nullen aangevuld, meestal 6 cijfers

Vier parameters spelen mee: het geheim, de periode (standaard 30 seconden), het aantal cijfers (6) en het algoritme (in de praktijk SHA-1). De authenticator-app van een telefoon doet precies het bovenstaande, offline. De server doet hetzelfde en vergelijkt.

Twee gevolgen verdienen het duidelijk gezegd te worden, want daar beginnen de meeste misverstanden:

  • Niets in een code identificeert zijn bron. De server ziet zes cijfers die kloppen, niet welk toestel of welke persoon ze maakte.
  • Er is geen toestand om in te trekken. Een toestel inschrijven is geen registratie, het is een kopie van het geheim. Niets in het account houdt het bij.

Het geheim ís het account

Het geheim is meestal 16 tot 32 base32-tekens lang. Het verloopt niet, het is aan geen toestel gebonden, en het protocol kent geen begrip van rotatie ervan.

Wanneer je een installatie-QR-code scant, lees je een URI in het formaat dat is gepubliceerd onder de naam Key URI Format:

otpauth://totp/Acme:[email protected]?secret=JBSWY3DPEHPK3PXP&issuer=Acme&algorithm=SHA1&digits=6&period=30

Die tekenreeks is de hele sleutel. De QR-code is er een foto van. Wat betekent dat een schermafbeelding van het inschrijfscherm geen gemak is. Het is de tweede factor, in een vorm die zich laat doorsturen, back-uppen en indexeren.

Daaruit vloeien de eigenschappen voort die gedeelde 2FA ongemakkelijk maken:

  1. Bezit staat gelijk aan blijvende toegang. Wie het geheim heeft kan alle toekomstige codes maken, op elk toestel, voor altijd.
  2. Kopieën zijn onzichtbaar. Geen enkele beveiligingspagina zal je ooit zeggen hoeveel er bestaan.
  3. Intrekken betekent alles terugnemen. De enige manier om één kopie ongeldig te maken is de 2FA op het account uitschakelen en opnieuw instellen, voor iedereen.

De inschrijving, stap voor stap

Wat er tijdens de installatie gebeurt verklaart waarom sommige latere keuzes onomkeerbaar zijn.

  1. De aanbieder maakt het geheim. Het ontstaat aan de serverkant, wordt op je account opgeslagen, en verandert nooit meer tenzij de 2FA wordt uitgeschakeld.
  2. Het wordt één keer getoond, als QR-code en meestal als base32-tekenreeks achter een link „kun je niet scannen?”. Dat is het enige moment waarop de sleutel aan een mens wordt getoond.
  3. Je client slaat het op. Een authenticator-app schrijft het op het toestel; een teamkluis schrijft het in zijn eigen versleutelde opslag. Beide doen hetzelfde: een kopie bewaren.
  4. Je voert een code in ter bevestiging. Dat bewijst dat de kopie werkt. Het is geen registratie van het toestel: niets over je telefoon wordt ergens naartoe gestuurd.
  5. De aanbieder levert herstelcodes, meestal op dat moment, en meestal één keer.

Stap 2 is de hele inzet. Wat dat scherm ziet, heeft vanaf dan de tweede factor van het account, en stap 4 geeft geen enkele aanwijzing over hoeveel dingen het hebben gezien. Daarom is „we maken er voorlopig even een schermafbeelding van” een beslissing zonder vervaldatum, en daarom is de juiste vraag tijdens de installatie niet wie hem scant, maar waar de ene kopie gaat wonen.

Waarom het venster van dertig seconden bestaat

Beide kanten moeten het eens worden over de teller zonder met elkaar te praten: dus worden ze het eens over de tijd. Een stap van dertig seconden is het compromis: lang genoeg om een code te typen, kort genoeg dat een onderschepte code kort daarna niets meer waard is.

Verificatiediensten aanvaarden meestal ook de onmiddellijk voorgaande stap, en soms de volgende, om typtijd en kleine klokafwijkingen op te vangen. Daarom werkt een code vaak nog een paar seconden nadat het aftellen op nul is gesprongen.

Het betekent ook dat klokken tellen. Een toestel waarvan de klok een minuut afwijkt berekent de codes van een stap die de server al verlaten heeft, en alle codes worden geweigerd. Meldt iemand dat de codes niet meer werken, dan is een niet-gesynchroniseerde klok het eerste om na te kijken, voor je aanneemt dat het geheim verkeerd is. Alles wat voor een team codes maakt heeft een gelijklopende klok nodig als strikte eis, niet als gemak.

Hoe de verificatie werkt, en waarom een code maar één keer zou moeten dienen

Aan de andere kant berekent de verificatiedienst de verwachte code voor de huidige stap en vergelijkt. Drie details van die vergelijking tellen voor wie gedeelde accounts beheert.

Het acceptatievenster is een bewuste afweging. Elke extra stap die een server aanvaardt verlengt de tijd waarin een onderschepte code bruikbaar blijft. Eén stap terug is normaal; brede vensters wijzen op een dienst die klokproblemen maskeert.

Een code zou eenmalig moeten zijn. RFC 6238 zegt uitdrukkelijk dat een tweede authenticatie met dezelfde tijdstap geweigerd zou moeten worden, en goede implementaties houden de laatst gebruikte stap per account bij. Het praktische effect voor een team: twee mensen die binnen dezelfde dertig seconden inloggen kunnen de tweede poging geweigerd zien terwijl de getoonde code klopt. Op de volgende code wachten is het middel, en dat is geen bug.

Pogingen zouden beperkt moeten zijn. Zes cijfers zijn een miljoen mogelijkheden, wat alleen tegen brute kracht bestand is als de server de pogingen beperkt. Die bescherming leeft volledig aan de kant van de aanbieder; niets wat je met het geheim doet verbetert haar.

Geen van deze punten is aan de kant van een team in te stellen, maar alle drie verklaren symptomen die anders op een kapotte configuratie lijken: een code die met vertraging werkt, een code die voor de ene collega werkt en voor de volgende niet, een aanmelding die na een reeks pogingen juiste codes gaat weigeren.

Is TOTP veilig? Waartegen het beschermt en waartegen niet

Het verdedigt goed tegen hergebruikte wachtwoorden, credential stuffing uit datalekken, en een op zichzelf gelekt wachtwoord. Een aanvaller die alleen het wachtwoord heeft kan niet inloggen.

Het verdedigt niet tegen doorgifte in realtime: een overtuigende valse aanmeldpagina die de code vraagt en hem binnen zijn geldigheidsvenster doorstuurt. TOTP-codes zijn per ontwerp phishbaar, want de gebruiker kan ze lezen. Het doet ook niets tegen malware op het toestel dat ze maakt, een gestolen sessiecookie, of een gekopieerd geheim.

Dat laatste punt is het gat dat teams aangaat. Alle andere dreigingen in de lijst worden behandeld door de beveiligingsinstellingen van het account zelf. Een gekopieerd geheim wordt alleen behandeld door de manier waarop je team het bewaart, wat de aanbieder niet kan zien en waarbij hij niet kan helpen.

Waar teams TOTP breken

Op oplopende schade:

  • Eén persoon houdt de app. Er wordt geen geheim gekopieerd, maar het account hangt nu af van de beschikbaarheid van een mens.
  • Een apart toestel in een lade. Prima op kantoor, nutteloos op afstand, en het legt niets vast.
  • Het geheim naast het wachtwoord in een gedeelde kluis. Handig, en het brengt beide factoren in één houder.
  • De QR-code gepubliceerd in een kanaal. Blijvende en niet te tellen verspreiding van de sleutel.

De afwegingen van elk, en hoe je kiest, zijn het onderwerp van de beste manieren om de 2FA van een gedeeld account te beheren en, per soort account, van de 2FA van een gedeeld account: hoe pakt een team dat aan?.

Wat een teamwaardige TOTP-opzet vereist

Een opzet die meer dan één persoon overleeft heeft zes dingen nodig, welk gereedschap je ook kiest. Dat is de lijst die je nakijkt voor je kiest.

Eén bewaarplaats voor het geheim. Eén plek heeft het, één keer ingevoerd, door wie het account beheert. Al het andere verbruikt codes.

Codes uitdelen zonder het geheim uit te delen. Wie moet inloggen krijgt de actuele code en het aftellen, en kan uit wat hij ziet het geheim niet reconstrueren.

Toestemming per persoon en per account. Niet per kluis. Wie op het sociale profiel post heeft geen enkele reden codes te maken voor de betaaldienstverlener.

Een spoor van de code-raadplegingen. Het logboek van de aanbieder toont het gedeelde account, niet de persoon. Een register van wie een code opvroeg is het enige dat een incident terugbrengt tot een naam en een tijdstempel.

Een gelijklopende klok overal waar codes worden gemaakt, om de hierboven gegeven reden.

Een getest herstelpad. Dat is het deel dat teams op het slechtste moment ontdekken.

Back-ups en herstel

Twee afzonderlijke dingen moeten terug te halen zijn, en ze zouden niet op dezelfde plek als elkaar moeten wonen, en ook niet in het account dat ze beschermen.

Het geheim, want het verliezen dwingt tot een 2FA-reset van het account. Zijn plek zou genoteerd moeten zijn, en bereikbaar voor meer dan één beheerder.

De herstelcodes van de aanbieder, eenmalige terugvallen die bij de inschrijving worden verstrekt. Ze dienen voor de dag dat het geheim weg is, niet voor dagelijkse toegang: ze zijn beperkt in aantal, en niemand kan zeggen welke door wie is opgebruikt.

De enige kopie van een herstelcode opbergen in het account dat hij herstelt is een lus die zich precies sluit wanneer je haar open nodig hebt. Hetzelfde geldt voor het geheim van het account dat de kluis bewaakt die je geheimen bevat.

Het herstelpad verdient het één keer bewust te worden doorlopen, op een account zonder belang: de 2FA uitschakelen met een herstelcode, hem opnieuw instellen, controleren dat iedereen die de codes nodig heeft ze nog heeft. Zonder die droogloop ontdek je de procedure op de dag dat het account al onbereikbaar is.

De parameters die je echt tegenkomt

De standaardwaarden dekken de overgrote meerderheid: SHA-1, zes cijfers, dertig seconden. Het gebruik van SHA-1 is hier niet de zwakte die het lijkt, want HMAC-SHA1 wordt niet geraakt door de botsingsaanvallen die SHA-1 voor handtekeningen met pensioen stuurden. De RFC staat SHA-256 en SHA-512 toe, maar de ondersteuning in apps is ongelijk genoeg dat aanbieders ze zelden gebruiken.

Een minderheid van diensten wijkt af: acht cijfers, een periode van zestig seconden, of een niet-numeriek codealfabet. Het praktische gevolg is een opslageis. Wat een geheim bewaart moet zijn parameters digits, period en algorithm ernaast bewaren, anders zijn de codes van die accounts verkeerd op een manier die op een kapot geheim lijkt. Elk gereedschap dat alleen een kale geheimtekenreeks aanvaardt neemt stilzwijgend de standaardwaarden aan.

Kleine woordenlijst

Het loont hier binnen een team overeenstemming over te bereiken, want deze woorden worden door elkaar gebruikt en mensen praten uiteindelijk over verschillende dingen.

  • Geheim (of seed): de base32-tekenreeks waar de codes uit komen. Blijvend.
  • Sleutel: meestal een synoniem voor geheim. Soms bedoelt men de Key URI, wat iets anders is.
  • Code (of OTP): de zes cijfers. Eén stap geldig, eenmalig.
  • Token: door elkaar gebruikt voor de zes cijfers en, bij eigen hardware, voor het toestel dat het geheim bewaart. Zeg welke.
  • HOTP: dezelfde constructie met een teller die bij elk gebruik oploopt.
  • Periode / tijdstap: het geldigheidsinterval van een code, meestal 30 seconden.
  • Drift: het verschil tussen twee klokken, waardoor codes falen.
  • Key URI / otpauth-URI: de tekstvorm van de inschrijflading; de QR-code is de visuele codering ervan.
  • Herstelcodes: eenmalige terugvallen van de dienst, los van TOTP.
  • Inschrijving: de kopie van het geheim naar een toestel. Geen registratie, ondanks de indruk die het wekt.

Waar Share Auth staat

Share Auth voert de zes eisen hierboven uit voor de accounts die echt maar één inlog hebben. De geheimen worden één keer ingevoerd en versleuteld opgeslagen, met digits, period en algorithm ernaast bewaard; de leden zien codes en aftellingen in plaats van geheimen; de rechten gelden per lid; en elke raadpleging wordt in een toegangslogboek geschreven. Een lid verwijderen trekt zijn toegang tot alle accounts in één keer in.

Het is geen vervanging voor toegang per persoon op de platforms die dat bieden. Waar een tool elke teamgenoot een eigen inlog en een eigen tweede factor kan geven, is dat strikt beter dan wat dan ook delen.

Controlelijst

  • Het geheim van elk gedeeld account heeft een bekend adres, bereikbaar voor twee beheerders.
  • Niemand heeft het geheim nodig om een code te krijgen.
  • Toegang wordt per persoon, per account verleend, en is met één handeling in te trekken.
  • Code-raadplegingen worden gelogd op het moment dat ze gebeuren.
  • De herstelcodes liggen bij het geheim, buiten het account dat ze herstellen.
  • De klokken lopen gelijk overal waar codes worden gemaakt.
  • Het herstelpad is één keer met opzet getest.
  • Elk account waarvan de QR-code ooit is gedeeld heeft een 2FA-reset gehad.

Veelgestelde vragen

TOTP, van „time-based one-time password”, is het algoritme uit RFC 6238 dat een gedeeld geheim en de huidige tijd omzet in een korte code, meestal zes cijfers, ongeveer dertig seconden geldig. Het account en de authenticator-app hebben hetzelfde geheim: ze berekenen dus dezelfde code zonder iets uit te wisselen.

Lees ook · Hoe TOTP werkt