Sichere Ahnen Prüfung mit Cryptographic Hashes

Bei der Erstellung eigner Stammbäume gibt es immer wieder den Wunsch in anderen Datenbeständen nach Familienangehörigen zu suchen. Dabei ergibt sich jedoch das Problem, dass personenbezogene Daten an andere versendet werden müssen.

Die meisten dieser Familienangehörigen sind zwar schon seit langer Zeit tot, aber manche sind vielleicht erst kürzlich verstorben oder schlimmer noch – für den Datenschutz – sie sind noch quicklebendig. Neben den Datenschutzbelangen missfällt es aber vielleicht auch den ein oder anderen per se, seine Ahnendaten herauszugeben.

Um dieses Problem zu lösen, muss also ein Weg gefunden werden, in Datenbeständen zu suchen, ohne zu verraten, wonach gesucht wird. Das klingt im ersten Moment nach einer nicht umsetzbaren Anforderung, aber glücklicherweise wurde es in einem ganz anderen Umfeld schon umgesetzt.

Bei der Verwendung von Passwörtern gibt es immer wieder das Problem, dass sie zu einfach sind und leicht erraten werden können. Mittlerweile existieren große Listen von unsicheren Passwörtern, die bei Einbrüchen in fremde Accounts genutzt werden. Spezielle Dienste bieten die Möglichkeit, dass eigene Passwort auf diesen Listen zu suchen. Natürlich schickt man dort nicht sein eigenes Passwort hin, sondern etwas anderes. Aber dafür müssen wir erst über Cryptographic Hashes reden.

Hashes oder Hash Funktionen sind eine Abbildung von einem beliebig langen Input auf einen Output mit fester Länge. Obwohl verschiedene Inputs auf den selben Output abgebildet werden können ist dies in der Praxis häufig nebensächlich, weil solche Kollisionen extrem selten sind. Cryptographic Hashes haben zusätzlich die Eigenschaft, dass es sich um Einwegfunktionen handelt, sie kollisionsresistent sind und eine Pseudozufälligkeit haben.

Bei einer Einwegfunktion ist die Berechnung des Inputs aus dem Output praktisch unmöglich. Nutzen wir also einen Cryptographic Hash, dann kann ein Angreifer nicht ohne Mühen herausfinden, welchen Input wir damit repräsentieren. Die Kollisionsresistenz bedeutet, dass es nahezu unmöglich ist, zwei Inputs zu finden, die den gleichen Output erzeugen. Die Pseudozufälligkeit sorgt dafür, dass ähnliche Inputs scheinbar willkürlich über die Menge der Outputs verteilt liegen. Ohne die Pseudozufälligkeit wäre es schwierig die Kollisionsresistenz zu gewährleisten.

Bei der Passwort Prüfung kann also ein Cryptographic Hash des eigenen Passwort an den Dienst geschickt werden und dort wird in der Liste von Cryptographic Hashes geprüft, ob der eigene dort zu finden ist. Wenn der folgende SHA256 Hash an einen Dienst geschickt wird, könnte er mir mitteilen, ob dieses Passwort unsicher ist.

8cd626bd979d05205337b47dc6a80edf6372b902e3dd1d10bc99dc3ecfd38f9e

Das teilt mir aber auch eine Google Suche mit, da für viele unsichere Passwörter diverse Hashes im Netz bereitliegen. Man kann zwar tatsächlich aus dem Hash nicht so einfach zurück rechnen, aber für alle banalen Passwörter wurden die Hashes schon erzeugt und können gesucht werden.

Als Passwort ist gonzo also nicht geeignet und außerdem kennt der Dienst in diesem Fall auch mein Passwort, weil es in seiner DB zusammen mit dem Hash gespeichert ist. Aber die Dienstanbieter haben an dieser Stelle etwas weiter gedacht. Ich übermittle nicht den gesamten Hash sondern nur die ersten 5 Zeichen. Daraufhin kann der Dienst mir alles Hashes zusenden, die mit diesen 5 Zeichen beginnen. Wenn mein Hash dabei ist, dann habe ich ein unsicheres Passwort. Wenn mein Hash nicht dabei ist, habe ich entweder ein gutes Passwort oder es ist einfach noch nicht in der Liste.

Diese Art der Überprüfung hat den Vorteil, dass der Dienst keine Ahnung hat, nach welchem Passwort ich eigentlich schaue. Zum einen weiß er nicht, ob mein Passwort in seiner Liste überhaupt enthalten ist und bei der Pseudozufälligkeit des Hashes, kann er auch keine Rückschlüsse auf das Passwort ziehen.

Ganz ähnlich kann ein Stammbaum Dienst die sichere Prüfung von Ahnen anbieten. Der Nutzer erzeugt nach einer festen Regeln seinen Hash und sendet dessen Präfix an den Dienst. In unserem Beispiel verwendet der Server SHA3-256 Hashes aus Namen, Geburtsdatum und Geburtsort.

private String getHash(Ancestor ancestor) throws NoSuchAlgorithmException {
  String content = "%s:%s:%s".formatted(ancestor.name(), ancestor.dateOfBirth(), ancestor.placeOfBirth());
  MessageDigest digest = MessageDigest.getInstance("SHA3-256");
  return new BigInteger(1, digest.digest(content.getBytes(StandardCharsets.UTF_8))).toString(16);
}

Für meinen Ahnen Johann Heinrich Albrecht Köhnsen ergibt dies den folgenden Hash.

1af4e60c16bf3e360df53816bfacdb1ac7a120a16e4018c683bacc45780bacda

An den Server wird nur die Zeichenfolge 1af4e gesendet und dort wird geprüft, ob Ahnen mit dem gegebenen Hash Präfix gespeichert sind. Gibt es keinen, dann war die Suche erfolglos. Existieren passende Hashes, dann werden diese zurück geliefert und der Nutzer kann prüfen, ob sein Hash enthalten ist.

Schreibe einen Kommentar