TOTP in eigene Anwendung integrieren

Heute zeige ich Ihnen, wie Sie Zwei-Faktor Authentifizierung in Ihre Anwendung integrieren können. Dazu verwenden wir Time-based One-time Passwords (TOTP), das ich schon in einem vorherigen Artikel vorgestellt habe.

User Interface

Für den Benutzer sollte die Aktivierung möglichst einfach sein. Ich erstelle daher einen Dialog, der einen QR Code anzeigt. Diesen QR Code kann der Benutzer mit einem 2FA-App scannen. Ich nutze für mein Windows Phone die App Authenticator von Microsoft. Für iPhone Nutzer gibt es unter anderem die App Google Authenticator, für Android ebenfalls.

Möglicherweise ist der Benutzer nicht in der Lage, den QR Code zu scannen. Der Code sollte daher auch als Text angezeigt werden.

Im zweiten Schritt wird der Benutzer aufgefordert, den vom App generierten Code einzugeben. Zusätzlich frage ich hier noch nach dem Passwort des Benutzers. Damit verhindere ich, dass jemand anders böswillig 2FA aktiviert.

So viel zum UI.

Quellcode

Den TOTP Algorithmus habe ich bereits erklärt. Wir benötigen dafür einen geheimen Schlüssel, den wir für jeden Benutzer neu erzeugen.

private byte[] GenerateRandomKey()
{
    var secretKey = new byte[20];

    using (var generator = new RNGCryptoServiceProvider())
    {
        generator.GetBytes(secretKey);
    }

    return secretKey;
}

Um den Schlüssel mit dem Benutzer auszutauschen, müssen wir ihn Base32 encodieren. Es gibt verschiedene Base32 Algorithmen, wir benötigen einen mit dem Alphabet ABCDEFGHIJKLMNOPQRSTUVWXYZ234567, also allen 26 Grossbuchstaben sowie den Zahlen 2 bis 7.
Eine mögliche Implementierung findet man in diesem Blogartikel von Oleg Ignat.

Nun erstellen wir die URL sowie den lesbaren geheimen Schlüssel.

var secretKey = GenerateRandomKey();
var username = "xpressive.cms:michaelschuler";
var issuer = "xpressive.cms";
var secret = Base32.ToBase32String(secretKey);

var url = $"otpauth://totp/{username}?secret={secret}&issuer={issuer}";

Das Ergebnis ist wie folgt.

Secret: PUIA6PDIZRQSBSUDKCPRNVE2Q2FB7B36
URL: otpauth://totp/xpressive.cms%3amichaelschuler?secret=PUIA6PDIZRQSBSUDKCPRNVE2Q2FB7B36&issuer=xpressive.cms

Wie wir sehen können, ist das Secret auch Teil der URL. Mit dieser URL können wir nun den QR Code generieren. Für den Fall, dass der Benutzer den QR Code nicht scannen kann, zeigen wir ihn noch als Text an.

Der Benutzer scannt nun den QR Code und das App beginnt damit, alle 30 Sekunden einen neuen, 6-stelligen Code zu generieren. Um den Code zu validieren, nutzen wir den gleichen geheimen Schlüssel und generieren den Code ebenfalls.

var code = Totp(secretKey, DateTime.UtcNow);
var isValid = string.Equals(code, userEnteredCode, StringComparison.Ordinal);

Die Wahrscheinlichkeit, dass der Server die genau gleiche Zeit hat wie das Smartphone des Benutzers, ist eher gering. Es ist daher üblich, den Code des Benutzers nicht nur gegen den aktuellen Code des Servers zu prüfen, sondern auch gegen diejenigen 30 Sekunden vorher und später.

Wenn der Code übereinstimmt, speichern wir den geheimen Schlüssel ab und aktivieren 2FA für den betreffenden Benutzer. Von nun an werden wir jedes Mal, wenn sich der Benutzer anmeldet, noch nach dem 6-stelligen Code fragen.