… und wie misst man das?
Abgesehen von der vielleicht wichtigsten Empfehlung – Passwörter nicht mehrfach benutzen, sondern für jede Anwendung ein anderes nehmen[1] – versucht dieser Artikel, den technischen Hintergrund von Passwortsicherheit zu beleuchten. Aber nur unter mildem Einsatz von Mathematik, versprochen.
Dieser Artikel basiert auf einem (englischen) Blogposting für meine Ex-Kollegen. Daher zuerst etwas Werbung für meinen ehemaligen Arbeitgeber, der ja schließlich einen Teil der Arbeitszeit bezahlt hat:
– vielleicht die größte unbekannte Software-Firma der Welt
Es gibt zwar keine Methode, die absolute Qualität eines beliebigen Passworts zu bestimmen (sie hängt von verschiedenen Bedingungen ab: Art der Anwendung, Angriffsvektoren, etc.), es gibt aber dennoch ein paar Messmöglichkeiten.
Zum Beispiel ist es ganz unbestritten, dass qy8&7l!5yvoY0JE%6m7KkVabfG1cAeUhc
ein relativ besseres Passwort ist als, sagen wir mal, hallo
: Es ist viel länger; es verwendet Groß- und Kleinbuchstaben, Zahlen und Sonderzeichen; und vor allem ist es kein gewöhnliches Wort, sondern nur eine zufällige Folge von Zeichen.[2]
Aber Moment: Warum ist die Verwendung einer zufälligen Zeichenfolge besser als jede andere Sequenz, die sich nicht leicht erraten lässt? Und ist es besser, genauso gut oder schlechter als 1gtz!5u@wn5!0i%h$qzxq68*&whxkd5vc
? Sehen wir uns dazu an, wie diese Beispielpasswörter sich gegen einen typischen Angriff behaupten würden.
Passwörter speichern
Jede vernünftige Anwendung speichert Passwörter heutzutage nicht im Klartext in ihrer Datenbank oder in einer Datei, sondern verschlüsselt („hashed“) sie zuerst.[3]
Nur diese Hash-Werte werden gespeichert. Bei einer Login-Anfrage berechnet die Anwendung den Hash aus dem eingegebenen Passwort und überprüft, ob beide Hashes identisch sind. Die wirklichen Passwörter müssen dem Backend-System nie bekannt sein und können daher nicht (leicht) kompromittiert werden. Zum Beispiel kann selbst ein Administrator sie nicht lesen (er oder sie könnte nur versuchen, sie zu knacken).
Einschub #1: Hashes
Das Generieren eines Hash-Wertes für eine Zeichenkette (oder ein beliebiges anderes binäres Objekt, zum Beispiel eine Programmdatei) bedeutet, dass eine bestimmte mathematische Operation an der Zeichenkette ausgeführt wird. Dieser Vorgang muss mindestens folgende Bedingungen erfüllen:
- Während die Berechnung des Hash-Wertes einfach sein sollte (das heißt sie sollte innerhalb akzeptabler Zeit berechnet werden können), muss das Gegenteil – die Berechnung des ursprünglichen Wertes aus dem Hash-Wert – praktisch unmöglich sein (das heißt nicht innerhalb eines angemessenen Zeitrahmens möglich sein). Ansonsten könnte ein Angreifer einfach rückwärts rechnen und das ursprüngliche Passwort wieder gewinnen – wir hätten es gleich als Klartext speichern können.
- Zwei verschiedene Eingabewerte sollten immer zu zwei verschiedenen Hashes führen. Das heißt, der Hash-Algorithmus muss frei von „Kollisionen“ sein, ansonsten könnte ein falsches Passwort immer noch akzeptiert werden, da sein Hash mit dem richtigen kollidiert.
- Der Hash-Wert muss immer die gleiche Länge haben. Ansonsten könnte ein Angreifer die Länge des Passworts aus der Länge des Hashes erraten (
qy8&7l!5yvoY0JE%6m7KkVabfG1cAeUhc
undhallo
ergeben beide einen Hash derselben Länge, abhängig vom gewählten Algorithmus). - Klingt offensichtlich aber trotzdem: Der gleiche Eingabewert muss immer den gleichen Hash ergeben (dies gilt aber nur für allgemeine Hash-Funktionen; vgl. die Anmerkungen zu Salt unten).
Solche mathematischen Objekte werden als „Einwegfunktion“ bezeichnet. Ein eher triviales Beispiel ist die Multiplikation zweier Primzahlen, was (insbesondere für Computer) sehr einfach ist. Die Umkehrung, also Zerlegung in Primzahlen, ist es nicht (speziell nicht für große Zahlen).
Eine spezielle Teilmenge von Einwegfunktionen sind sogenannte „Falltürfunktionen“, die sich nur dadurch unterscheiden, dass man, wenn man ein Geheimnis kennt (den Schlüssel zur Falltür), auch die Umkehrung einfach berechnen kann. Im Beispiel: wenn eine der Primzahlen bekannt ist. Sie sind wichtig für die Verschlüsselung mit öffentlichen Schlüsseln (Public-Key-Infrastruktur, PKI), zum Beispiel bei TLS für HTTPS-Verbindungen.
Unten kann man selber ein bisschen mit Hashes spielen.
Passwörter angreifen
Ein Angreifer hat mehrere Möglichkeiten, um Zugriff auf ein Konto zu erhalten. Die größten Gefahren dieser Tage sind wahrscheinlich Phishing bzw. Social Engineering allgemein. Das ist nicht in erster Linie ein technisches Problem, also werde ich jetzt nicht weiter darauf eingehen.
Das erste, was einem in den Sinn kommt, ist das stupide Ausprobieren aller möglichen Kombinationen. Normalerweise ist es nicht sehr vielversprechend, solche „Brute-Force-Angriffe“ direkt gegen die Anwendung auszuführen: Während Computer das prinzipiell sehr schnell machen können, sind oft nur soundso viele fehlgeschlagene Versuche erlaubt, oder man muss länger und länger nach jedem fehlgeschlagenen Versuch warten, oder das System geht davon aus, dass man einen DoS-Angriff versucht und setzt einen auf eine Blacklist, oder ähnliches.
Allerdings brauchen Angreifer nur allzu oft gar nicht auf direktem Weg in die Anwendung eindringen, weil sie anderweitig Zugriff auf die gespeicherten Kontoinformationen erhalten haben, wie zum Beispiel die Datenbanktabelle mit Login-Name und (gehashten) Passwörtern.[4] Wie diese faszinierende und beängstigende Grafik deutlich macht, kann man fast schon davon ausgehen (es sei denn, man hat in den letzten Jahren in einer Höhle gelebt), dass zumindest eine Email-Adresse von jedem von uns irgendwann irgendwo Teil eines Datenmissbrauchs war.
Sobald Angreifer die gehashten Passwörter erbeutet haben, können sie entweder versuchen sie direkt zu knacken (zum Beispiel durch Ausprobieren aller Kombinationen oder, besser, über sogenannte „Wörterbuchangriffe“): Man lässt einen Computer die Klartexte hashen und vergleicht das Ergebnis mit der Liste der gestohlenen Hash-Werte. Eine andere Möglichkeit besteht darin, die Hashes mit einer Liste vorberechneter Hash-Werte zu vergleichen (oder mit Rainbow Tables, das heißt ausgeklügelten Listen von Hashes; sie sind über das Internet leicht zu erwerben: Hunderte von GB, die jedes mögliche Passwort bis zu einer bestimmten Länge abdecken). Im letzteren Fall muss man nur einmal in Rechenzeit investieren und kann dann jedes Passwort (bis zu einer maximalen Länge und je nach Zeichensatz) schnell knacken. Selbst einfach zu bedienende Web-Services[5] gibt es…
Glücklicherweise sind Rainbow Tables in Wirklichkeit weniger nützlich, weil wir den bösen Menschen die Suppe versalzen können.
Einschub #2: Salt
Was für normale Hash-Funktionen zwingend ist, zum Beispiel bei der Überprüfung von Datenintegrität, erweist sich bei der Speicherung von Passwörtern als Nachteil: Wenn man den Hash-Wert eines Passworts kennt, kennt man ihn für alle möglichen Konten mit dem gleichen Passwort und derselben Hash-Funktion. Um dieses Problem zu beheben, verwenden alle aktuellen Systeme eine zufällige Zahl, Salt genannt, die dem Passwort hinzugefügt („konkateniert“) wird, bevor es gehasht wird. Das Salt wird zusammen mit dem Hash-Wert in der Datenbank des Servers gespeichert.
Ein Angreifer, der nun versucht, eine Rainbow Table zu verwenden, müsste 2n-mal mehr Werte vorberechnen (für Salts von n Bit Länge). Die Anzahl der Kombinationen steigt schnell an und macht den Angriff damit unausführbar.
Bevor die Frage kommt: Es gibt einen verwandten Mechanismus, und ja, er heißt Pepper.
Passwörter sicher machen
Das hallo
-Passwort von oben ist jedenfalls in dem Moment, in dem man es zum ersten Mal eingibt, dem Untergang geweiht. Aktuelle Hardware und Cracking-Software ermöglichen es, gängige, unsichere Passwörter im Handumdrehen durchzutesten. – Und was ist nun ein sicheres Passwort?
Nun, wir wollen Folgendes erreichen:
- Brute-Force-Angriffe dürfen nicht sinnvoll durchführbar sein, das heißt das Passwort muss lang genug sein, die Anzahl der möglichen Zeichen muss groß genug sein, oder beides.
- Wörterbuchangriffe sollen nicht möglich sein, das heißt das Passwort muss „ungewöhnlich“ genug sein.
Zu Punkt 1: Die Lösung hängt natürlich von der verwendeten Hard- und Software ab, aber die Mathematik kann uns helfen: Es gibt nk verschiedene Tupel (Variationen), wobei n die Anzahl der möglichen Zeichen und k die Länge des Passworts ist. Der Gewinn, den man durch Erhöhen von n erzielt (zum Beispiel durch Wechsel von {a-z, A-Z}
auf {a-z, A-Z, 0-9}
, das heißt von n = 52 auf n = 62), ist gering im Vergleich zu ansteigendem k (letztendlich, weil jede Exponentialfunktion jedes Polynom überholt).
Wir sehen: Die Länge ist wichtig; die gewählte Zeichenmenge… nicht so sehr.[6]
Zu Punkt 2: Die beste Lösung ist eine zufällige Zeichenkette. Problem dabei: Menschen scheitern immer, wenn sie gebeten werden, sich etwas Zufälliges auszudenken. (Übrigens: Wörterbuch-Attacken berücksichtigen Substitutionen wie i
→!
, a
→@
.)
Daher werden Passwortgeneratoren dringend empfohlen. Wenn stattdessen eine „Passphrase“ gewünscht wird, die man sich leichter merken kann, wie wär’s mit Diceware?
Jedenfalls, wenn wir dann mal eine zufällige Zeichenkette haben, können wir das Konzept der „Informationsentropie“ verwenden, um die Qualität unseres Passworts zu messen.[7]
Einschub #3: Entropie
Obwohl es Überschneidungen gibt (und als gelernter Physiker ist es wirklich schwer, diese zu ignorieren – Mikrozustände! statistische Ensembles!), die fragliche Entropie befasst sich hier nicht mit der Thermodynamik. Vielmehr ist sie ein Maß für die durchschnittliche Menge an Information, die in einer Nachricht enthalten ist.
Im Grunde ist die (sogenannte Shannon-) Entropie einer Zeichenkette die Anzahl der Bits, die man zur Kodierung benötigt. Für eine gegebene Zeichenkette kann man zum Beispiel hier die Entropie berechnen. Dies misst jedoch nicht die Qualität der Zeichenkette, wenn sie als Passwort verwendet wird. Dazu müssen wir den Standpunkt des Angreifers einnehmen, der ja lediglich Annahmen über die Anzahl der Zeichen (n) und die Länge (k) treffen kann. In diesem Fall läuft es darauf hinaus:
H = k log2 n [bits]
was bedeutet, dass jedes zusätzliche Zeichen die Entropie um einen konstanten Faktor erhöht. Für {a-z, A-Z}
ist der Faktor log2 52 = 5,7…; für {a-z, A-Z, 0-9}
ist er log2 62 = 5,954…
Mit anderen Worten: Man endet immer bei ≈6 Bits pro Zeichen. Wenn man auch Sonderzeichen verwendet, ergibt sich 6,555…, also ≈ 7 Bits (kommt natürlich auf die Zahl der erlaubten Sonderzeichen an).
Eine Faustregel besagt, dass ein „sicheres“ Passwort eine Entropie von 72 Bits oder mehr haben sollte – 72 Bits ÷ 6 Bits/Zeichen = 12 Zeichen (oder mehr).
Eine Referenz findet man in den Tabellen hier. Dabei ist zu beachten, dass dies nur für zufällig ausgewählte Passwörter gilt, menschlich generierte Passwörter sind immer schwächer.
Wir können jetzt die Qualität der oben genannten Beispiele abschätzen:
qy8&7l!5yvoY0JE%6m7KkVabfG1cAeUhc
vs. 1gtz!5u@wn5!0i%h$qzxq68*&whxkd5vc
(vs. hallo
… naja, das ist ehrlicherweise nicht satisfaktionsfähig).
Die Passwörter sind je 33 Zeichen lang, also k = 33 für beide. Als ich sie auswählte, habe ich {a-z, A-Z, 0-9, !$/^%@#&*}
als Zeichensatz für das erste verwendet, aber nur {a-z, 0-9, !$/^%@#&*}
für das zweite. Daraus ergibt sich n = 71 bzw. n = 45. Also ist das Ergebnis:
H = 33 × log2 71 ≈ 203 Bits vs.
H = 33 × log2 45 ≈ 181 Bits.[8]
Es gibt zwar einen Unterschied, aber er ist eher akademisch: Das sind beides geradezu lächerlich gute Werte, und es gibt keinen wirklichen Sicherheitsgewinn mehr, verglichen mit einem Passwort von zum Beispiel 25 Zeichen. (Es sei denn, man ist für die Erstellung von Root-Zertifikaten oder so verantwortlich. In dem Fall sollte es aber hoffentlich nicht nötig sein diesen Artikel zu lesen.)
Unten habe ich einen Test eingebaut, der versucht die eingegebene Zeichenkette anhand mehr oder weniger vernünftiger Annahmen für die Entropie zu bewerten.
Man sollte sich aber nicht blind auf irgendwelche „Passwort-Stärkemesser“ verlassen, die manchmal mit Login-Seiten verbunden sind; im besten Fall können sie sagen, dass ein Passwort nicht total schlecht ist.
Eine letzte Bemerkung (auch auf die Gefahr hin zu langweilen): Wie auch immer du deine Passwörter erstellst – es ist eine gute Strategie damit zu rechnen, dass sie irgendwann einmal Opfer eines Datenverlusts werden. Wenn dies geschieht, möchtest du bestimmt, dass alle anderen Konten noch sicher sind (vor allem die wichtigen wie E-Mail, Bank, und so fort). Darum:
Verwende nicht dasselbe Passwort für mehr als ein Konto!
Titelfoto: Jason Blackeye / Unsplash
Spielwiese
Ein bisschen interaktiver Content zum Schluss, das wollen die Nutzer heute.
Hier kann man selber was hashen[9]
Hier kann man Passwörter testen[10]
Ein paar Einschränkungen, zusätzlich zum oben Erwähnten: Die Tabellen der Wortlisten und Buchstabenhäufigkeiten basieren auf englischen Gegebenheiten. Deshalb gilt hier zum Beispiel passwort
als vergleichsweise OK, im Gegensatz etwa zu password
. (Sie sind natürlich beide grauenhaft schlecht!)
Dies hat keine Auswirkungen auf Single Sign On-Szenarien (SSO), bei denen dieselben Kontoinformationen für die Authentifizierung gegen mehrere Anwendungen verwendet werden. Diese Anwendungen teilen nicht das Passwort, sondern delegieren den gesamten Authentifizierungsprozess an das SSO-System (welches dies wiederum ordentlich machen muss, sonst sind alle abhängigen Anwendungen verloren). ↩︎
Ich habe den Passwortgenerator meines Passwortmanagers benutzt, also sollte es wirklich ein einigermaßen „zufälliges“ Passwort sein. – Ob und wie Computer (also deterministische Maschinen!) Zufall überhaupt erzeugen können, ist ein interessantes Thema für sich, sprengte aber den Rahmen dieses Artikels. ↩︎
Mit anderen Worten: Wenn du herausfindest, dass ein von dir benutzter Dienst Passwörter unverschlüsselt speichert, empfehle ich dir, dem Dienstanbieter eine unfreundliche Notiz zu hinterlassen und ihn nie wieder zu nutzen. ↩︎
Immer noch eine Methode der Wahl: SQL-Injection (auch bekannt als „Bobby Tables-Attacke“). ↩︎
Ich bin mir ziemlich sicher, dass diese URL sicher ist, falls sich jemand fragt. ↩︎
Ein Brute-Force-Angriff auf einen zufälligen Schlüssel der Länge 2q (q Bits) wird im Durchschnitt nach 2q-1 Versuchen erfolgreich sein, das heißt Angreifer können damit rechnen den Schlüssel zu knacken, nachdem sie die Hälfte der möglichen Werte ausprobiert haben. ↩︎
Strenggenommen können wir die Entropie immer dann berechnen, wenn wir die Wahrscheinlichkeitsverteilung kennen. Aber das wird schnell kompliziert… ↩︎
Und H ≈ 24 Bits für
hallo
– wenn es denn zufällig wäre. ↩︎Das Beispiel vergleicht zwei Hash-Algorithmen: MD5 und SHA256 (genauer: SHA2-256). MD5 wird seit langem verwendet, ist aber als kryptographische Hash-Funktion in der Zwischenzeit sehr unsicher. Ein normaler PC kann Kollisionen in kurzer Zeit finden. Es kann weiterhin als Prüfsumme verwendet werden, um die Datenintegrität in unkritischen Situationen zu überprüfen, ebenso wie das etwas bessere SHA1. Die Klasse der SHA2-Funktionen ist immer noch in Ordnung, aber mit SHA3 existiert ein besserer Nachfolger. (Der, anders als SHA2, auch nicht von der NSA entworfen wurde. Ich mein’ ja nur…)
Darüber hinaus gibt es auch andere moderne Algorithmen, die speziell für Passwort-Hashing entwickelt wurden und auch fortgeschrittene Techniken wie Key Stretching verwenden. Sie sind absichtlich langsam, um das Knacken von Passwörtern zu aufwendig zu machen. Bei legitimen Anwendungsfällen ist dieser zusätzliche Aufwand im Vergleich zu anderen Faktoren nicht signifikant. ↩︎Die Software wurde von dieser Seite übernommen und ins Deutsche übersetzt. Sie unterliegt der GPLv3. ↩︎