Passwort-Check mit Haveibeenpwned

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Passwort-Check mit Haveibeenpwned

Beitrag von Helle »

Aus Neugier habe ich mir die SHA1-Passwort-Datei von "haveibeenpwned.com" runtergeladen (s.a. c´t 5/2019) und ein Auswert-Programm (natürlich) in PB erstellt. Genutzt wird binäre Suche um auf Speed zu kommen:

Code: Alles auswählen

;"Helle" K.Helbing 21.3.2019
;binäre Passwortsuche in Datei "pwned-passwords-sha1-ordered-by-hash-v4.txt"
;von "https://haveibeenpwned.com/Passwords" die Passwort-Datei "pwned-passwords-sha1-ordered-by-hash-v4.7z" runterladen und entpacken
;s.a. c´t 5/2019
;getestet mit Windows7/64, PB 5.70 LTS (x64)

;wer Langeweile hat oder besonders ordentlich ist kann auch die Variablen sauber verwalten. Dieser Test läuft auch so... :-)
FileSeekKonst.q = 64                   ;Konstanter Offset für FileSeek
Offset_DP_HA.q = 40                    ;Abstand Doppelpunkt - Anfang HashWert

If OpenWindow(0, 0, 0, 600, 600, "Passwort-Überprüfung", #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)
  TextGadget(1, 10, 10, 230, 15, "Select das Passwort-Text-File:")
  ExplorerTreeGadget(2, 10, 30, 580, 565, "*.TXT", #PB_Explorer_NoDriveRequester)   
  SetActiveGadget(2)

  Repeat
    Event = WaitWindowEvent()
    If Event = #PB_Event_CloseWindow
      End  
    EndIf
  Until EventType() = #PB_EventType_LeftDoubleClick And GetGadgetState(2) = #PB_Explorer_File
  File$ = GetGadgetText(2)
  FreeGadget(1) : FreeGadget(2)

  If ReadFile(0, File$, #PB_Ascii)
    LenFileOri.q = Lof(0)
    Buffer.q = AllocateMemory(128, #PB_Memory_NoClear)

    Repeat                             ;für noch ein Passwort
      Adresse.q = 0

      Passwort$ = InputRequester("Passwort-Eingabe", "Bitte hier das zu suchende Passwort eingeben:", "", #PB_InputRequester_Password)

      TA = ElapsedMilliseconds()
      UseSHA1Fingerprint()
      SHA1_Uni$ = UCase(StringFingerprint(Passwort$, #PB_Cipher_SHA1, -1, #PB_UTF8))     ;in der TXT-Datei werden Großbuchstaben verwendet. Die Passwörter in der Datei wurden als UTF8 gehasht!
      SHA1$ = Space(40)
      PokeS(@SHA1$, PeekS(@SHA1_Uni$), -1, #PB_Ascii)

      ;Startwerte
      SuchQuad1.q = PeekQ(@SHA1$)      ;die 40 Byte von SHA1$ in 5 Integer-Quads kopieren. Zuerst wird nach SuchQuad1 gesucht 
      !mov rax,[v_SuchQuad1]           ;ist hier mit ASM einfacher
      !bswap rax                       ;notwendig wegen Größen-Vergleich!
      !mov [v_SuchQuad1],rax
;für neuere CPUs eine Instruktion weniger:
;!mov rax,[v_SuchQuad1]
;!movbe [v_SuchQuad1],rax
      SuchQuad2.q = PeekQ(@SHA1$ + 8)
      SuchQuad3.q = PeekQ(@SHA1$ + 16)
      SuchQuad4.q = PeekQ(@SHA1$ + 24)
      SuchQuad5.q = PeekQ(@SHA1$ + 32)

      LenFileOld = LenFileOri
      LenFile = LenFileOld
      LenFile >> 1                     ;1.Halbierung für binäre Suche
      FilePos.q = LenFile

      LenFileOld >> 1

      While LenFile > Offset_DP_HA
        FileSeek(0, FilePos - FileSeekKonst)
        Gelesen.q = ReadData(0, Buffer, 128)

        For j = 0 To Gelesen - 1
          If PeekB(Buffer + j) = $3A   ;Suche nach Doppelpunkt ":"
            If j > Offset_DP_HA
              Break 
            EndIf
          EndIf
        Next

        FindQuad1.q = PeekQ(Buffer + j - Offset_DP_HA)
        !mov rax,[v_FindQuad1]
        !bswap rax                     ;notwendig wegen Größen-Vergleich!
        !mov [v_FindQuad1],rax
        If FindQuad1 = SuchQuad1
          FindQuad2.q = PeekQ(Buffer + j - Offset_DP_HA + 8)
          FindQuad3.q = PeekQ(Buffer + j - Offset_DP_HA + 16)
          FindQuad4.q = PeekQ(Buffer + j - Offset_DP_HA + 24)
          FindQuad5.q = PeekQ(Buffer + j - Offset_DP_HA + 32)
          If FindQuad2 = SuchQuad2 And FindQuad3 = SuchQuad3 And FindQuad4 = SuchQuad4 And FindQuad5 = SuchQuad5
            TE = ElapsedMilliseconds() - TA
            Adresse = FilePos + j - FileSeekKonst - Offset_DP_HA
            Adresse$ = "Das eingegebene Passwort wurde gefunden an Adresse " + Str(Adresse) + #LFCR$ + "($" + Hex(Adresse) + ")"
            j + 1
            Wieoft$ = ""
            While PeekB(Buffer + j) <> $0D
              Wieoft$ + Chr(PeekB(Buffer + j))
              j + 1
            Wend
            Anzahl$ = "Lt. Liste wurde dieses Passwort " + Wieoft$ + "mal verwendet." 
            Warntext$ = "Ergebnis ist aber eigentlich negativ! Passwort sollte nicht verwendet werden!"
            Abfrage = MessageRequester("Ergebnis positiv!", Adresse$ + " in " + Str(TE) + " ms!" + #LFCR$ + Anzahl$ + #LFCR$ + Warntext$ + #LFCR$ + #LFCR$ + "Nochmal?", #PB_MessageRequester_YesNo)
            If Abfrage = #PB_MessageRequester_No  
              Break 2
            EndIf
            Break
          EndIf
        EndIf
        LenFile >> 1                   ;nächste Halbierung
        If FindQuad1 > SuchQuad1       ;1.Hälfte    nur Quad1 zu testen sollte ausreichen
          FilePos = LenFileOld - LenFile
         Else                          ;2.Hälfte
          FilePos = LenFileOld + LenFile
        EndIf
        LenFileOld = FilePos
      Wend

      If Adresse = 0
        Abfrage = MessageRequester("Ergebnis negativ!", "Passwort nicht gefunden!" + #LFCR$ + "Ist aber eigentlich positiv!" + #LFCR$ + #LFCR$ + "Nochmal?", #PB_MessageRequester_YesNo)
        If Abfrage = #PB_MessageRequester_No  
          Break
        EndIf
      EndIf

    ForEver                            ;noch ein Passwort 

    CloseFile(0)
    FreeMemory(Buffer)
    Else
     MessageRequester("Fehler!", "Kann Passwort-Datei nicht einlesen!")
  EndIf
EndIf
Sinn soll sein, in der Liste gefundene Passwörter als unsicher zu betrachten und nicht weiter verwenden.
Schon lustig, was alles so gefunden werden kann. Sogar "purebasic" ist 15x vertreten :mrgreen: . Gut vertreten ist z.B. auch "a....loch" (22278mal!!!); das kenne ich noch von Arbeit...

Viel Spaß!
Helle

Edit 21.03.2019: Adresse-Berechnung wieder an richtige Stelle gesetzt :D
Zuletzt geändert von Helle am 21.03.2019 12:48, insgesamt 1-mal geändert.
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8675
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Passwort-Check mit Haveibeenpwned

Beitrag von NicTheQuick »

Man kann auch einfach die API von haveibeenpwned.com benutzen. Dazu schickt man die ersten 5 Zeichen des SHA1-Hashes des eingegebenen Passworts an die Seite, erhält alle Hashes, die mit diesen 5 Zeichen beginnen und kann dann vergleichen, ob der eigene Hash dabei ist.
Hab mir dafür mal ein kleines Bash-Skript geschrieben, wen es interessiert:

Code: Alles auswählen

#!/bin/bash
pwned_check ()
{
    local pwd="$1"
    local hash=$(echo -n "$pwd" | sha1sum | cut -c -40)
    local expected=${hash#?????}
    local prefix=${hash%$expected}
    local match

    match=$(curl -s https://api.pwnedpasswords.com/range/$prefix | grep -i "^$expected:") || return 1 
    echo ${match#*:}
}

while read -p "Passwort: " pwd; do
	if [ -z "$pwd" ]; then
		echo "Cya"
		exit 0
	fi
	result="$(pwned_check "$pwd")"
	if [ -n "$result" ]; then
		echo "Occurences: $result"
	else
		echo "Not found :)"
	fi
done
Bild
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8675
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Passwort-Check mit Haveibeenpwned

Beitrag von NicTheQuick »

Da mir gerade langweilig war, hab ich den Bash-Code noch nach Purebasic übersetzt.

Code: Alles auswählen

EnableExplicit
UseSHA1Fingerprint()
InitNetwork()
Define password.s, sha1.s, *utf8, *result, allHashes.s, pos.i, posEnd.i, occ.s
Repeat
	password = InputRequester("HaveIbeenPwned?", "Zu überprüfendes Passwort:", "", #PB_InputRequester_Password)
	If password = "" : Break : EndIf
	*utf8 = UTF8(password)
	FillMemory(@password, Len(password) * SizeOf(Character))
	sha1.s = UCase(Fingerprint(*utf8, MemorySize(*utf8) - 1, #PB_Cipher_SHA1))
	FillMemory(*utf8, MemorySize(*utf8))
	FreeMemory(*utf8)
	*result = ReceiveHTTPMemory("https://api.pwnedpasswords.com/range/" + Left(sha1, 5))
	If Not *result
		MessageRequester("Fehler", "API nicht erreichbar.")
		Continue
	EndIf
	allHashes = PeekS(*result, -1, #PB_UTF8)
	FreeMemory(*result)
	pos = FindString(allHashes, Mid(sha1, 6) + ":")
	If pos = 0
		MessageRequester("HaveIbeenPwned?", "Dieses Passwort scheint sicher zu sein.")
	Else
		posEnd = FindString(allHashes, #LF$, pos)
		occ = Mid(allHashes, pos + 36, posEnd - pos - 37)
		MessageRequester("HaveIbeenPwned?", "Dieses Passwort existiert " + occ + " mal in der Datenbank!")
	EndIf
ForEver
Bild
Benutzeravatar
dige
Beiträge: 1182
Registriert: 08.09.2004 08:53

Re: Passwort-Check mit Haveibeenpwned

Beitrag von dige »

Danke Nic! :allright:
"Papa, mein Wecker funktioniert nicht! Der weckert immer zu früh."
Nino
Beiträge: 1300
Registriert: 13.05.2010 09:26
Wohnort: Berlin

Re: Passwort-Check mit Haveibeenpwned

Beitrag von Nino »

@NicTheQuick:
Vielen Dank! :allright:

Es sind da allerdings 2 "ü" kaputt gegangen. :-)
NicTheQuick hat geschrieben:

Code: Alles auswählen

	password = InputRequester("HaveIbeenPwned?", "Zu ?berpr?fendes Passwort:", "", #PB_InputRequester_Password)
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8675
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Passwort-Check mit Haveibeenpwned

Beitrag von NicTheQuick »

Nino hat geschrieben:@NicTheQuick:
Vielen Dank! :allright:

Es sind da allerdings 2 "ü" kaputt gegangen. :-)
NicTheQuick hat geschrieben:

Code: Alles auswählen

	password = InputRequester("HaveIbeenPwned?", "Zu ?berpr?fendes Passwort:", "", #PB_InputRequester_Password)
Ah, das liegt an Synergy. Keine Ahnung, was das momentan verbockt. Wenn ich was von Windows nach Linux kopiere, gehen alle Sonderzeichen flöten, umgekehrt gibt es keine Probleme.
Bild
Antworten