Gibt es SHA-256-Javascript-Implementierungen, die allgemein als vertrauenswürdig gelten?

93

Ich schreibe ein Login für ein Forum und muss das Passwort clientseitig in Javascript hashen, bevor ich es an den Server weiterleiten kann. Ich habe Probleme herauszufinden, welcher SHA-256-Implementierung ich tatsächlich vertrauen kann. Ich hatte erwartet, dass es eine Art maßgebliches Skript gibt, das jeder verwendet, aber ich finde viele verschiedene Projekte, alle mit ihren eigenen Implementierungen.

Mir ist klar, dass die Verwendung der Krypto anderer Leute immer ein Vertrauenssprung ist, es sei denn, Sie sind qualifiziert, sie selbst zu überprüfen, und dass es keine universelle Definition von "vertrauenswürdig" gibt, aber dies scheint etwas Gemeinsames und Wichtiges zu sein, das es geben sollte Konsens darüber, was zu verwenden ist. Bin ich nur naiv

Bearbeiten, da es in den Kommentaren häufig vorkommt: Ja, wir machen auf der Serverseite wieder einen strengeren Hash. Das clientseitige Hashing ist nicht das Endergebnis, das wir in der Datenbank speichern. Das clientseitige Hashing liegt daran, dass der menschliche Client es anfordert. Sie haben keinen bestimmten Grund angegeben, warum sie wahrscheinlich nur Overkill mögen.

Jono
quelle
9
Nicht um vom Thema abzukommen, aber warum hasst du das Passwort auf der Clientseite?
Steve
5
@ddyer Nicht einmal in der Nähe. "Rollen Sie nicht Ihre eigenen" bezieht sich auf das Erfinden Ihres eigenen Algorithmus, das Schreiben Ihrer eigenen Implementierung eines Algorithmus, das Entwickeln Ihres eigenen Protokolls auf der Grundlage von Kryptoalgorithmen oder so ziemlich alles darüber, eine Abstraktion auf so hoher Ebene wie möglich zu verwenden. Wenn Sie glauben, dass Sie sicher an einem sicheren Kern festhalten und nur Klebercode schreiben, werden Sie eine schlechte Zeit haben.
Stephen Touset
26
Wenn Sie ein Hash-Passwort ohne Challenge / Response-Protokoll verwenden, ist das Hash-Passwort das Passwort und entspricht in Wirklichkeit der Übertragung des Passworts im Klartext.
Ddyer
26
@ddyer Es ist sinnvoll, das Klartextkennwort des Benutzers für alle anderen Websites zu schützen, auf denen er möglicherweise verwendet wird, wenn nicht für unsere Website. Es ist eine einfache Lösung, die uns vielleicht nicht hilft, aber möglicherweise dem Benutzer helfen kann, wenn wir es irgendwo vermasseln. Und wie gesagt, Kundenanfrage, nichts, was ich dagegen tun kann, selbst wenn ich wollte.
Jono
4
@Anorov Ich bin mehr als offen dafür, meine Meinung zu ändern :) aber in diesem Fall verstehe ich nicht wirklich, wie Ihr Punkt zutrifft. Wir haben das Passwort zweimal gehasht: einmal auf der Clientseite mit einem einfachen SHA-256 und einmal auf der Serverseite mit etwas Anspruchsvollerem. Der erste zum Schutz des Klartextes bei MITM oder ähnlichem und der zweite zum brutalen Schutz. Selbst wenn Sie die Datenbank und den Admin-Hash in den Griff bekommen haben, können Sie dies nicht direkt verwenden, um die Anmeldung zu überprüfen.
Jono

Antworten:

110

Die Stanford JS Crypto Library enthält eine Implementierung von SHA-256. Während Krypto in JS nicht so gut geprüft ist wie andere Implementierungsplattformen, wird diese zumindest teilweise von Dan Boneh entwickelt und bis zu einem gewissen Grad von Dan Boneh gesponsert, einem etablierten und vertrauenswürdigen Namen in der Kryptographie und bedeutet, dass das Projekt von jemandem überwacht wird, der tatsächlich weiß, was er tut. Das Projekt wird auch von der NSF unterstützt .

Es ist jedoch darauf hinzuweisen,
dass, wenn Sie das Kennwort clientseitig hashen, bevor Sie es senden, der Hash das Kennwort ist und das ursprüngliche Kennwort irrelevant wird. Ein Angreifer muss nur den Hash abfangen, um sich als Benutzer auszugeben. Wenn dieser Hash unverändert auf dem Server gespeichert ist, speichert der Server das wahre Kennwort (den Hash) im Klartext .

Ihre Sicherheit ist jetzt schlechter, weil Sie beschlossen haben , Ihre eigenen Verbesserungen zu einem zuvor vertrauenswürdigen Schema hinzuzufügen .

tylerl
quelle
30
Wenn Sie jedoch erneut auf dem Server hashen, ist diese Vorgehensweise absolut legitim.
Nick Brunt
13
@NickBrunt Wenn Sie einen Hash auf dem Server haben, speichern Sie das übertragene Passwort nicht, aber Sie haben keine Sicherheit hinzugefügt, die über die Übertragung des ursprünglichen Passworts hinausgeht, im Gegensatz zur Übertragung des Passwort-Hash, da in beiden Fällen das übertragen wird echtes Passwort.
Tyler
54
Stimmt, aber es ist nicht schlimmer als ein Passwort zu senden, das möglicherweise an anderer Stelle im Internet im Klartext verwendet wird.
Nick Brunt
15
Ich stimme @NickBrunt zu. Es ist besser, wenn der Angreifer eine zufällige Hash-Zeichenfolge liest, die möglicherweise nur zu dieser bestimmten App passt, als das ursprüngliche Kennwort, das an mehreren Stellen verwendet werden kann.
Aebsubis
12
Ich muss clientseitiges Hashing verwenden, da ich nicht möchte, dass der Server jemals das Klartextkennwort des Benutzers sieht. Nicht für zusätzliche Sicherheit für den von mir bereitgestellten Dienst, sondern für den Benutzer. Ich speichere nicht nur den Hash des Benutzers, der mit einem konstanten Salt gesalzen ist (konstant pro Benutzer, nicht global), sondern hashe ihn auch mit einem zufälligen Sitzungssalz bei jedem Login, der ein wenig zusätzliche Sicherheit über das Netzwerk gegen Schnüffeln bietet. Wenn der Server kompromittiert wird, ist das Spiel vorbei, aber das gilt für alles, was nicht wirklich p2p ist.
Hatagashira
48

Unter https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest habe ich dieses Snippet gefunden, das das interne js-Modul verwendet:

async function sha256(message) {
    // encode as UTF-8
    const msgBuffer = new TextEncoder('utf-8').encode(message);                    

    // hash the message
    const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);

    // convert ArrayBuffer to Array
    const hashArray = Array.from(new Uint8Array(hashBuffer));

    // convert bytes to hex string                  
    const hashHex = hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('');
    return hashHex;
}

Beachten Sie, dass crypto.subtlein nur verfügbar für httpsoder localhost- zum Beispiel für Ihre lokale Entwicklung mit - python3 -m http.serverSie diese Zeile zu Ihrem hinzufügen müssen /etc/hosts: 0.0.0.0 localhost

Neustart - und Sie können localhost:8000mit der Arbeit öffnen crypto.subtle.

Vitaly Zdanevich
quelle
2
Das ist fantastisch. Danke dir. Ist eine vergleichbare API in Node.js verfügbar?
Con Antonakos
2
Die eingebaute 'Krypto'-Bibliothek sollte gut funktionieren: nodejs.org/dist/latest-v11.x/docs/api/crypto.html
tytho
17

Die SHA-256-Implementierung von Forge ist schnell und zuverlässig.

Unter http://brillout.github.io/test-javascript-hash-implementations/ können Sie Tests für mehrere SHA-256-JavaScript-Implementierungen ausführen .

Die Ergebnisse auf meinem Computer deuten darauf hin, dass Forge die schnellste Implementierung ist und auch erheblich schneller als die in der akzeptierten Antwort erwähnte Stanford Javascript Crypto Library (sjcl).

Forge ist 256 KB groß, aber das Extrahieren des SHA-256-Codes reduziert die Größe auf 4,5 KB (siehe https://github.com/brillout/forge-sha256)

Brillout
quelle
Ich habe es geschafft, es mit zu installieren npm install node-forge, aber jetzt, wie man die Bibliothek benutzt, erstellt man einen Hash aus der Zeichenfolge foo?
Benutzer
15

Für Interessenten ist dies ein Code zum Erstellen von SHA-256-Hash mit sjcl:

import sjcl from 'sjcl'

const myString = 'Hello'
const myBitArray = sjcl.hash.sha256.hash(myString)
const myHash = sjcl.codec.hex.fromBits(myBitArray)
Danny Sullivan
quelle
4
Dies sollte in die akzeptierte Antwort für diejenigen aufgenommen werden, die die Lösung dort verwenden möchten. (Heck, sogar ihre eigenen Dokumente haben keinen Ausschnitt wie diesen)
MagicLegend
Dies ist die beste Antwort. Ich habe die Kryptolösung ausprobiert, aber sie hat bei mir nicht funktioniert.
Augusto Gonzalez
11

Nein, es gibt keine Möglichkeit, Browser-JavaScript zu verwenden, um die Kennwortsicherheit zu verbessern. Ich empfehle Ihnen dringend, diesen Artikel zu lesen . In Ihrem Fall ist das größte Problem das Hühnerei-Problem:

Was ist das "Hühnerei-Problem" bei der Bereitstellung von Javascript-Kryptografie?

Wenn Sie dem Netzwerk nicht vertrauen, dass es ein Kennwort übermittelt, oder, schlimmer noch, dem Server nicht vertrauen, dass er keine Benutzergeheimnisse bewahrt, können Sie ihm nicht vertrauen, dass er Sicherheitscode übermittelt. Derselbe Angreifer, der vor der Einführung von Krypto Passwörter beschnüffelt oder Tagebücher gelesen hat, entführt einfach Krypto-Code, nachdem Sie dies getan haben.

[...]

Warum kann ich TLS / SSL nicht verwenden, um den Javascript-Kryptocode bereitzustellen?

Sie können. Es ist schwieriger als es sich anhört, aber Sie übertragen Javascript-Krypto sicher über SSL an einen Browser. Das Problem ist, dass Sie nach dem Einrichten eines sicheren Kanals mit SSL keine Javascript-Kryptografie mehr benötigen. Sie haben "echte" Kryptographie.

Was dazu führt:

Das Problem beim Ausführen von Kryptocode in Javascript besteht darin, dass praktisch jede Funktion, von der die Krypto abhängt, von allen Inhalten, die zum Erstellen der Hosting-Seite verwendet werden, stillschweigend überschrieben werden kann. Die Kryptosicherheit kann zu Beginn des Prozesses (durch Generieren falscher Zufallszahlen oder durch Manipulation von Konstanten und Parametern, die von Algorithmen verwendet werden) oder später (durch Zurückschicken von Schlüsselmaterial zu einem Angreifer) oder --- im wahrscheinlichsten Szenario rückgängig gemacht werden --- indem die Krypto vollständig umgangen wird.

Es gibt keine zuverlässige Möglichkeit für einen Teil des Javascript-Codes, seine Ausführungsumgebung zu überprüfen. Javascript-Kryptocode kann nicht fragen: "Habe ich es wirklich mit einem Zufallszahlengenerator zu tun oder mit einem Faksimile eines von einem Angreifer bereitgestellten?" Und es kann mit Sicherheit nicht behaupten, dass "niemand etwas mit diesem Krypto-Geheimnis anfangen darf, außer auf eine Weise, die ich als Autor gutheiße". Dies sind zwei Eigenschaften, die häufig in anderen Umgebungen mit Krypto bereitgestellt werden und in Javascript nicht möglich sind.

Grundsätzlich ist das Problem folgendes:

  • Ihre Clients vertrauen Ihren Servern nicht und möchten daher zusätzlichen Sicherheitscode hinzufügen.
  • Dieser Sicherheitscode wird von Ihren Servern bereitgestellt (denen, denen sie nicht vertrauen).

Oder alternativ,

  • Ihre Kunden vertrauen SSL nicht und möchten daher, dass Sie zusätzlichen Sicherheitscode verwenden.
  • Dieser Sicherheitscode wird über SSL übermittelt.

Hinweis: Außerdem ist SHA-256 dafür nicht geeignet, da es so einfach ist, ungesalzene, nicht iterierte Kennwörter brutal zu erzwingen . Wenn Sie sich trotzdem dazu entschließen, suchen Sie nach einer Implementierung von bcrypt , scrypt oder PBKDF2 .

Brendan Long
quelle
1
Das ist nicht richtig. Die Passwortsicherheit kann durch Hashing auf der Clientseite verbessert werden. Es ist auch falsch, dass SHA-256 nicht geeignet ist, solange so etwas wie PBKDF2 auf der Serverseite verwendet wird. Drittens, während der Matasano-Artikel nützliche Erkenntnisse enthält, ist die Pointe falsch, da wir jetzt Browser-Apps haben, die das Henne-Ei-Problem grundlegend ändern.
user239558
2
Sie haben Recht, dass PBKDF2 auf dem Client verwendet werden sollte. Die Wut gegen clientseitige Javascript-Krypto setzt voraus, dass die erste Seite und nachfolgende Anforderungen dieselben Sicherheitseigenschaften haben. Dies gilt natürlich nicht für Browser-Apps, bei denen die erste Seite separat installiert und statisch ist. Dies gilt auch nicht für Seiten mit einer Cache-für-immer-Semantik. In diesen Fällen ist es TOFU, das genau das gleiche ist wie bei der Installation eines clientseitigen Programms. Dies kann auch bei komplexeren Setups der Fall sein, bei denen die Bereitstellung statischer und dynamischer Seiten auf dem Server getrennt ist.
user239558
3
Es ist legitim zu vermeiden, dass Systemadministratoren Zugriff auf Benutzerkennwörter haben. Daher verhindert das Hashing auf der Clientseite, dass Datenbankadministratoren oder andere IT-Experten Benutzerkennwörter anzeigen und versuchen, sie für den Zugriff auf andere Dienste / Systeme wiederzuverwenden, da viele Benutzer ihre Kennwörter wiederholen .
Yamada
1
@Xenland Alles, was Sie tun, ist ein neues Passwort zu erstellen, das "Token + Passwort" ist. Alle ursprünglichen Probleme gelten weiterhin. Beispielsweise kann ein Angreifer das JavaScript durch Code ersetzen, um ihm das Token + Passwort zu senden.
Brendan Long
2
@Xenland Das Henne-Ei-Problem hat nichts mit den von Ihnen gesendeten Anfragen zu tun. Das Problem ist, dass Ihr Client JavaScript-Code verwendet , den er über eine unsichere Verbindung heruntergeladen hat , um Sicherheitsarbeiten auszuführen . Die einzige Möglichkeit, JavaScript sicher an einen Webbrowser zu senden, besteht darin, es über TLS bereitzustellen. Sobald Sie dies getan haben, müssen Sie nichts weiter tun, da Ihre Verbindung bereits sicher ist. Es spielt keine Rolle, wie Ihr Protokoll lautet, wenn Sie JavaScript-Code verwenden, den ein Angreifer einfach ersetzen kann.
Brendan Long
10

Ich fand diese Implementierung sehr einfach zu bedienen. Hat auch eine großzügige BSD-Lizenz:

jsSHA: https://github.com/Caligatio/jsSHA

Ich brauchte einen schnellen Weg, um die Hex-String-Darstellung eines SHA-256-Hash zu erhalten. Es dauerte nur 3 Zeilen:

var sha256 = new jsSHA('SHA-256', 'TEXT');
sha256.update(some_string_variable_to_hash);
var hash = sha256.getHash("HEX");
Cobbzilla
quelle
0

Neben der Stanford Lib, die Tyler erwähnte. Ich fand jsrsasign sehr nützlich (Github-Repo hier: https://github.com/kjur/jsrsasign ). Ich weiß nicht, wie genau es vertrauenswürdig ist, aber ich habe die API von SHA256, Base64, RSA, x509 usw. verwendet und es funktioniert ziemlich gut. Tatsächlich enthält es auch die Stanford-Bibliothek.

Wenn Sie nur SHA256 tun möchten, ist jsrsasign möglicherweise ein Overkill. Aber wenn Sie andere Bedürfnisse in dem verwandten Bereich haben, denke ich, dass es eine gute Passform ist.

Weit weg
quelle
2
Es ist sicherer und schneller, developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API
Vitaly Zdanevich
1
Stimmen Sie zu, aber um fair zu sein, beantwortete ich diese Frage im Jahr 2015, während Mozillas Web-Krypto-API-Spezifikation im Januar 2017
Faraway