Über komplexe Passwörter

Passwörter sichern den eigenen Account ab
Passwörter sichern den eigenen Account ab

Viele Anwendungen und Dienste laufen heutzutage nicht mehr lokal auf dem Rechner sondern remote irgendwo im Internet. Für die Benutzung der Tools muss man sich in der Regel mit Benutzernamen und Password authentifizieren. Um den eigenen Account zu schützen sollte das Passwort natürlich möglichst komplex sein. Es gibt verschiedene Möglichkeiten ein Passwort komplex zu gestalten. Man kann beispielsweise Zeichen aus möglichst vielen verschiedenen Zeichenklassen wählen: kleine Buchstaben, große Buchstaben, Ziffern, Sonderzeichen. Oder man nutzt ein extrem langes Passwort, zum Beispiel einen Satz wie meine kuh frisst sonntags am liebsten gummibärchen mit schokososse. Wenn der Satz vielleicht noch einen Rechtschreibfehler enthält wird das Passwort nochmal sicherer und wenn er etwas Peinliches verrät kann man sich den Satz auch gut merken :)

Ich behaupte, dass der Passwort-Satz oben wesentlich sicherer ist als das Passwort a0%Al;k/m (und verschiedene Passwort -Analysetools geben mir da Recht). Umso ärgerlicher ist es, wenn die Dienste im Internet nervige Anforderungen an ein Passwort haben und es bspw. aus kleine Buchstaben, große Buchstaben, Ziffern bestehen und mindestens 8 und maximal 20 Zeichen lang sein muss. Ich kann absolut nicht nachvollziehen, warum einige Anbieter die Passwortlänge nach oben begrenzen!? Mein sicherer Passwort-Satz würde hier jedenfalls abgelehnt, das eher unsichere Passwort aber akzeptiert werden.

Die Entropie eines Passworts

Dieser Beitrag entstand als ich gerade für ein Projekt eine Passwort-Authentifizierung implementieren sollte. Statt aber verschiedene Zeichenklassen und eine Mindestlänge zu fordern habe ich mich entschieden eine Mindestkomplexität zu verlangen. Die Komplexität messe ich dabei bezogen auf die theoretische Entropie des Passwords: Enthält das Passwort Zeichen aus vielen Zeichenklassen ist die Entropie pro Zeichen wesentlich höher, als wenn das Passwort nur aus Zahlen besteht.

Die Entropie H\Eta (griechischer Buchstabe Eta) berechne ich als H=Llog2N\Eta = L\log_2 N, wobei LL der Länge des Passworts und NN der Größe des Alphabets entspricht. Ein langes Passwort hat also eine größere Entropie als ein Kurzes; ein Passwort, das aus Zeichen unterschiedlicher Klassen besteht, hat eine größere Entropie als ein Passwort, das nur Zahlen enthält. Nach dieser Methode haben die folgenden Passwörter eine ähnliche Komplexität:

  • a0%Al;k/m (59.2647 bit Entropie)
  • 123456789012345678 (59.7947 bit Entropie)

Damit kann die Servicebetreiber:in eine Mindestentropie für gewählte Passwörter verlangen und Nutzer:innen können freier über die Wahl des Passworts entscheiden. Meine technische Umsetzung habe ich unten an diesen Artikel angehängt.

Generelle Passwort-Tips

Zum Schluss habe ich noch ein paar generelle Tips zu Passwörtern.

Benutzt verschiedene Passwörter!

Das ist glaub ich die wichtigste Regel. Passwörter gehen wirklich sehr oft verloren: Ein Hacker bricht bei einem Anbieter ein oder ein unzufriedener Administrator teilt sie die Datenbank in irgendwelchen Foren. Wenn man man überall das gleiche Passwort hat ein Angreifer dann natürlich Zugriff auf alle Accounts. Wird beispielsweise Twitter gehackt, kommen die Hacker mit den Logindaten auch an euer Facebook-Profil etc.

Es ist natürlich schwer sich für jeden Dienst ein anderes Passwort zu merken, insbesondere wenn sie auch noch kompliziert sein sollen. Daher gibt es zwei Tricks:

  • Wählt ein Passwort mit einer Regel. Beispielsweise a_0%Al;k/m, wobei ihr den Unterstrich immer mit dem ersten und letzten Buchstaben des Dienstes ersetzt, für den ihr das Passwort einsetzen wollt. Für Twitter ergäbe sich als atr0%Al;k/m als Passwort. Dieses Passwort funktioniert dann nicht bei Facebook, wo das Passwort afk0%Al;k/m lautet.
  • Nutzt einen Passwort-Manager! Die sind echt gut. Wer dem Passwort-Safe nicht so recht trauen will kann sich zusätzlich ein Prä- oder Suffix zu den Passwörtern merken. Dann wird im Passwort-Manager nur ein Teil des Passworts gespeichert (der aber schön komplex und lang sein kann) und man muss bei der Anmeldung noch ein paar weiter Zeichen eingeben. Das heißt, selbst wenn die Passwortdatenbank in falsche Hände Gerät kann ein Angreifer damit nichts anfangen: Keines der Passwörter funktioniert ohne das zusätzliche Wissen! :)

Benutzt mehrere Faktoren

Viele Dienste bieten heutzutage eine Zwei-Faktor-Authentifizierung an. In der Regel muss man zu einem Passwort dann noch einen weiteren Code ein geben,

  • den man per SMS zu geschickt bekommt,
  • den eine App ausrechnet, oder
  • der von einem Hardware-Token generiert wird.

Diese Art der Authentifizierung ist wesentlich sicherer als ein reines Passwort.

Umsetzung des Entropie-Checks

Vielleicht hilft meine Implementierung jemandem weiter?

Clientseitig im Angular-Frontend:

import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';

/**
 * A validator that checks if a string contains enough entropy.
 * It accept a validation error object to return if it encounters an error,
 * and the minimum number of bits required
 */
export function entropyValidator(error: ValidationErrors, minBits: number = 80): ValidatorFn {
  return (control: AbstractControl): { [key: string]: any } => {
    if (!control.value) {
      // if control is empty return no error
      return null;
    }

    let alphabetSize = 0;
    if (/\d/.test(control.value)) alphabetSize += 10;
    if (/[a-z]/.test(control.value)) alphabetSize += 26;
    if (/[A-Z]/.test(control.value)) alphabetSize += 26;
    if (/[^0-9a-zA-Z]/.test(control.value)) alphabetSize += 34;

    // if true, return no error (no error), else return error passed in the second parameter
    return control.value.length * Math.log2(alphabetSize) > minBits ? null : error;
  };
}

Serverseitig in der PostgreSQL Datenbank:

create or replace function XXX.assert_valid_password
(
  new_password text
) returns void as
$$
declare
  alphabet_size  integer := 0;
  min_bits integer := 80;
begin

    -- contains lower case letter
    if (select new_password ~ '[a-z]') then
        alphabet_size = alphabet_size + 26;
    end if;

    -- contains upper case letter
    if (select new_password ~ '[A-Z]') then
        alphabet_size = alphabet_size + 26;
    end if;

    -- contains digit
    if (select new_password ~ '[0-9]') then
        alphabet_size = alphabet_size + 10;
    end if;

    -- contains special char
    if (select new_password ~ '[^A-Za-z0-9]') then
        alphabet_size = alphabet_size + 34;
    end if;

    -- estimate entropy
    if length(new_password) * log (2, alphabet_size) < min_bits then
        raise exception 'Password is too weak' using errcode = 'WEAKP';
    end if;
    
end;
$$
  language plpgsql volatile;