Einzelnen Beitrag anzeigen

Satty67

Registriert seit: 24. Feb 2007
Ort: Baden
1.566 Beiträge
 
Delphi 2007 Professional
 
#1

Passwort-Stärke ermitteln (Code und Prüflogik)

  Alt 17. Sep 2010, 23:39
Hallo,

in mein aktuelles Projekt bekommt ja einen verschlüsselten Dokumenten-Container. Für die Passwortwahl will ich dem Benutzer ein Hilfe geben, wie gut das gewählte Passwort ist. Dazu hab' ich im Internet viele Theorien gelesen und natürlich PassphraseQuality aus der Codelib ausprobiert.

Erst schien mir PassphraseQuality perfekt, wobei mir dann insbesondere bei der Passwortlänge ein merkwürdiges Verhalten aufgefallen ist. "Satty67!" wird mit 21% bewertet, während "Sattttttttttttttttttttttttttttttttttty67!" unter 5% liegt (wie kann das schlechter sein?).

Dann "TheQuickBrownFoxJumpsOverTheLazyDog" bekommt 100% während "TheWeatherIsWet,WhenTheWeatherIsWet!" nur 55% erhält. Beide Passwörter sind gleich lang, nur das Erste enthält lauter unterschiedliche Buchstaben. Allerdings denke ich, dass die Sonderzeichen höher zu bewerten sind, denn bei einer BruteForce-Attacke ist der eigeschränkte Buchstabensatz ja nicht bekannt (A-Z wir immer geprüft). D.h. es wird auch etwas zuviel Wert auf unterschiedliche Zeichen gelegt, wobei die zusätzliche Verwendung von Sonderzeichen keinen verstärkenden Einfluß hat.

Lange Rede, kurzer Sinn, ich hab' mir selber was überlegt
  • Zeichenwiederholungen werden bei der Bewertung ignoriert und als Einzelzeichen gewertet, die restlichen Zeichen aber dadurch nicht abgewertet.
  • Bei erkanntem Datum werden die Punkte nicht als wertvolle Sonderzeichen gewertet
  • Prüfen, ob der Zeichensatz breit genutzt wird (Also Ziffern, Groß/Kleinbuchstaben, Sonderzeichen). Mindestens ein Zeichen der Gruppe ist nötig, um eine evtl. Attacke zu einem erweiterten Suchbereich zu zwingen.
  • Anzahl unterschiedliche Zeichen im Verhältnis zur Länge ermitteln (nicht extrem hoch bewertet)
  • Passwörter, die nur aus Buchstaben bestehen, abwerten (vermutetes Wort einer Sprache)
  • Gesamtlänge bewerten (Länge schätze ich bis zu einer Obergrenze am wichtigsten ein)
  • Die ganzen Einzelergebnisse ins richtige Verhältnis setzen
Ergebnis der Funktion ist hier auch ein Wert zw. 0.0 und 1.0 (zur besseren Vergleichbarkeit). Später werde ich wohl gleich ganze Prozente übergeben, damit man es gleich ohne Umrechnen einer ProgressBar o.ä. übergeben kann.

Alles in allem bin ich über das Bewertungsergebnis glücklicher, allerdings kenne ich nicht die Techniken im Detail, die bei einer Attacke angewendet werden. Deshalb kann ich mich bei der Bewertung eines Passwortes auch irren!

ToDo:
  • TopTen-Passwörter abwerten, wenn das dann in ein TPasswordEdit kommt, per Property setzbar
  • Keyboard-Layout: QWERTZ und CO, was Hagen bei sich implementiert hat

Der Code wird später in einer Komponente verbaut und ist nur zu besseren Prüf-/Tesbarkeit hier als Funktion vorgestellt. Kann es auch später als Einzelklasse aufbauen, das ist fix gemacht. Nach derzeitigem Diskussionsstand ist man aber noch nicht von der Qualität des Prüfergebnisses überzeugt, weshalb es sich noch nicht lohnt da etwas zu entwerfen.
Delphi-Quellcode:
function PasswordStrength(const Password : String): Extended;
const
  LowerMultiplicator = 1.9; // Verhältnisse der 4 Zeichengruppen untereinander
  UpperMultiplicator = 2.1; // und Anteil am Gesamtergebnis
  NumericMultiplicator = 1.5; // In der Summe etwa 10 scheint ausgewogen
  SignMultiplicator = 2.5;
  DiffCharsMaxMulti = 10; // Entropische Prüfung [schwach 10 - 100 stark]
  MinPasswordLength = 4; // Ein kürzeres Passwort fällt völlig durch
  MaxPasswordLength = 32; // Ein längeres Passwort verbessert nicht mehr das Ergebnis

  // Zeichenwiederholungen (da wenig Wert) entfernen
  function RemoveRepetitions(const AString : string): String;
  var
    i : Integer;
  begin
    Result := AString;
    i := 2;
    while i <= Length(Result) do
    begin
      if Result[i] = Result[i-1] then
        Delete(Result, i, 1)
      else
        inc(i);
    end;
  end;

  // Sofern ein Datum erkannt wird, wertlose Separatoren entfernen
  function RemoveDateSeparator(const AString : string): String;
  var
    i : Integer;
    dt : TDateTime;
    DateStr : String;
  begin
    DateStr := AString;
    i := Length(DateStr);
    if (i > 0) and (AString[i] = DateSeparator) then
      Delete(DateStr, i, 1);

    if TryStrToDate(DateStr, dt) then
      Result := StringReplace(AString, DateSeparator, '', [rfReplaceAll])
    else
      Result := AString;
  end;

  // Längen-Multiplikator mit Rücksicht auf Obergrenze
  function LengthMul(const CurrentLength, MaxLength : Integer): Extended;
  begin
    if CurrentLength > MaxLength then
      Result := Math.Log2(MaxLength) * Math.Log2(MaxLength)
    else
      Result := Math.Log2(CurrentLength) * Math.Log2(CurrentLength)
  end;

  // Bewerten der im Passwort verwendeten Zeichen
  function CalculateEntropie(CleanPWLength, DiffCharsCount : Integer;
                  LowerMul, UpperMul, NumericMul, SignMul: Extended):Extended;
  begin
    Result := (DiffCharsCount * DiffCharsMaxMulti) / CleanPWLength;
    if (NumericMul + SignMul) = 0 then
      Result := Result + ((LowerMul + UpperMul) / 2)
    else
      Result := Result + LowerMul + UpperMul + NumericMul + SignMul;
  end;

  // Maximal ereichbares Ergebnis für Prozentrechnung ermitteln
  function GetBestResult: Extended;
  begin
    Result := CalculateEntropie(1, 1, LowerMultiplicator, UpperMultiplicator,
                                NumericMultiplicator, SignMultiplicator);
    Result := Result * LengthMul(MaxPasswordLength, MaxPasswordLength);
  end;

var
  i, CleanPWLength : Integer;
  CleanPassword,
  DiffChars : String;
  EntropieMul,
  LowerMul, UpperMul,
  NumericMul, SignMul : Extended;
begin
  Result := 0;
  LowerMul := 0;
  UpperMul := 0;
  NumericMul := 0;
  SignMul := 0;

  CleanPassword := Trim(Password);
  CleanPassword := RemoveDateSeparator(CleanPassword);
  CleanPassword := RemoveRepetitions(CleanPassword);

  CleanPWLength := Length(CleanPassword);
  if (CleanPWLength >= MinPasswordLength) then
  begin

    for i := 1 to Length(CleanPassword) do
    begin
      case CleanPassword[i] of
        'a'..'z': LowerMul := LowerMultiplicator;
        'A'..'Z': UpperMul := UpperMultiplicator;
        '0'..'9': NumericMul := NumericMultiplicator;
      else
        SignMul := SignMultiplicator;
      end;

      if Pos(CleanPassword[i], DiffChars) < 1 then
        DiffChars := DiffChars + CleanPassword[i];
    end;

    EntropieMul := CalculateEntropie(CleanPWLength, Length(DiffChars),
                                     LowerMul, UpperMul, NumericMul, SignMul);

    Result := EntropieMul * LengthMul(CleanPWLength, MaxPasswordlength);
    Result := Result / GetBestResult;
  end;
end;
Der Vollständigkeit halber ein Anwendungsbeispiel:
Delphi-Quellcode:
procedure TForm1.Edit1Change(Sender: TObject);
begin
  Label1.Caption := Format('Sicherheit bei annähernd %.1f%%',
                           [PasswordStrength(Edit1.Text) * 100]);
end;

Geändert von Satty67 (18. Sep 2010 um 16:39 Uhr)
  Mit Zitat antworten Zitat