Time-based One-time Password: TOTP erklärt
Internetnutzer müssen regelmäßig Passwörter eingeben – egal ob bei der Anmeldung auf einer Social-Media-Plattform, beim Onlineshopping oder dem Banking im Internet. Passwörter sichern sensible Daten vor dem Zugriff anderer. Dabei vernachlässigen viele Nutzer allerdings die nötige Sorgfalt: Einfache Passwörter lassen sich von Profis innerhalb von Sekunden knacken. Andere bewahren ihre eigentlich guten Kennwörter falsch auf und bieten Kriminellen auf diese Weise ein Einfallstor. Nicht zu verachten sind zudem Schwachstellen bei den Diensten, bei denen man sich als Nutzer anmeldet. Werden die Passwörter dort nicht ordentlich gesichert, sind mitunter die Daten von tausenden Nutzern gefährdet.
Eine Möglichkeit, das Risiko zu verkleinern, ist die Zwei-Faktor-Authentifizierung bzw. Multi-Faktor-Authentifizierung. Statt nur ein Passwort zu verwenden, muss mindestens ein weiteres Authentifikationsmerkmal angegeben werden. Dieses bekommen Nutzer der Technik entweder per Smartphone oder über einen Hardware-Token. Die zusätzlichen Faktoren haben meist gemeinsam, dass sie einmalig erstellt werden und nur für kurze Zeit gültig sind – es wird ein sogenanntes Time-based One-time Password generiert. Wir erklären, wie das funktioniert.
Wofür braucht man TOTP?
Gewöhnliche Passwörter – wie sicher sie auch immer gewählt werden – haben einen Nachteil: Kennt auch jemand anderes die Zeichenfolge, ist die Sicherheit nicht mehr gegeben. Eine Lösung wäre, das Kennwort häufig zu wechseln, aber selbst die vorbildlichsten Nutzer machen dies nicht stündlich. Die Lösung ist TOTP: ein Passwort, dass nur für einen kurzen Zeitraum gültig ist und dann wieder verfällt. Die Internet Engineering Task Force (IETF) hat den Time-based One-time Password Algorithm 2011 im RFC 6238 veröffentlicht, um für mehr Sicherheit im Internet zu sorgen.
Besonders beliebt sind solche Einmalpasswörter im Zuge von Multi-Faktor-Authentifizierung. Dabei verwenden Nutzer zur Anmeldung bei einem Webdienst zunächst ihr persönliches, feststehendes Kennwort; zusätzlich wird ein zeitbegrenztes Passwort zu genau diesem Anmeldevorgang erzeugt. Der Nutzer erhält dies z. B. per App oder durch ein extra dafür vorgesehenes Gerät (Token).
Ist das Passwort einmal genutzt oder wird in einem bestimmten Zeitraum nicht genutzt, verfällt es. Kriminellen wird es somit sehr schwer gemacht, den zweiten Faktor zu bekommen. Auch wenn sie das eigentliche Passwort kennen, haben sie nur wenige Möglichkeiten, auch das TOTP zu erlangen, bzw. keine Zeit, dieses zu knacken.
Wie funktioniert der Time-based One-time Password Algorithm?
Basis des TOTP ist eine Hashfunktion, also ein kryptografisches Verfahren. Man bildet aus einem geheimen Passwort und einem Zeitstempel eine verschlüsselte Zeichenfolge. Das Passwort ist sowohl dem Nutzer als auch dem Server bekannt. Die Zeitangabe findet in Unixzeit statt.
Unixzeit ist ein Wert, der die Sekunden seit dem 1. Januar 1970 angibt.
TOTP ist eigentlich eine Weiterentwicklung von HOTP, was für „HMAC-based One-time Password“ steht. Auch TOTP basiert auf dem HMAC-Verfahren – die Hash-Operation im Hintergrund. Sowohl das Gerät des Nutzers als auch der Server erstellen aus dem geheimen Passwort in Kombination mit einem Zähler einen Hashwert. Beide Werte sind identisch, weshalb die Authentifizierung funktioniert.
Die Hashfunktion an sich ist nicht festgelegt; in der Praxis greift man z. B. zu SHA-1 (so auch der Google Authenticator), das einen Hashwert mit einer Länge von 160 Bit erzeugt. Der Einfachheit halber wird dieser Wert noch über eine Kompressionsfunktion gekürzt. Am Ende hat man beispielsweise eine sechsstellige Zahl, die Nutzer dann leicht bei der Anmeldung im Webdienst eingeben können.
Das geheime Passwort ist bei einem Token im Gerät verankert. Der Nutzer selbst weiß es daher oftmals nicht. Falls dies anders ist, sollte der SecretKey sicher aufbewahrt werden, bestenfalls offline und eventuell sogar ausgedruckt und an einem gesicherten Ort gelagert. Verliert man dieses Passwort, ist die Anmeldung beim Dienst nicht mehr möglich.
HOTP setzt auf einen Zähler als zweiten Bestandteil der Funktion. Diesen teilen Server und Nutzer miteinander. Das Problem dabei ist, dass das generierte Passwort so lange gültig ist, bis es verwendet wurde. TOTP schränkt dies ein: Nur innerhalb eines festgelegten Zeitrahmens kann der so erstellte Code eingesetzt werden. Wie funktioniert das?
Für den Time-based-One-time-Algorithmus sind drei Formeln von Bedeutung:
TOTP = HOTP(SecretKey,CurrentTime)
Diese einfache Formel legt nur fest, dass TOTP ein HOTP-Verfahren mit den beiden Parametern SecretKey und CurrentTime ist:
- SecretKey: zufällig erzeugtes Passwort, das sowohl Server als auch Client bekannt ist
- CurrentTime: aktueller Zeitpunkt in Unixzeit
Die Zeitangabe ändert sich allerdings sekündlich. Das ist nicht genug Zeit, um den Code in die Anwendung zu übertragen. Eine Sekunde später wäre das TOTP schon nicht mehr gültig: Der Server hat bereits einen neuen Hashwert erstellt. Deshalb greift man auf eine weitere Formel zurück:
CurrentTime = floorunixtime(now) – unixtime(T0/T1)
Der Parameter CurrentTime wird also definiert:
- unixtime(now): aktueller Zeitpunkt in Unixzeit
- unixtime(T0): Unixzeit zum Zeitpunkt T0, ab dem gezählt wird – also in den meisten Fällen der 1.1.1970 um Mitternacht (= 0)
- T1: Intervall, in dem das TOTP gültig sein soll – üblicherweise 30 Sekunden
- floor: Abrundungsfunktion, um den errechneten Wert auf eine ganze Zahl zu runden
Es kann theoretisch auch ein anderer Wert als 0 für T0 gewählt werden. Wichtig ist nur, dass Client und Server den gleichen Wert wählen.
Division und Abrundung erzeugen den Effekt, dass sich das Resultat intervallmäßig ändert.
Anschließend wird der erzeugte Hashwert noch benutzerfreundlich verkürzt:
Result = TOTPmod10d
Mit der Modulo-Rechnung erzeugt man eine Prüfsumme:
- mod 10: Modulo bei Divisor 10
- d: Anzahl der Stellen, die das TOTP haben soll
Man potenziert also die Basis 10 zu den Stellen, die der Code haben soll, teilt das TOTP durch diesen Wert und extrahiert dann den Rest.
TOTP-Berechnung an einem Beispiel
Nehmen wir an, man möchte ein TOTP erzeugen, das eine Gültigkeit von 30 Sekunden hat. Damit können wir nun schon die CurrentTime berechnen und erkennen so auch, wie die Gültigkeitsdauer garantiert wird. Als unixtime(now) nehmen wir 1548322860 an, den 24.01.2019 um 10:41 Uhr. Teilen wir diesen Wert durch 30, erhalten wir genau 51610762. Da es sich bereits um eine ganze Zahl handelt, gibt auch die Abrundung dieses Ergebnis aus. Setzen wir die aktuelle Zeit nun 15 Sekunden später an (also 1548322875), erhalten wir nach der Division das Ergebnis 51610762,5. Auch dies ist abgerundet 51610762. Die CurrentTime bleibt also gleich. In der folgenden Tabelle kann man sehen, dass erst nach 30 Sekunden ein anderer Wert entsteht:
unixtime(now) | unixtime(now)/30 | floor(unixtime(now)/30) |
---|---|---|
1548322857 | 51610761,9000 | 51610761 |
1548322858 | 51610761,9333 | 51610761 |
1548322859 | 51610761,9667 | 51610761 |
1548322860 | 51610762,0000 | 51610762 |
1548322861 | 51610762,0333 | 51610762 |
1548322862 | 51610762,0667 | 51610762 |
1548322863 | 51610762,1000 | 51610762 |
1548322864 | 51610762,1333 | 51610762 |
1548322865 | 51610762,1667 | 51610762 |
1548322866 | 51610762,2000 | 51610762 |
1548322867 | 51610762,2333 | 51610762 |
1548322868 | 51610762,2667 | 51610762 |
1548322869 | 51610762,3000 | 51610762 |
1548322870 | 51610762,3333 | 51610762 |
1548322871 | 51610762,3667 | 51610762 |
1548322872 | 51610762,4000 | 51610762 |
1548322873 | 51610762,4333 | 51610762 |
1548322874 | 51610762,4667 | 51610762 |
1548322875 | 51610762,5000 | 51610762 |
1548322876 | 51610762,5333 | 51610762 |
1548322877 | 51610762,5667 | 51610762 |
1548322878 | 51610762,6000 | 51610762 |
1548322879 | 51610762,6333 | 51610762 |
1548322880 | 51610762,6667 | 51610762 |
1548322881 | 51610762,7000 | 51610762 |
1548322882 | 51610762,7333 | 51610762 |
1548322883 | 51610762,7667 | 51610762 |
1548322884 | 51610762,8000 | 51610762 |
1548322885 | 51610762,8333 | 51610762 |
1548322886 | 51610762,8667 | 51610762 |
1548322887 | 51610762,9000 | 51610762 |
1548322888 | 51610762,9333 | 51610762 |
1548322889 | 51610762,9667 | 51610762 |
1548322890 | 51610763,0000 | 51610763 |
1548322891 | 51610763,0333 | 51610763 |
CurrentTime steht damit fest (51610762). Den SecretKey erzeugen wir durch einen Passwort-Generator: >cHSB_UQ#O5m;~b
HMAC mit SHA-1 formt aus Passwort und Uhrzeit einen Hashwert (in hexadezimaler Schreibweise): c0 62 37 94 dd 37 7a 3a f0 91 22 08 1f 21 6f 9b 17 4b 17 45. Dieser 160 Bit bzw. 20 Byte lange Wert wird durch sogenannte Dynamic Truncation nun auf 31 Bit verkürzt. Hierfür werden zunächst die letzten 4 Bit betrachtet, also die Zahl 0x5, die auch in dezimaler Schreibweise als 5 geschrieben wird. Dies entspricht dem Offset-Wert der Dynamic Truncation und bedeutet, dass man vom Byte mit dem Index 5 ausgehend (von links aus beginnend mit 0 zählen), vier Bytes extrahiert: 0x377a3af0. In diesem Fall beginnt der Wert bereits mit einem Bit, das auf 0 steht. Sollte dies nicht der Fall sein, ändert man entsprechend. Der 31 Bit lange Wert lautet hier also auch: 0x377a3af0 bzw. 930757360.
Um diese neunstellige Ziffernfolge nun auf sechs Stellen zu reduzieren, wendet man eine Modulo-Operation an und füllt bei Bedarf links mit Nullen auf: 930757360 mod (106) = 757360. Dies ist nun das TOTP, das 30 Sekunden lang gültig ist. In Kombination mit einem weiteren Faktor hat man so ein ziemlich sicheres Anmeldeverfahren.