Zwei-Faktor-Authentifizierung gilt als teuer und exotisch. Doch mit Googles Authenticator existiert eine flexible und quelloffene Lösung, die sogar zwei Kommunikationskanäle nutzt. Über ein Android-Telefon und ohne Verbindung zum Datenkraken Google lassen sich so Linux-Server und -Dienste absichern.
Wissen und Besitz gelten als hervorragendes Paar, wenn es darum geht, sichere Authentifizierung zu bewerkstelligen. Weder der erfolgreiche Brute-Force-Angriff auf ein Passwort noch der Diebstahl des Token allein ermöglichen das unbefugte Eindringen ins System. Gelangt dann ein One-Time-Passwort auch noch über einen getrennten Kanal zum Anwender, steht einem sicheren Login nichts im Wege.
Google und Onlinebanking
Der Internetriese Google zeigt derzeit mit einer Smartphone-App und einem PAM-Modul, wie das funktionieren kann. Weil Letzteres als RFC standardisiert ist und sich nach dem flexiblen Standard der Pluggable Authentication Modules (PAM) richtet, können Admins ohne großen Aufwand einzelnen Usern und Diensten unterschiedliche Authentifizierungsmethoden verordnen.
Geldhäuser nutzen seit den Anfängen des Onlinebankings ähnliche Systeme, lange Jahre mit Hilfe von papierenen TAN-Listen, mittlerweile überwiegend über dynamisch per Smartcard erzeugte TANs oder mit per SMS versandten One-Time-TANs. Für die Fernwartung gibt es Hardware-Tokens, die eine von fest verbauter Kryptohardware erzeugte Zahlenfolge zum Abtippen präsentieren oder sich über USB als Tastatur ausgeben, auf der der Anwender sein Geheimnis direkt eingibt, oder Zertifikate und Keys für den Zugriff sicher verwahren [1].
TOTP und HOTP
Experten unterscheiden grob zwischen Systemen auf TOTP- und auf HOTP-Basis. HOTP steht für HMAC-based OTP (One-time Password) und nutzt einen in RFC 4226 [2] spezifizierten Algorithmus auf Basis eines Hashwerts, der sich bei jeder Benutzung ändert. Der Algorithmus führt einen Zähler mit, der bei Benutzer und Dienst stets auf dem aktuellen Stand gehalten sein will. TOTP dagegen steht für Time-based OTP und befindet sich derzeit noch in der Spezifizierungsphase [3]. Bei diesem Algorithmus sorgt kein Zähler dafür, dass sich der Wert ändert, sondern schlicht die Zeit. Diese Abhängigkeit von der aktuellen Uhrzeit macht das OTP vergänglich, es ist nur für eine kurze Zeitspanne nutzbar.
Initialisierung mit QR-Code
Auch die Anmeldung bei Google erlaubt es, das Konto mit einer Zwei-Faktor-Authentifikation abzusichern [4]. Dabei hat der Benutzer die Wahl zwischen dem Empfang einer SMS mit der TAN oder dem Einsatz einer Smartphone-App, die Google über die Webseite für Android, I-OS und Blackberry bereitstellt.
Nach dem Scan eines QR-Codes auf der Webseite erhält die Anwendung einen Initialisierungswert und kann ab sofort aktuelle TANs zum Login berechnen. Der Quelltext [5] unterliegt der Apache License 2.0 und steht frei zum Download. Die App unterscheidet den Google Authenticator auch im Wesentlichen von Projekten wie Lin OTP [6], denen kein zweiter Kommunikationskanal für die Authentifizierung zur Verfügung steht.
Weil sich im Ordner »libpam« des Mercurial-Repository auch die Quellen eines PAM-Moduls finden, lassen sich nahezu beliebige Linux-Server-Dienste absichern, auf Wunsch für einzelne User und dank PAM mit verschiedenen Methoden.
Mathematik
Der mathematische Algorithmus zum Berechnen des HOTP-Werts ist im RFC offen dokumentiert und basiert auf:
Diese Formel addiert binär zum Schlüssel K Byte-weise 0x36 und verkettet ihn mit dem Zähler C. Aus dem Ergebnis berechnet sich der SHA1-Hash, der an die Byte-weise Addition des Schlüssels mit dem Wert 0x5c angehängt ist und von dem dann abermals der SHA1-Hash berechnet wird.
Dieser String wäre eigentlich schon ein geeigneter Wert, das Abtippen durch den Benutzer erweist sich aber als zu langwierig. Daher wählt die Truncate-Funktion 4 Bytes aus, aus denen per Modulo-Division durch 106 ein rein numerischer Wert entsteht, der auch auf Smartphones leicht einzugeben ist.
Obwohl der Algorithmus komplex erscheint, kann ihn selbst einfache Hardware leicht bewerkstelligen. Alle Berechnungen sowohl für den Client auf dem Token als auch für den Server sind offline möglich, eine Verbindung zu einem Authentifizierungsserver ist nicht nötig.
Ob dieses Vorgehen angesichts der Bedrohungen und der vergleichsweise schlechten Administrierbarkeit moderner Smartphones [7] in der Praxis die Sicherheit erhöht, muss jeder Admin für sich entscheiden. Für die Vertrauenswürdigkeit des Endgeräts ist in fast allen Fällen ohnehin der Benutzer zuständig, es entzieht sich immer noch dem Einfluss des Admin. Erfreulich für Datenschutz-sensible Anwender ist jedoch, dass sowohl PAM-Modul als auch Smartphone-Applikation (Abbildung 1) komplett ohne Onlinekontakt zu Google auskommen.

Abbildung 1: Die Smartphone-Applikation von Google präsentiert sich aufgeräumt und zeigt oben eine TOTP-Ziffer, unten das HOTP-Eingabefeld.
Auf dem eigenen Server
Wer den Google Authenticator auf dem eigenen Server verwenden will (Abbildung 2), muss zuerst das PAM-Modul kompilieren. Den Quellcode hat er mit »hg clone https://code.google.com/p/google-authenticator/« rasch geklont. Jetzt finden sich im Ordner »libpam« die Sourcen des Moduls. Fürs erfolgreiche Kompilieren bedarf es des Distributionspakets »libpam« mit seinen Headern. Das optionale Paket »libqrencode« erleichtert die Einrichtung der Smartphone-Applikation. Der Quellcode ist recht kompakt und daher zügig mit einem einfachen »make« übersetzt.

Abbildung 2: Eine Anmeldung an der SSH mit Googles Authenticator. Nach der Eingabe des Kennworts fordert der Server den Benutzer zur Eingabe des Verification-Codes auf. Beim Login mit einem Private Key erfolgt die Abfrage des Verification-Key direkt.
Weil die Authentifizierung auch auf der aktuellen Zeit basiert, sollten sowohl Server als auch Smartphone die Uhrzeit aus dem Netz beziehen, über NTP oder aus dem Mobilfunknetz.
Makefile-Anpassungen für Ubuntu
Auf Ubuntu-Systemen muss der Admin eventuell noch das Makefile anpassen. Es benötigt meist noch ein »-ldl« als zusätzliche Option in der Zeile:
DEF_LDFLAGS := $(shell [ `uname` = SunOS ] &&echo ' -mimpure-text') $$ $(LDFLAGS) -ldl
Sind alle Abhängigkeiten erfüllt, sollte das Übersetzen sauber durchlaufen und den Weg für die Installation mittels »make install« frei machen, die das PAM-Modul »pam_google_authenticator.so« und die ausführbare Datei »google-authenticator« an ihren Bestimmungsort kopiert.
Deren Pfade sind leider nicht immer korrekt, bei Ubuntu Server (32 Bit) muss der Administrator danach beispielsweise noch manuell einen entsprechenden Symlink mit »ln -s /usr/lib/pam_google_authenticator.so /lib/i386-linux-gnu/security/« setzen.
Google Authenticator zeigt den QR-Code am Terminal
Das Programm Google Authenticator übernimmt jetzt die Rolle des Konfigurationstools, das dem Benutzer die komplexe Einrichtung und Erzeugung seines Schlüssels und der Parameter abnimmt. Jeder Benutzer muss vor dem Aktivieren einmal »google-authenticator« aufrufen, weil es den Schlüssel erzeugt, den es dem User Base32-kodiert anzeigt und den dieser in die Google-Authenticator-App auf dem Smartphone eingibt.
Ist das Modul mit der QR-Bibliothek kompiliert, dann wird es (auch in textbasierten Terminals, Abbildung 3) einen QR-Code generieren, den der Anwender mit der Smartphone-App abfotografieren kann. Außerdem zeigt der Authenticator noch fünf Notfallcodes an, die bei Verlust des Smartphones dafür sorgen, dass der Benutzer sich anmelden kann, um einen neuen Key zu erzeugen.

Abbildung 3: Vor der ersten Anmeldung muss ein Anwender das Programm Google Authenticator einmal starten und den QR-Code einscannen. Ab dann ermöglichen die von der Android-App erzeugten Ziffern das Login.
Um den SSH-Daemon nach der Eingabe des Kennworts noch das One-Time-Passwort abfragen zu lassen, muss der Admin die PAM-Konfiguration in der Datei »/etc/pam.d/sshd« anpassen. Je nachdem, ob er vor oder nach dem Benutzerkennwort das One-Time-Passwort verlangen will, muss er die Zeile »auth required pam_google_authenticator.so« vor oder hinter der Standardzeile einfügen, bei Ubuntu beispielsweise »@include common-account« .
SSH-Server konfigurieren
Den Open-SSH-Server weist eine Änderung in der »/etc/ssh/sshd_config« an, Challenge-Response-Authentifikation zu erlauben. Dazu ändert der Admin einfach den Wert von »ChallengeResponseAuthentication« von »no« auf »yes« .
Jetzt ist die Zwei-Faktor-Authentifikation aktiviert. Ab diesem Moment sollte sich der Tester, der die Authentifikation per SSH auf einem entfernten Rechner konfiguriert, immer eine Rootshell zur Sicherung offen halten und die Anmeldung nur an einem zweiten Terminal testen. Anderenfalls läuft er Gefahr, sich dauerhaft aus seinem Server auszusperren. Wenn alles richtig eingerichtet ist, fragt die SSH nach dem Kennwort nun den Verification-Code ab und gibt nach der korrekten Eingabe die Shell frei.
Ist dies trotz korrektem Code nicht der Fall, findet der Admin in den SSH- oder Auth-Logs, etwa in »/var/log/auth.log« , nützliche Hinweise auf den Fehler. Eine häufige Ursache ist, dass ein Anwender »google-authenticator« noch nicht oder nicht richtig aufgerufen hat. Ebenfalls hilfreich kann es sein, mit der richtigen PAM-Konfiguration den Einsatz auf wenige User zu beschränken. Alternativ liegt unter [8] ein Patch bereit, das vor allem in der Migrationsphase dienlich ist: Es sorgt dafür, dass das PAM-Modul den Verification-Code nur abfragt, wenn eine Konfigurationsdatei namens ».google-authenticator« im Homeverzeichnis des Benutzers vorhanden ist.
Leider funktioniert die Anmeldung mit Public Key und Zwei-Faktor-Authentifizierung nur, wenn sich der Benutzer mit einem Kennwort anmeldet.
Kniffliges Zusammenspiel mit Public Keys
Beim Anmelden über den öffentlichen Schlüssel übergeht der Open-SSH-Daemon die PAM-Konfiguration komplett und gibt die Shell ohne Abfrage des zusätzlichen Codes frei. Dies ist jedoch kein Fehler, sondern liegt daran, dass die Public-Key-Authentifizierung per Definition das eigentliche Standardverfahren ist. Durch die Absicherung der Client-seitigen privaten Schlüssel mit einer Passphrase ist dieses Verfahren bereits sehr sicher, allerdings ist es nicht möglich, diese Verschlüsselung für den Client zu erzwingen. Auch hier kann die Zwei-Faktor-Authentifizierung hilfreich sein.
Um auch bei der Anmeldung mit dem Public Key den Verification-Code abfragen zu können, bedienen sich Admins eines Tricks: Sie zwingen die SSH dazu, statt der Shell des Benutzers einen Gatekeeper zu starten, der wiederum eine PAM-Abfrage startet. Nach erfolgreichem Login startet der Gatekeeper die eigentliche Shell, sonst beendet er die Sitzung.
Gatekeeper
Ein solcher Gatekeeper findet sich unter [9], doch benötigt er für den Einsatz mit dem PAM-Modul von Google noch ein paar Modifikationen. Eine angepasste Version findet sich im Git-Repository [10], auf der virtuellen Ubuntu-Server-Maschine der DELUG-DVD ist sie für einen User (»gatekeeper« ) beispielhaft eingerichtet. Das Gatekeeper-Skript ruft das Programm »pam_proxy« auf, das mit dem PAM-Backend kommuniziert.
DELUG-DVD
Auf der DELUG-DVD dieses Magazins finden Admins ein virtuelles Image eines Ubuntu-Servers, in das sie sich mit Hilfe der Google-Authenticator-App als Anwender »gatekeeper« einloggen. Dazu nutzen sie den SSH-Key von der DELUG-DVD oder das Passwort »linux-magazin« und den QR-Code aus Abbildung 3.
Übersetzen
Die Datei »pam_proxy.c« aus Listing 1 lässt sich einfach mit »gcc -o pam_proxy pam_proxy.c -lpam -lpam_misc« übersetzen. Das Kompilat »pam_proxy« und die »ssh_gatekeeper.sh« sollte der Admin nach »/usr/sbin« oder »/usr/local/sbin« kopieren, wo es für Root verfügbar ist. Auch für »pam_proxy« muss in »/etc/pam.d« eine funktionierende Konfiguration vorliegen. Im einfachsten Fall enthält sie nur die Zeile »auth required pam_google_authenticator.so« .
Listing 1
pam_proxy.c
01 /*
02 This script was altered to work with an SSH-Gatekeeper by
03 Philipp Neuhaus. The original program check_user.c was
04 contributed by Shane Watts.
05 [Modifications by AGM and kukuk]
06 */
07
08 #include <security/pam_appl.h>
09 #include <security/pam_misc.h>
10 #include <stdio.h>
11
12 static struct pam_conv conv = {
13 misc_conv,
14 NULL
15 };
16
17 int main(int argc, char *argv[])
18 {
19 pam_handle_t *pamh=NULL;
20 int retval;
21 const char *user="nobody";
22 if(argc == 2) {
23 user = argv[1];
24 }
25 if(argc > 2) {
26 fprintf(stderr, "Usage: pam_proxy [username]\n");
27 exit(1);
28 }
29
30 retval = pam_start("pam_proxy", user, &conv, &pamh);
31 if (retval == PAM_SUCCESS)
32 retval = pam_authenticate(pamh, 0); /* is user really user? */
33 if (retval == PAM_SUCCESS)
34 retval = pam_acct_mgmt(pamh, 0); /* permitted access? */
35
36 /* This is where we have been authorized or not. */
37
38 if (retval == PAM_SUCCESS) {
39 fprintf(stdout, "Authenticated\n");
40 } else {
41 fprintf(stdout, "Not Authenticated\n");
42 }
43 if (pam_end(pamh,retval) != PAM_SUCCESS) { /* close Linux-PAM */
44 pamh = NULL;
45 fprintf(stderr, "check_user: failed to release authenticator\n");
46 exit(1);
47 }
48
49 return ( retval == PAM_SUCCESS ? 0:1 ); /* indicate success */
50 }
Mit den richtigen PAM-Einstellungen kann ein Benutzer »pam_proxy« an der Konsole aufrufen und nach der Aufforderung das aktuelle TOTP eingeben. Ist die Konfiguration korrekt, gibt der Rechner »Authenticated« aus, bei falscher Konfiguration oder falschem TOTP ein wenig überraschendes »Not authenticated« .
Für die Zusammenarbeit des Gatekeeper mit Diensten wie der SSH gibt es zwei Möglichkeiten: Eine wäre es, den SSH-Gatekeeper als »ForceCommand« in der »sshd_config« anzugeben. Dann ist jeder Benutzer – systemweit – dazu gezwungen, bei der Anmeldung TOTPs anzugeben. Sollen sich auch Backup- oder andere Skripte von anderen Rechnern automatisiert anmelden können, ist die Pam-Konfiguration in »pam_ proxy« so zu ändern, dass sie diese Benutzer von der erzwungenen erweiterten Authentifikation ausnimmt.
Alternativ lässt sich der Zwang zur Zwei-Faktor-Authentifizierung auch noch in einer benutzerspezifischen »authorized_keys« -Datei konfigurieren.
Ausnahmen
Um dies zu erreichen, platziert der Administrator in »~/.ssh/authorized_keys« des gewünschten Benutzers vor dem SSH-Pub-Key das auszuführende Kommando »COMMAND=”/usr/local/bin/ssh_gatekeeper.sh” ssh-rsa AAAAB3 […]« . Das sorgt dafür, dass der Daemon statt der eigentlichen Shell des Benutzers zuerst das Gatekeeper-Skript startet. Ist die PAM-Authentifizierung erfolgreich, startet die Shell, andernfalls kappt der SSH-Server die Verbindung. Listing 2 zeigt das ganze Skript.
Listing 2
ssh_gatekeeper.sh
01 #/bin/bash
02 ## Calomel.org ssh_gatekeeper.sh
03 #
04 ## This script is run by the ForceCommand directive in the sshd_config.
05 ## It expects the user SSH'ing to the box to answer the $QUERY question
06 ## before being allowed a shell. Permissions of this script should be
07 ## owned by root and executable by all other users (chmod 755)". Rsync,
08 ## sftp and sshfs are allowed through, but scp is denied.
09
10 ## Disconnect clients who try to quit the script (Ctrl-c)
11 trap jail INT
12 jail()
13 {
14 kill -9 $PPID
15 exit 0
16 }
17
18 ## Allow SSH. Clients can ssh to the box and then answer the $QUERY question.
19 if [ -z "$SSH_ORIGINAL_COMMAND" ];
20 then
21 ## This is the question the client needs to know the answer to. Here we
22 ## are asking for the current minute, day of the month and hour
23 ## (24-hour time). If the date is "Mon Jan 10 13:25:00 EST 2020"
24 ## then the answer is "251013"
25 QUERY=`date +%M%d%H`
26
27 ### The welcome message. This can be helpful or completely arbitrary
28 ### depending on your user. Here we use a random quote as we are
29 ### expecting the user to already know the question.
30 echo ""
31 echo " All truths are easy to understand once they are"
32 echo " discovered; the point is to discover them."
33 echo " -Galileo Galilei"
34 echo ""
35
36 ### The Decision
37 ### If the answer is correct give the user their shell.
38 ### If the answer is wrong, log the attempt and kill the connection.
39 pam=`pam_proxy $USER`
40 if [ $? -eq 0 ];
41 then
42 echo 'Authenticated'
43 $SHELL -l
44 exit 0
45 else
46 logger "ssh_gatekeeper $USER login failed from $SSH_CLIENT"
47 echo 'Authentication falied'
48 kill -9 $PPID
49 exit 0
50 fi
51 fi
52
53 ## Allow RSYNC. Rsync can not be used with the question above.
54 ## We need to let the command though so our shell environment is clean.
55 if [ `echo $SSH_ORIGINAL_COMMAND | awk '{print $1}'` = rsync ];
56 then
57 $SHELL -c "$SSH_ORIGINAL_COMMAND"
58 exit 0
59 fi
60
61 ## Allow sftp and sshfs. Make sure the path to your sftp-server binary
62 ## is correctly expressed below. We need to let the command though so
63 ## our shell environment is clean.
64 if [ `echo $SSH_ORIGINAL_COMMAND | awk '{print $1}'` = "/usr/lib/openssh/sftp-server" ];
65 then
66 $SHELL -c "$SSH_ORIGINAL_COMMAND"
67 exit 0
68 fi
69
70 ## Default deny. This is the last command to catch all other command
71 ## input. If the client tries to use anything other than ssh or rsync
72 ## the connection is dropped. SCP is denied.
73 kill -9 $PPID
74 exit 0
Neben der Anmeldung an der Shell sind aber auch andere Anwendungen besonders schützenswert. Dazu zählen zum Beispiel Fernwartungswerkzeuge wie PHP My Admin, aber auch Benutzerkonten bei Foren oder anderen Webanwendungen. Dank der Standardisierung in zwei RFCs existieren mehrere Bibliotheken, die das ermöglichen.
Für Java liefert das RFC den nötigen Code als Referenzimplementation bereits mit. Dabei ist darauf zu achten, dass die Smartphone-Applikation von Google den Key als Base32-kodierten String erwartet, die Java-Bibliothek ihn aber als Hex-String verarbeitet. Auch für PHP gibt es bereits eine funktionierende Implementierung.
Ein richtiger Schritt
Obwohl alle Grundlagen vollständig frei verfügbar sind, nutzen bisher nur wenige Admins die Möglichkeiten einer erweiterten Authentifizierung. Komplexität und Preismodelle der kommerziellen Produkte schrecken wahrscheinlich viele von ihnen noch ab. Für das Unix-Umfeld hat Google mit seinem flexiblen und standardkonformen PAM-Modul und den zugehörigen Apps aber schon einen wichtigen ersten Schritt in die richtige Richtung getan.
Infos
- Felix Kronlage, Markus Feilner, “Magisches Leuchten”: Linux-Magazin 12/10, S. 40
- RFC 4226: http://tools.ietf.org/html/rfc4226
- Draft zum TOTP-Algorithmus:http://tools.ietf.org/id/draft-mraihi-totp-timebased-06.txt
- Google-Hilfe: http://www.google.com/support/accounts/bin/static.py?page=guide.cs&guide=1056283&topic=1056284
- Google Code Repository: http://code.google.com/p/google-authenticator/
- Michael Kromer, “Doppelt gesichert”: Linux-Magazin 02/10, S. 68
- Markus Feilner, “Schwächstes Glied”: Linux-Magazin 10/11, S. 54
- Patch fürs PAM-Modul: http://code.google.com/p/google-authenticator/issues/detail?id=32
- SSH-Gatekeeper-Skript: https://calomel.org/openssh.html
- GIT-Repository mit dem modifizierten Gatekeeper- und PAM-Skript: https://github.com/TeGuy/2-Factor-Auth







