it-swarm.com.de

Warum das Improvisieren Ihrer eigenen Hash-Funktion aus vorhandenen Hash-Funktionen so schlecht ist

Ich fürchte, ich werde Tomaten nach mir werfen lassen, weil ich diese alte Frage gestellt habe, aber jetzt geht es los.

Nachdem ich gelesen habe, dass es gefährlich ist, aus vorhandenen Hashing-Funktionen einen eigenen Passwort-Hash zu erstellen über und über wieder verstehe ich die Logik immer noch nicht. Hier sind einige Beispiele:

  • md5(md5(salt) + bcrypt(password))
  • scrypt(bcrypt(password + salt))
  • sha1(md5(scrypt(password + md5(salt))))

Die typischen Argumente dagegen lauten wie folgt:

Du bist kein Kryptograf! Sie haben keine Ahnung, ob diese Hashes sicherer sind. Überlassen Sie es den Experten, die wissen, was sie tun. Diese bieten keine zusätzliche Sicherheit.

Zugegeben, sie verbessern die Funktion als Hash nicht (dh erschweren das Umkehren oder Auffinden von Kollisionen usw.), aber sicher sicher sie schaffen es nicht schlimmer als Hash? Wenn sie dies tun würden, könnten Hacker Standard-Hash-Passwörter erneut hashen in diese verrückten Hashes, wie sie es für richtig halten und den Hash schwächen? Ich kaufe es nicht.

Zweites Argument:

Kerckoffs Prinzip : Ein Kryptosystem sollte sicher sein, auch wenn alles über das System bekannt ist.

Einverstanden. Dies ist im Grunde die Motivation, Ihre Passwörter überhaupt nicht als Klartext zu speichern. Aber wenn meine Antwort auf die erste Kritik zutrifft, fungieren diese verrückten Hashes immer noch als sichere Hashes, und unser System bricht Kerckoffs Prinzip nicht mehr als es würde mit einem Standard-Hash.

Hier sind zwei mögliche (und meines Erachtens lohnende) Vorteile der Verwendung eines "verrückten" Hash gegenüber einem normalen Hash:

  1. Sicher, Ihr System sollte sicher sein, wenn der Angreifer über den Quellcode verfügt, aber es ist sehr wahrscheinlich, dass Ihr Angreifer keinen Zugriff auf Ihren Quellcode hat wahrscheinlich kann Ihren verrückten Hash nicht erraten, was einen Versuch einer rohen Gewalt unmöglich macht.
  2. (Dies ist die eigentliche Motivation hinter mir, diese Frage zu stellen.) BCrypt gilt als sicher, schwer für die CPU und die GPU (großartig), aber kann mit spezieller Hardware sehr schnell sein . SCrypt soll auf CPUs, GPUs und derzeit verfügbaren spezialisierten Hardwards schwer zu bruteforce sein, ist jedoch jünger und wird von der kryptografischen Community aufgrund der mangelnden Belichtung nicht so sehr wie BCrypt als vertrauenswürdig eingestuft. Aber bekommt der Hash BCrypt(SCrypt(password + salt)) nicht das Beste aus beiden Welten?

Ich weiß zu schätzen, dass die Leidenschaft/Wut, die hinter den meisten Beschimpfungen gegen diese selbst gebrauten Hashes steckt, auf dem mangelnden Wissen des durchschnittlichen Programmierers darüber beruht, was einen guten Hash ausmacht, und auf der Sorge, dass die Förderung dieser Art von verrücktem Hashing unweigerlich schwach und nutzlos wird Hashes, die in den Produktionscode gelangen. Aber Wenn der verrückte Hash sorgfältig aus soliden und vertrauenswürdigen Hashes erstellt wird, sind die Sicherheitsgewinne dann nicht sehr wertvoll und real?


Aktualisieren

Ich habe ein paar gute Antworten darauf bekommen, danke. Was ich in meinen Annahmen zu übersehen schien, war, dass, obwohl das Kombinieren von Hashes es nicht einfacher machen kann, das Original Passwort zu knacken und daher die konstituierenden Hashes zu knacken, die Kombination von zwei oder mehr sicheren Hashes kann - zumindest im Prinzip - aufgrund der nicht untersuchten und komplexen Wechselwirkungen zwischen ihnen schwächer sein als jeder seiner inneren Hashes. Das heißt, es könnte möglich sein, einige Zeichenfolgen zu finden, die über den verrückten Hash hinausgegangen sind, ohne unbedingt die Hashes zu brechen, aus denen er besteht.

80
George Powell

Die Tatsache, dass Sie diese Frage stellen müssen, ist die Antwort selbst - Sie wissen nicht , was beim Stapeln dieser Grundelemente falsch ist, und daher kann unmöglich wissen, welche Vorteile oder Schwächen es gibt.

Lassen Sie uns jedes der von Ihnen angegebenen Beispiele analysieren:

md5(md5(salt) + bcrypt(password))

Ich kann hier einige Probleme sehen. Das erste ist, dass Sie das Salz MD5'ing. Welchen Nutzen bringt dies? Keiner. Dies erhöht die Komplexität, und das Salt soll lediglich eindeutig sein, um Kennwortkollisionen und Angriffe vor der Berechnung (z. B. Rainbow Table) zu verhindern. Die Verwendung von MD5 macht hier keinen Sinn und könnte das Schema tatsächlich schwächen, da MD5 triviale Kollisionen kennt. Daher besteht eine geringe Wahrscheinlichkeit, dass die Einführung von MD5 hier bedeutet, dass zwei einzigartige Salze denselben MD5-Hash produzieren, was zu einem effektiv duplizierten Salz führt. Das ist schlecht.

Als nächstes verwenden Sie bcrypt für das Passwort. OK. Nun, die meisten bcrypt-Implementierungen erfordern intern ein Salt, daher ist dies technisch bereits ungültig. Nehmen wir an, Sie wissen das und wollten bcrypt(md5(salt), password) sagen. Dieser Teil fällt immer noch auf die Schwäche, die ich oben beschrieben habe, aber es ist nicht zu schäbig - entfernen Sie das MD5 und es ist eine Standardverwendung von bcrypt.

Schließlich haben Sie MD5 die ganze Sache. Warum tust du das? Was ist der Zweck? Welchen Nutzen bringt es? Soweit ich sehen kann, gibt es überhaupt keinen Nutzen. Auf der nachteiligen Seite erhöht es die Komplexität. Da die meisten bcrypt-Implementierungen die Notation $2a$rounds$salt$hash Verwenden, müssen Sie Code schreiben, um diesen zu analysieren, damit Sie den Hash-Teil extrahieren und den Rest separat speichern können. Sie benötigen außerdem eine MD5-Implementierung, die nicht erforderlich war.

In Bezug auf den Code-Footprint für potenzielle Angriffsvektoren haben Sie sich von einer einfachen bcrypt-Implementierung zu einer bcrypt-Implementierung mit benutzerdefiniertem Parsing-Code und MD5-Implementierung sowie etwas Klebercode entwickelt, um alles zusammenzuhalten. Für null Nutzen und eine potenzielle Verwundbarkeit beim Umgang mit Salz.

Nächster:

scrypt(bcrypt(password + salt))

Dieser ist nicht schlecht, aber auch hier benötigen Sie Code, um die Ergebnisse von bcrypt separat in Hash und Salt/Round Count zu analysieren. In diesem Fall würde ich vermuten , dass es einen kleinen Vorteil gibt, da bcrypt und scrypt für auf unterschiedliche Weise funktionieren. ungefähr dasselbe Ziel, was es für einen äußerst gut finanzierten Angreifer etwas schwieriger machen würde, benutzerdefinierte ASICs zu erstellen, um Ihr Schema zu brechen. Aber ist das wirklich notwendig? Werden Sie wirklich in eine Situation geraten, in der ein Nationalstaat ein paar Millionen Dollar dafür ausgeben wird, nur um Ihren Hash zu brechen? Und wenn dieser Fall jemals auftritt, wird es den Angreifer dann wirklich stören, ein paar zusätzliche Millionen ausgeben zu müssen, um seine Chipanzahl zu verdoppeln?

Ein weiteres potenzielles Problem bei der Kombination von bcrypt und scrypt auf diese Weise besteht darin, dass nur sehr wenige Studien zur Interaktion der beiden durchgeführt wurden. Daher wissen wir nicht , ob es seltsame Fälle gibt, die Probleme verursachen können. Nehmen Sie als offensichtlicheres Beispiel das Zeitfenster. Wir berechnen c=m^k Für eine Nachricht m und einen gleich langen, perfekt zufälligen Schlüssel k und erhalten perfekte Sicherheit. Machen wir es also zweimal , um noch mehr Sicherheit zu gewährleisten! Das gibt uns c=m^k^k ... oh, warte, das gibt uns nur m. Da wir uns nicht die Zeit genommen haben, die Funktionsweise der Interna des Systems richtig zu verstehen, hatten wir eine echte Sicherheitslücke. Natürlich ist es bei KDFs komplizierter, aber das gleiche Prinzip gilt.

Und schlussendlich:

sha1(md5(scrypt(password + md5(salt))))

Wieder stoßen wir auf das MD5-Salzproblem. Ich bin auch fasziniert davon, wie MD5 den SHA1-Hash verwendet. Welchen möglichen Nutzen könnte das haben, wenn Sie bereits ein langsames KDF wie scrypt verwenden? Die wenigen Nanosekunden, die für die Berechnung dieser Hashes benötigt würden, verblassen im Vergleich zu den Hunderten von Millisekunden, die für die Berechnung des Scrypt Digest des Kennworts erforderlich wären. Sie erhöhen die Komplexität für eine absolut irrelevante Ebene der "Sicherheit", was immer eine schlechte Sache ist. Jede Codezeile, die Sie schreiben, ist eine potenzielle Sicherheitslücke.


Denken Sie jetzt an den Punkt, den ich zu Beginn meiner Antwort gemacht habe. Wenn Sie zu irgendeinem Zeitpunkt in dieser Antwort dachten "Oh ja, darüber habe ich nicht nachgedacht", dann ist mein Standpunkt bewiesen.

Sie stoßen auf die falsche Maxime von Dave :

Wenn ich mehr Krypto-Dinge hinzufüge, ist es sicherer.

Dies ist eine häufige Eigenschaft unter Entwicklern, und ich habe es auch einmal geglaubt. Es geht Hand in Hand mit der Ablehnung anderer Prinzipien wie Kerckhoffs Prinzip . Letztendlich muss man erkennen und akzeptieren, dass Dunkelheit keine Sicherheitsschiene ist. Es ist eine Krücke für schwache Krypto. Wenn Ihre Krypto stark ist, braucht sie keine Krücke.

70
Polynomial

Krypto-Grundelemente können sicher gestapelt werden und erhöhen die Sicherheit nur dann, wenn Sie die Grundelemente gut genug kennen, um ihre Schwächen und die Wechselwirkung dieser Schwächen zu verstehen. Wenn Sie sie nicht kennen oder die Details nicht verstehen - so erhalten Sie Daves Protokoll .

Das Problem ist, dass nur sehr wenige Menschen sie alle gut genug kennen, um beurteilen zu können, ob eine bestimmte Kombination sicher ist. Aus diesem Grund muss es veröffentlicht und überprüft werden. Wenn es nicht überprüft wurde, können Sie nicht wissen, ob es so stark wie scrypt ist oder näher an CRC32 liegt.

Wenn Sie also kein Experte sind, ist es durchaus möglich, dass Sie etwas Schwächeres als das schwächste Primitiv haben, das Sie verwendet haben (siehe Daves Protokoll), und Sie würden es nicht wissen. Zumindest würden Sie es nicht wissen, bis es geknackt ist - das Finden der Passwörter Ihrer Benutzer in Pastebin ist nicht der ideale Weg, um festzustellen, ob das Schema fehlerhaft ist.

Ich stimme zu, dass ein gewisses Maß an Dunkelheit aus einer Tiefenverteidigungsperspektive helfen kann, aber das zugrunde liegende System muss sicher sein.

Zwischen scrypt, bcrypt und PBDKF2 - Mindestens einer von ihnen wird auf so ziemlich jeder Plattform unterstützt. Diese sind bekannt und gut getestet - sie bieten unterschiedliche Schutzniveaus, sind aber immer noch weitaus sicherer als ein bizarres Stapeln von md5 und sha1.

15
Adam Caudill

Denken Sie bei Ihrer speziellen Frage zum Kombinieren von Verschlüsselung und Verschlüsselung daran, dass diese Funktionen konfigurierbare Kosten haben. Sie möchten diese Kosten so weit wie möglich erhöhen und sie gleichzeitig für Ihre spezifische Verwendung tolerierbar halten. Zum Beispiel, wenn Sie bcrypt mit bis zu [~ # ~] x [~ # ~] Iterationen verwenden können (darüber hinaus ist es für Ihren Server und Ihre durchschnittliche Anzahl von Benutzerverbindungen pro zu teuer zweitens) oder mit bis zu [~ # ~] y [~ # ~] Iterationen verschlüsseln, dann können Sie mit [~ # ~] x [nicht verschlüsseln (bcrypt). ~ # ~] Iterationen für bcrypt dann [~ # ~] y [~ # ~] Iterationen für scrypt: Dies liegt außerhalb Ihrer CPU Budget.

Wenn Sie also scrypt und bcrypt kaskadieren, müssen Sie beide mit weniger Iterationen verwenden, als Sie es mit einer allein hätten tun können. Sie "bekommen nicht das Beste aus beiden Welten", indem Sie sie einfach aneinander reihen. Das Beste, auf das Sie hoffen können, ist eine Art Durchschnitt zwischen beiden. Und das geht zu Lasten von komplexerem Code, was von Natur aus schlecht ist, wenn es um Sicherheit (oder, was das betrifft, Wartbarkeit) geht.

13
Thomas Pornin

Zusätzlich zu Adams Antwort möchte ich auch erwähnen, dass Sie jedes Mal, wenn Sie Kryptografie verwenden, einen starken und unvermeidbaren Grund dafür haben sollten. In Ihren obigen Beispielen existiert dies nicht.

md5(md5(salt) + bcrypt(password))
scrypt(bcrypt(password + salt))

Die Algorithmen bcrypt und scrypt sind bereits stark genug und werden als effektiv unzerbrechlich angesehen. Welches Problem versuchen Sie zu lösen? Und warum glauben Sie, dass die Kombination ihrer Ergebnisse (insbesondere mit md5) wird es lösen? Im besten Fall haben Sie wahrscheinlich lediglich die Schwierigkeit, das Kennwort zu knacken, auf die des schwächsten Hashs reduziert, anstatt die Sicherheit tatsächlich zu verbessern. Und das Worst-Case-Szenario ist erschreckend undefiniert.

md5(sha1(md5(md5(password) + sha1(password + salt)) + password))

Diese Lösung ist noch schlimmer. Es implementiert manuell ein wiederholtes Hashing-Schema, jedoch ohne genügend Runden, um Angreifern tatsächlich einen signifikanten Arbeitsfaktor aufzuerlegen.

Kurz gesagt, das Problem ist, dass:

  • sie werfen Kryptografie herum, ohne tatsächlich ein Problem zu haben, das gelöst werden muss
  • sie haben die Wahrscheinlichkeit von Fehlern in Ihrer Implementierung drastisch erhöht
  • sie haben wahrscheinlich die Sicherheit auf den schwächsten Hashing-Algorithmus reduziert, und
  • sie haben ein unbekanntes Worst-Case-Szenario eingeführt, in dem früher keines existierte
9
Stephen Touset

Wenn Sie unsichere Operationen auf einen sicheren Algorithmus anwenden, können Sie die Hashing-Funktion definitiv unterbrechen. Ihre neue Funktion könnte sogar weitaus schlechter sein als das schwächste Glied.

Warum nutzen Angreifer dies nicht, um sichere Funktionen zu unterbrechen? Es hilft ihnen nicht. Wenn ich zum Beispiel die ersten 440 Bits eines mit bcrypt sicher gespeicherten Passworts mit Nullen überschreibe, kann ich mit Brute Force leicht ein passendes Passwort finden, aber dieses Passwort funktioniert nur mit meinem eigenen schrecklichen Algorithmus. Eine vernünftige Implementierung würde es wahrscheinlich ablehnen.

Das Nullstellen großer Teile eines Hashs ist eindeutig schlecht, aber selbst sichere Operationen können zu etwas Gefährlichem kombiniert werden. Das Hinzufügen von zwei Zahlen (modulo n, um eine konstante Länge sicherzustellen) ist 'sicher'. Im Allgemeinen geht keine Entropie verloren. Dennoch reduziert h (x) + h(x) mod n die Qualität des Hash h (x) um ein Bit, Das Ergebnis ist immer gerade. Der ebenso sichere Operator XOR macht es noch schlimmer, als h (x) XOR h (x) = gibt immer Null zurück.

Diese Fallstricke sind ziemlich offensichtlich, aber nicht alle. Denken Sie daran, dass es wie immer trivial ist, ein Schema zu erfinden, das gut genug ist, dass Sie selbst keine Schwächen finden, aber sehr schwierig, eines zu erfinden, wo es sonst niemand kann.

8
Marcks Thomas

Hash-Funktionen werden von Kryptographen erstellt und von Kryptographen zerstört. Es gibt viele starke und schwache Hash-Funktionen, die heute noch verwendet werden. Programmierer sollten den Kryptographen und der Hash-Funktion vertrauen. Wenn es jemals eine Sicherheitslücke in der Hash-Funktion gab, dann würden Sie sicherlich im Internet oder durch Mitarbeiter davon erfahren, und dann werden Kryptographen sicherlich eine gründliche Untersuchung durchführen. Bei jedem sicheren Hash-Algorithmus ist nur bekannt, dass eine Schwäche ein Bruteforce-Angriff sein kann.

Das Kombinieren von Hash-Funktionen bietet fast keine zusätzliche Sicherheit und alles, was Sie hinzufügen möchten, ist wahrscheinlich bereits in der Funktion implementiert.

Das Versalzen eines Passworts ist ideal, um die Effektivität von Rainbow-Tabellen zu verringern, sodass ein Passwort nicht einfach "nachgeschlagen" werden kann. Unabhängig davon, ob Sie eine Funktion zweimal hashen oder die Hash-Funktion ändern, wird das Kennwort im Wesentlichen gesalzen. Und die meisten Funktionen enthalten eine einfache Methode zum Salzen, sodass dies nicht unbedingt implementiert werden muss.

Nehmen wir an, ich möchte meinen eigenen sicheren Hash erstellen, weil jeder das tut. Und da ich kein Kryptograf bin, werde ich es "wirklich" sicher brauchen, denn natürlich weiß jeder Programmierer, wie man einen sicheren Hash erstellt, anstatt die bereits erstellten sicheren zu verwenden. Also erstelle ich meine verschlagene Hash-Funktion mod10 (md5 (sha1 (bcrypt (Passwort + Salt)))).

Wie Sie an meiner Hash-Funktion sehen können, ist sie wirklich sicher, weil ich so viele verschiedene Dinge darin verwende. Natürlich ist in diesem albernen Beispiel leicht zu erkennen, dass es nur 10 verschiedene mögliche Ausgänge geben wird. Durch die Verwendung einer einzigen sicheren Hash-Funktion wäre dies jedoch vollständig vermieden worden.

Sicher, Ihr System sollte sicher sein, wenn der Angreifer über den Quellcode verfügt, aber es ist sehr wahrscheinlich, dass Ihr Angreifer keinen Zugriff auf Ihren Quellcode hat und wahrscheinlich nicht in der Lage ist, Ihren verrückten Hash zu erraten und einen Versuch eines Brute zu unternehmen Kraft unmöglich

Wir gehen also davon aus, dass ein Angreifer die Datenbanktabelle mit den Hashes erhalten hat. Ich würde annehmen, dass es sehr wahrscheinlich ist, dass ein Angreifer auch die Webseitendateien erhalten kann. Ihre Systeme, auf denen diese Dienste ausgeführt werden, haben möglicherweise denselben Exploit, mit dem Sie die Datenbank abrufen konnten. Ein Datenbankserver ist so eingerichtet, dass die Öffentlichkeit nicht direkt darauf zugreifen kann. Auf der anderen Seite steht Ihr Webserver mit Ihrem Code an vorderster Front.

6
ponsfonze

Jedes Mal, wenn Sie die Komplexität eines Algorithmus erhöhen oder sogar mehr Codezeilen hinzufügen, erhöhen Sie die Fehlerquellen in Ihrer Anwendung. Das Kombinieren von Algorithmen kann unbeabsichtigte Folgen haben. Dies kann zu bestimmten tell oder anderen Zeichen führen, die die kryptografische Stärke des Systems tatsächlich schwächen können.

Je mehr Bibliotheken Sie in Ihrer Anwendung verwenden, desto größer ist das Risiko für Ihre Anwendung im Allgemeinen. Wenn in einer Implementierung ein Fehler gefunden wird, der eine Schwachstelle, Codeausführung usw. zulässt, ist Ihre Anwendung anfällig. Wenn Sie zufällig einen anderen Algorithmus ausgewählt haben, der nicht angegriffen wurde, ist dies vorerst Ihr Tresor (natürlich könnten Sie auch auf der unglücklichen Seite des Glücks sein).

Denken Sie daran [~ # ~] Kuss [~ # ~] : Halten Sie es einfach, dumm , sonst können Sie sich verlaufen in der Komplexität.

5
Eric G

Ich werde mit einer ganzen Reihe von Leuten nicht einverstanden sein, die klüger als ich und erfahrener in der Sicherheit sind als ich. Also irre ich mich wahrscheinlich.

Das Improvisieren Ihrer eigenen Hash-Funktion ist eine gute Idee - wenn Sie es richtig machen. Befolgen Sie diese 3 einfachen Schritte.

  1. Identifizieren Sie eine Schwäche oder einen Fehler in den vorhandenen Hash-Funktionen.
  2. Improvisieren Sie Ihre eigene Hash-Funktion, die diesen Fehler nicht aufweist.
  3. Stellen Sie sicher, dass Ihre improvisierte Hash-Funktion alle Stärken der vorhandenen Hash-Funktionen aufweist.

Nachdem Sie Schritt 3 abgeschlossen haben, wären Sie ein Dummkopf, wenn Sie Ihre improvisierte Hash-Funktion nicht verwenden würden.

2
emory

Wenn Sie verschiedene Hash-Funktionen verketten, weil Sie sich Sorgen um die eine machen, ist es wichtig, dass Sie das Salt vor dem Anwenden des nächsten Hashing-Algorithmus erneut anwenden. Kollisionsschwächen in einem der Algorithmen beeinträchtigen die zweite Hash-Funktion, die Sie verwenden, nicht:

scrypt (bcrypt (Passwort + Salz) + Salz)

Aber ich denke, Verschlüsselung ist mittlerweile eine etablierte Technik. Argon 2i hat den Wettbewerb zum Hashing von Passwörtern gewonnen und gilt als sicherer. Verschlüsselung gibt es schon seit langer Zeit und hat sich als sicher gegen triviale Bypässe erwiesen.

Daher wäre das Folgende sinnvoller und würde die Stärke von Argon 2i kombinieren, würde jedoch auf bcrypt zurückgreifen, wenn ein zukünftiger Angreifer zeigen würde, wie Argon 2i trivial gebrochen werden kann, was derzeit als schwierig angesehen wird:

bcrypt (Argon2i (Passwort + Salz) + Salz)

Es ist jedoch weniger wahrscheinlich, dass Sie einen Fehler machen, wenn Sie einfach Folgendes tun:

scrypt (Passwort + Salz) oder bcrypt (Passwort + Salz)

Denken Sie daran, dass die meisten Verstöße auf menschliches Versagen im Code zurückzuführen sind. Es ist viel besser, ihn einfach zu halten und Ihren Code mit dynamischen, statischen Analysen und Überprüfern des menschlichen Codes gründlich zu überprüfen, um sicherzustellen, dass keine SQL-Injection-Angriffe durchgehen (Denken Sie daran, Ihre Datenbankabfragen immer zu parametrisieren !)

0
user3083447