Vertrauensvolle Speicherung von Passwort-Dateien
Speicherung im Klartext
In Betriebssystemen oder bei Diensten im Internet müssen in irgendeiner Form Informationen über die Passwörter der angemeldeten Nutzer:innen gespeichert werden. Ein erster Ansatz dazu könnte zunächst sein, Passwörter einfach im Klartext in einer einfachen Textdatei zu speichern:
Beispiel für eine Klartext-Passwortdatei passwd.txt:
mustermann:qwertz
falken:joshua
administrator:passw123
root:fmvku76%d
edward:jdjhfZhd8/6ei:dk*askd_ddk?(djsh%hdZH5:dk
Die Benutzer:in mit dem Login „mustermann“ hat also das Passwort „qwertz“ usw. Der Nachteil einer solchen Speicherung im Klartext ist natürlich, dass jede, die Lesezugriff auf die Passwortdatei bekommt, sofort die Passwörter von allen Nutzer:innen im Klartext auslesen kann.
Deshalb sollten von verantwortungsbewussten Systemadministrator:innen Passwörter von Nutzer:innen niemals im Klartext in Passwortdateien abgespeichert werden.
Speicherung der Hashwerte
Passwörter lassen sich auch so abspeichern, dass sie nicht im Klartext ausgelesen werden können. Dazu wird eine kryptographische Einwegfunktion, eine sogenannte Hashfunktion, verwendet. Mittels einer solchen Hashfunktion lassen sich Klartextpasswörter leicht und effizient zu Hashwerten verrechnen. Umgekehrt ist es aber praktisch unmöglich, aus einem vorgegebenen Hashwert das Klartextpasswort wieder effizient zurückzuberechnen. Da der Hashwert eines Passwortes praktisch einmalig sind, wird er anschaulich oft auch als dessen Fingerabdruck bezeichnet.
Das sogenannte SHA1-Hashverfahren ordnet einem beliebig langen Text einen 20 Byte langen hexadezimalen Hashwert zu. Für das Passwort des Benutzers „mustermann“ aus dem obigen Beispiel ergibt sich damit der folgende Hashwert:
Damit bekommt die gesamte Passwortdatei aus dem obigen Beispiel die folgende Form:
Beispiel für eine Hashwert-Passwortdatei passwd.txt:
mustermann:8c829ee6a1ac6ffdbcf8bc0ad72b73795fff34e8
falken:d6955d9721560531274cb8f50ff595a9bd39d66f
administrator:4de730479c592c0619802013bc9883dfbde67fea
root:277c21563ade912ffa1f183f04ed482648790fed
edward:712778715c24c68886ecf69d7191cc2acec05919
Enthält eine Passwortdatei oder eine Datenbank statt der Klartext-Passwörter nur
ihre Hashwerte, so kann eine potentielle Angreiferin mit diesen Hashwerten erst
einmal nichts anfangen, weil sich Hashwerte nicht zurückrechnen lassen.
Das gleiche gilt auch für die Systemadministrator:in des Computersystems, die Passwörter nicht im Klartext auslesen kann, obwohl sie natürlich Zugriff auf die Passwortdatei hat.
Beachte an dieser Stelle jedoch, dass die Nutzer:in beim späteren Einloggen nicht etwa den Hashwert, sondern ihr Klartext-Passwort in das Passwort-Eingabefeld eingibt. Anschließend wird der Hashwert des eingegebenen Passworts berechnet. Nur wenn dieser berechnete Hashwert mit dem gespeicherten übereinstimmt, wird der Zugang gewährt.
Anmerkung 1:
Berechnung von SHA1-Hashwerten in einer GNU/Linux-Shell:
echo -n 'qwertz' | sha1sum
Berechnung von SHA1-Hashwerten in Python:
import hashlib
hashlib.sha1("qwertz".encode('utf-8')).hexdigest()
Berechnung von SHA1-Hashwerten mittels der Suchmaschine DuckDuckGo:
Im Suchfeld zum Beispiel eingeben: sha1 qwertz
Anmerkung 2:
Aufgrund der Entwicklung immer schnellerer Rechner entspricht das SHA1-Verfahren nicht mehr den heutigen Anforderungen an ein sicheres Hashverfahren. Es wurde im obigen Beispiel lediglich zur Illustration der prinzipiellen Funktionsweise von Passwortdateien verwendet. Aktuelle Verfahren wie SHA512 erzeugen sehr viel längeren Hashwerte, so dass die Übersichtlichkeit der obigen Beispiel-Passwortdatei (für eine menschlichen Leserin) damit erschwert wäre.Aufgabe 1
Füge der obigen Hashwert-Passwortdatei eine Zeile hinzu für die Benutzerin 'Constanze' mit dem Klartextpasswort 'Kurz'.
Aufgabe 2
Beschreibe detailliert, wie der Anmeldeprozess des Benutzers „mustermann“ bei Verwendung der oben angegebenen Hashwert-Passwortdatei abläuft. Wann ist der Anmeldeprozess erfolgreich, wann schlägt er fehl?
Aufgabe 3
Entwickle ein Python-Programm zur Überprüfung von Passwörtern. Das Programm soll nach einem Benutzernamen und nach einem zugehörigen Passwort fragen. Falls das eingegebene Passwort korrekt ist, soll auf dem Bildschirm die Meldung „Passwort korrekt, Authentifizierung erfolgreich“ ausgegeben werden, andernfalls die Meldung „Passwort falsch, Authentifizierung gescheitert“. Verwende als Passwortdatei die oben angegebene Datei passwd.txt
.
Aufgabe 4
Recherchiere, was man unter dem sogenannten „Kerckhoffschen Prinzip“ versteht. Erläutere ausführlich, ob dieses Prinzip bei dem Programm aus Aufgabe 3 eingehalten wurde.
Aufgabe 5
Erweitere das Programm aus Aufgabe 3 um die folgenden Funktionen:
- einen neue Benutzerin hinzufügen
- eine vorhandenen Benutzerin löschen
- das Passwort einer Benutzerin ändern
Pepper
Werden gehashte Passwörter nach dem oben beschriebenen einfachen Hashverfahren gespeichert, so ergibt sich allerdings das Problem, dass sich die Hashwerte typischer Passwörter vorherberechnen und in sogenannten Rainbowtables speichern lassen. Wird dann zu einem späteren Zeitpunkt eine gehashte Passwortdatei erbeutet, so können die dort gespeicherten Hashwerte einfach mit den vorherberechneten verglichen werden, ohne dass dazu eine erneute zeitaufwendige Berechnung nötig wäre. Findet der Angreifer eine Übereinstimmung von Hashwerten, so hat er damit auch das zugehörige Klartextpasswort gefunden.
Um solche Angriffe auf Passwortdateien mittels Rainbowtables zu erschweren, wird ein sogenanntes Pepper eingesetzt. Dazu werden die Klartextpasswörter pw um ein hinreichend lange Zeichenfolge, das sogenannte Pepper p, ergänzt. Anschließend wird auf die konkatenierte Zeichenfolge p+pw ein Hashverfahren wie z.B. SHA1 angewandt.
Beispiel:
pw = 'qwertz'
p = '9cm9hsgkdcucz7ckdje-z7652cv_mnbsdsj_'
sha1(p+pw) = sha1('9cm9hsgkdcucz7ckdje-z7652cv_mnbsdsj_qwertz')
= 5f684e914ba9840cd0a0b2af79251c476ec5ffc2
Mit dem Pepper p = '9cm9hsgkdcucz7ckdje-z7652cv_mnbsdsj_'
bekommt die gesamte Passwortdatei aus dem obigen Beispiel damit die folgende Form. Beachte, dass für alle Passwörter das gleiche Pepper p verwendet wird.
Beispiel für eine Hashwert-Passwortdatei passwd.txt mit Pepper:
mustermann:5f684e914ba9840cd0a0b2af79251c476ec5ffc2
falken:08eb33988697de0ad395ba94290e2a324887d0ab
administrator:a1becea837841197fb847940653c66b84859a576
root:162b0d49d63b2b280e313ec7ae012ce7871b578a
edward:6d47b7c8a88bc2e633d05d8defb0b76405859044
Wird nun die Passwortdatei durch einen Angreifer erbeutet, so ist für diesen nicht erkennbar, dass ein Pepper verwendet wurde. Aufgrund der hohen Entropie der um das Pepper verlängerten Klartextpasswörter funktioniert auch ein Angriff über vorherberechnete Rainbowtables nicht mehr.
Eine Schwachstelle des Hashen von Passwörtern mit Pepper entsteht aber, sobald ein Angreifer zusätzlich zur Passwortdatei auch noch das Pepper p erbeutet. Denn dann kann er z.B. Wörterbuch- oder Bruteforceangriffe starten, indem er die zu testenden Passwörter einfach vor dem Hashen um das Pepper ergänzt und damit einen Rainbowtable für das erbeutete Pepper berechnet.
Ein Ausweg daraus bietet wiederum das Hashen der Passwörter mit einem sogenannten Salt:
Aufgabe 6
Füge der obigen Passwortdatei mit Pepper eine Zeile hinzu für die Benutzerin 'Constanze' mit dem Klartextpasswort 'Kurz'. Verwende p = '9cm9hsgkdcucz7ckdje-z7652cv_mnbsdsj_'
als Pepper.
Salt
Während beim Hashen von Passwörtern mit einem Pepper für die gesamte Passwortdatei ein festes Pepper verwendet wird, wird für Passwortdateien mit Salt für jedes Passwort ein unterschiedlicher sogenanntes Salt s verwendet.
Beispiel:
pw = 'qwertz'
s = 'dkkfjerfjc983883(7akfrjklfds8/akdjf=adfsh*'
sha1(s+pw) = sha1('dkkfjerfjc983883(7akfrjklfds8/akdjf=adfsh*qwertz')
= b1abab1a8efe6bd81df13f6f2fa3ae2aadce960d
Die Saltwerte werden dann jeweils zusammen mit dem Hashwert in der Passwortdatei gespeichert, so dass sich für das obige Beispiel dann eine Datei der folgenden Form ergibt:
Beispiel für eine Hashwert-Passwortdatei passwd.txt mit Salt:
mustermann:dkkfjerfjc983883(7akfrjklfds8/akdjf=adfsh*:b1abab1a8efe6bd81df13f6f2fa3ae2aadce960d
falken:dk38d7/%&jdjdsjd9djsu;(djja8nvmnbx:a53fba661a4e031a7de12bfc55a422d8fbfad6e2
administrator:aslkfa8e4jfsaksakfdjlalkdsfjdsa:286752d79187249ffa28d2068e460c2c12a8f093
root:ivcjcxy8737dki9cyyxvjlkj28347askf:2e4fc670daa272fac4bf7d9c67fbdadddf55878a
edward:aslkfj8vcx7j38h2983hfnc832fjc:55f2ee3ce9296cb9fbaa49581c998c26cbb0c6ce
Da für alle Passwörter unterschiedliche Saltwerte verwendet werden, müsste bei einem Rainbowtable-Angriff für jedes Passwort ein eigener kompletter Rainbowtable berechnet werden. Dies ist so aufwendig, dass ein solcher Angriff nicht effizient möglich ist.
Aufgabe 7
Füge der obigen Passwortdatei mit Salt eine Zeile hinzu für die Benutzerin 'Constanze' mit dem Klartextpasswort 'Kurz'. Verwende s='dkcuec73&445djNdZ/736!“dhjd'
als Salt.
Aufgabe 8
Zum Hashen von Passwörtern mit Salt werden heute üblicherweise Hashverfahren verwendet, die den Hashwert in mehreren Berechnungsrunden ermitteln, um die nötige Berechnungsdauer künstlich zu verlängern. Begründe, warum dieses Vorgehen sinnvoll ist.
Aufgabe 9
Recherchiere, was man unter einer sogenannten Shadow-Passwortdatei versteht. An welchem Ort im Dateisystem findet man in aktuellen GNU/Linux-Systemen die Passwortdatei und die Shadow-Passwortdatei? Recherchiere, welches Hashverfahren in einem aktuellen GNU/Linux-System in der Regel zur Speicherung von Passwortdateien verwendet wird.
Aufgabe 10
Weiter oben wurde bereits erwähnt, dass der sha-Hashingalgorithmus nur zur Veranschaulichung verwendet wurde. In Wirklichkeit wird in heutigen Linux-Systemen zum Beipiel das Programmbrcypt
verwendet, das auf dem Blowfish-Algorithmus basiert. In bcrypt
kann man eine Rundenzahl angeben, so dass der sogenannte Work Factor, also die Rechenzeit zum Hashen eines Klartext-Passwortes, bewusst erhöht werden kann. Schreibe ein Pythonprogramm, welches ein Klartextpasswort mit verschiedenen Rundenzahlen hashed. Bestimme mit deinem Programm eine praktikable Anzahl an Runden, die ein legitime Nutzer:in einerseits nicht zu lange auf das Ergebnis warten lässt, während andererseits aber die Rechenzeit für eine illegitime Angreifer:in, die eine Vielzahl an Passwörtern hashen muss, inakzeptable hoch wird.
Tipp: Verwende die Funktionen
bcrypt.gensalt
und bcrypt.hashpw
aus dem Python-Paket bcrypt
. Ein Cheat-Sheet für die Verwendunge von bcrypt findest du zum Beispiel
hieroder
hier.
Aufgabe 11 (Für Expert:innen)
Implementiere (wie in Aufgabe 3) eine Simulation für ein Login-System. Verwende nun jedoch die Funktion bcrypt
zum Hashen der Passwörter und eine sqlite
-Datenbank zum Speichern der Hashwerte.
Informationen zu sqlite findest du hier. Notiere auch, welchen Vorteil die Verwendung von brcypt gegenüber dem md5-Hashverfahren aus Aufgabe 3 hat.