2013-05-20 // Login absichern mit Hashing und Zwei-Faktor-Authentifizierung
Ein normaler Login besteht in der Regel aus einer HTML-Form und einem Server-Skript zur Validierung. Ist die Verbindung nicht zusätzlich über HTTPS abgesichert, kann ein Angreifer durch das Abhören des Netzwerkverkehrs die Zugangsdaten erlangen. Selbst wenn die Verbindung gesichert ist, gibt es andere (nicht technische) Möglichkeiten für einen Angreifer, z.B. die Eingabe der Zugangsdaten zu beobachten.
Einen HTTP Login absichern:
- Sichere Übertragung des Kennworts (per „Salted-Hash“)
- Zusätzliche Authentifizierung über ein Token (Smartphone App)
Grundgerüst
Um einen einfachen Login zu realisieren benötigt man eine HTML-Form mit Text- und Kennwortfeldern, sowie ein Server-Skript (in diesem Beispiel PHP) für die Validierung der Benutzerdaten:
- login.html
<html> <head> <title>Login</title> </head> <body> <form action="login.php" method="POST"> Benutzername: <input type="TEXT" name="username"><br> Kennwort: <input type="PASSWORD"name="password"><br> <input type="SUBMIT" name="Login" value="Login"> </form> </body> </html>
- login.php
<?php if ($_POST["username"] == "foo" && $_POST["password"] == "bar") { echo "Login ok"; else { echo "Login fehlgeschlagen"; }
Ablauf
- Ein Benutzer ruft
http://example.com/login.html
auf. - In die Eingabemaske gibt er seine Zugangsdaten ein und sendet sie ab.
- Das Server-Skript
login.php
überprüft die Benutzerdaten und gibt eine entsprechende Meldung aus.
In diesem Beispiel wird kein HTTPS verwendet und die Benutzerdaten unverschlüsselt übertragen. In nicht vertrauenswürdigen Umgebungen (Internet-Cafe, öffentliches WLAN etc.) sollte man daher auf Logins dieser Art komplett verzichten.
Ein Mitschnitt des HTTP-Request (mit Hilfe eines Sniffer) sieht so aus:
POST /login.php HTTP/1.1 Host: example.com User-Agent: Mozilla/5.0 Connection: close Content-Type: application/x-www-form-urlencoded Content-Length: 37 username=foo&password=bar&Login=Login
Hier sieht man in der letzten Zeile, dass als Benutzername foo
und als Kennwort bar
übertragen wurde.
Hashing mit SHA1
Oft wird im Zusammenhang mit hashen auch von Verschlüsselung gesprochen. Das ist schlichtweg falsch. Bei einer Verschlüsselung gibt es immer eine entsprechende Entschlüsselung. Hashen hingegen ist eine Einbahnstraße. Aus dem Ergebnis einer Hashfunktion kann man nicht auf den ursprünglichen Klartext schließen.
Mit Hilfe von clientseitigem JavaScript kann man das Kennwort vor dem Absenden hashen. Aus dem Kennwort „bar“ liefert die Hashfunktion SHA1 immer 62cdb7020ff920e5aa642c3d4066950dd1f01f4d
zurück.
Ein potentieller Angreifer würde jetzt zwar nicht mehr das Kennwort im Klartext sehen, es würde ihm aber auch nicht weiter Kopfzerbrechen bereiten. Es reicht ihm völlig, den abgefangenen Hash erneut an den Server zu senden und sich so zu authentifizieren (Replay-Angriff). Sollte ihn das Kennwort trotzdem interessieren, könnte er mit Rainbow-Tabellen oder Brute-Force arbeiten..
Hieraus ergeben sich zwei Probleme:
- der Hash eines Kennworts ist immer gleich.
- je nach Länge und Komplexität des Kennworts kann durch eine Brute-Force Attacke oder mit Rainbow-Tabellen das ursprüngliche Kennwort1) aus dem Hash ermittelt werden.
Um dies zu vermeiden, kann man dem Angreifer die Suppe „versalzen“
Salt (engl. ‚Salz‘) bezeichnet in der Kryptographie eine zufällig gewählte Zeichenfolge, die an einen gegebenen Klartext vor der Verwendung als Eingabe einer Hashfunktion angehängt wird, um die Entropie der Eingabe zu erhöhen. 2)
Nochmal zur Erinnerung: Hash = Hashfunktion(Kennwort)
.
Ein anderes Kennwort
bzw. Kennwort + Salt
ergibt folglich einen anderen Hash.
Das Server-Skript generiert zwei zufällige Zeichenkette (je ein Salt für das Kennwort und den Token), speichert diese in einer PHP-Session ab und fügt sie der Login-Form als „Hidden Value“ hinzu. Später beim Login wird dann mit JavaScript ein Hash aus dem Kennwort und dem Kennwort-Salt erzeugt. Das selbe passiert mit dem Token.
bcrypt
Die Benutzung von SHA1 ist nur beispielhaft. Mehr Sicherheit bietet bcrypt, dass im Vergleich zu SHA1 speziell für Kennwort-Hashing entwickelt wurde. Mehr Infos dazu gibt es hier. Ich habe mich im Beispiel für SHA1 entschieden, da ich keine vernünftige bcrypt Implementierung in JavaScript gefunden habe.
Zusätzlicher Authentifizierungsfaktor (Token)
Als zusätzlicher Authentifizierungsfaktor bietet sich die Benutzung eines Token in Form einer Smartphone-App an. Ähnlich dem TAN-Verfahren wird nach Eingabe einer PIN in der App eine 6-stellige Zeichenkette erzeugt. Diese muss beim Login zusätzlich zu Benutzername und Kennwort eingegeben werden. Die Zeichenkette ist wie eine TAN nur einmal gültig.
Hierfür wurde Mobile-OTP entwickelt. Zur Einrichtung muss ein gemeinsames Secret
, sowie eine PIN
definiert werden. Beides wird im Login-Skript, sowie in der App hinterlegt.
mOTP Smartphone Apps
Demo
Benutzername: | foo |
---|---|
Kennwort: | bar |
Secret: | topsecret123456! |
PIN: | 1234 |
URL: | http://www.heiko-barth.de/stuff/login-demo/ |
Download
Leave a comment…
- E-Mail address will not be published.
- Formatting:
//italic// __underlined__
**bold**''preformatted''
- Links:
[[http://example.com]]
[[http://example.com|Link Text]] - Quotation:
> This is a quote. Don't forget the space in front of the text: "> "
- Code:
<code>This is unspecific source code</code>
<code [lang]>This is specifc [lang] code</code>
<code php><?php echo 'example'; ?></code>
Available: html, css, javascript, bash, cpp, … - Lists:
Indent your text by two spaces and use a * for
each unordered list item or a - for ordered ones.