Existe-t-il des implémentations JavaScript SHA-256 qui sont généralement considérées comme dignes de confiance?

95

J'écris un login pour un forum et je dois hacher le mot de passe côté client en javascript avant de l'envoyer au serveur. J'ai du mal à déterminer à quelle implémentation SHA-256 je peux vraiment faire confiance. Je m'attendais à ce qu'il y ait une sorte de script faisant autorité que tout le monde utilise, mais je trouve des tas de projets différents, tous avec leurs propres implémentations.

Je me rends compte que l'utilisation de la crypto d'autres personnes est toujours un acte de foi à moins que vous ne soyez qualifié pour l'examiner vous-même, et qu'il n'y a pas de définition universelle de «digne de confiance», mais cela semble être quelque chose de commun et assez important pour qu'il devrait y en avoir un. de consensus sur ce qu'il faut utiliser. Suis-je juste naïf?

Modifier car cela revient souvent dans les commentaires: Oui, nous refaisons un hachage plus strict côté serveur. Le hachage côté client n'est pas le résultat final que nous sauvegardons dans la base de données. Le hachage côté client est dû au fait que le client humain le demande. Ils n'ont pas donné de raison précise pour laquelle, probablement ils aiment juste exagérer.

Jono
la source
9
Ne pas sortir du sujet, mais pourquoi hachez-vous le mot de passe côté client?
Steve
5
@ddyer Pas même proche. "Don't roll your own" s'applique à l'invention de votre propre algorithme, à l'écriture de votre propre implémentation d'un algorithme, au développement de votre propre protocole en plus des algorithmes cryptographiques, ou à peu près tout ce qui précède en utilisant une abstraction de haut niveau. Si vous pensez que vous serez sûr de vous en tenir à un noyau sécurisé et d'écrire uniquement du code glue, vous allez passer un mauvais moment.
Stephen Touset
26
si vous utilisez un mot de passe haché sans protocole de challenge / réponse, alors le mot de passe haché EST le mot de passe et c'est vraiment la même chose que la transmission du mot de passe en texte clair.
ddyer
26
@ddyer Il y a une certaine valeur à protéger le mot de passe en texte brut de l'utilisateur pour tous les autres sites sur lesquels il pourrait l'utiliser, sinon pour notre site en particulier. C'est une solution facile qui ne nous aidera peut-être pas mais qui peut potentiellement aider l'utilisateur si nous nous trompons quelque part. Et comme je l'ai dit, la demande du client, je ne peux rien y faire même si je le voulais.
jono
4
@Anorov Je suis plus qu'ouvert à l'idée de changer d'avis :) mais dans ce cas, je ne comprends pas vraiment comment votre argument s'applique. Nous hachons le mot de passe deux fois: une fois côté client avec un simple SHA-256, et une fois côté serveur avec quelque chose de plus exigeant. Le premier pour protéger le texte brut en cas de MITM ou similaire, et le second pour la protection brute. Même si vous détenez la base de données et le hachage de l'administrateur, vous ne pouvez pas l'utiliser directement pour valider la connexion.
jono

Réponses:

111

La bibliothèque cryptographique Stanford JS contient une implémentation de SHA-256. Bien que la crypto dans JS ne soit pas vraiment une entreprise aussi bien approuvée que les autres plates-formes de mise en œuvre, celle-ci est au moins partiellement développée par, et dans une certaine mesure parrainée par, Dan Boneh , qui est un nom bien établi et de confiance en cryptographie. , et signifie que le projet est supervisé par quelqu'un qui sait réellement ce qu'il fait. Le projet est également soutenu par la NSF .

Cela vaut la peine de souligner, cependant ...
... que si vous hachez le mot de passe côté client avant de le soumettre, alors le hachage est le mot de passe , et le mot de passe d'origine devient inutile. Un attaquant n'a qu'à intercepter le hachage pour se faire passer pour l'utilisateur, et si ce hachage est stocké non modifié sur le serveur, alors le serveur stocke le vrai mot de passe (le hachage) en texte brut .

Donc, votre sécurité est maintenant pire parce que vous avez décidé d' ajouter vos propres améliorations à ce qui était auparavant un système de confiance.

Tylerl
la source
31
Cependant, si vous hachez à nouveau sur le serveur, cette pratique est parfaitement légitime.
Nick Brunt
13
@NickBrunt si vous hachez sur le serveur, vous n'enregistrez pas le mot de passe transmis, mais vous n'avez ajouté aucune sécurité au-delà de la transmission du mot de passe d'origine par opposition à la transmission du hachage du mot de passe, car dans les deux cas, ce que vous transmettez est le vrai mot de passe.
tylerl
54
C'est vrai, mais ce n'est pas pire que d'envoyer un mot de passe qui est peut-être utilisé ailleurs sur Internet en texte clair.
Nick Brunt
15
Je suis d'accord avec @NickBrunt. Il est préférable que l'attaquant lise une chaîne de hachage aléatoire qui ne peut correspondre qu'à cette application particulière plutôt que le mot de passe d'origine qui peut être utilisé à plusieurs endroits.
Aebsubis
12
Je dois utiliser le hachage côté client car je ne veux pas que le serveur voie le mot de passe en texte brut de l'utilisateur. Pas pour une sécurité accrue du service que je fournis, mais pour l'utilisateur. Je sauvegarde non seulement le hachage de l'utilisateur salé avec un sel constant (constante par utilisateur, pas globalement), mais je le rechappe également avec un sel de session aléatoire à chaque connexion, ce qui offre un peu de sécurité supplémentaire sur le réseau contre le reniflement. Si le serveur est compromis, la partie est terminée, mais c'est vrai pour tout ce qui n'est pas vraiment p2p de toute façon.
Hatagashira
49

Sur https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest, j'ai trouvé cet extrait de code qui utilise le module js interne:

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;
}

Notez que crypto.subtledans uniquement disponible sur httpsou localhost- par exemple pour votre développement local avec python3 -m http.servervous devez ajouter cette ligne à votre /etc/hosts: 0.0.0.0 localhost

Redémarrez - et vous pouvez ouvrir localhost:8000en travaillant crypto.subtle.

Vitaly Zdanevich
la source
2
C'est génial. Je vous remercie. Une API comparable est-elle disponible dans Node.js?
Con Antonakos
2
La bibliothèque 'crypto' intégrée devrait très bien fonctionner: nodejs.org/dist/latest-v11.x/docs/api/crypto.html
tytho
17

L'implémentation SHA-256 de Forge est rapide et fiable.

Pour exécuter des tests sur plusieurs implémentations JavaScript SHA-256, accédez à http://brillout.github.io/test-javascript-hash-implementations/ .

Les résultats sur ma machine suggèrent que forge est la mise en œuvre la plus rapide et également considérablement plus rapide que la bibliothèque crypto Javascript Stanford (sjcl) mentionnée dans la réponse acceptée.

Forge a une taille de 256 Ko, mais l'extraction du code associé à SHA-256 réduit la taille à 4,5 Ko, voir https://github.com/brillout/forge-sha256

brillant
la source
J'ai réussi à l'installer avec npm install node-forge, mais maintenant, comment utiliser la bibliothèque pour créer un hachage à partir de la chaîne foo?
utilisateur
15

Pour ceux qui sont intéressés, voici le code pour créer un hachage SHA-256 en utilisant sjcl:

import sjcl from 'sjcl'

const myString = 'Hello'
const myBitArray = sjcl.hash.sha256.hash(myString)
const myHash = sjcl.codec.hex.fromBits(myBitArray)
Danny Sullivan
la source
4
Cela devrait être incorporé dans la réponse acceptée pour ceux qui veulent utiliser la solution là-bas. (Heck, même leurs propres documents n'ont pas d'extrait de
code
C'est la meilleure réponse. J'essayais la solution cryptographique mais cela n'a pas fonctionné pour moi.
Augusto Gonzalez
11

Non, il n'y a aucun moyen d'utiliser le JavaScript du navigateur pour améliorer la sécurité des mots de passe. Je vous recommande vivement de lire cet article . Dans votre cas, le plus gros problème est le problème des œufs de poule:

Quel est le "problème de l'oeuf de poule" avec la livraison de la cryptographie Javascript?

Si vous ne faites pas confiance au réseau pour fournir un mot de passe ou, pire, si vous ne faites pas confiance au serveur pour ne pas garder les secrets des utilisateurs, vous ne pouvez pas leur faire confiance pour fournir le code de sécurité. Le même attaquant qui reniflait des mots de passe ou lisait des journaux avant d'introduire la cryptographie détourne simplement du code cryptographique après vous.

[...]

Pourquoi ne puis-je pas utiliser TLS / SSL pour fournir le code crypto Javascript?

Vous pouvez. C'est plus difficile qu'il n'y paraît, mais vous transmettez en toute sécurité la cryptographie Javascript à un navigateur utilisant SSL. Le problème est qu'après avoir établi un canal sécurisé avec SSL, vous n'avez plus besoin de la cryptographie Javascript; vous avez une «vraie» cryptographie.

Ce qui conduit à ceci:

Le problème avec l'exécution de code crypto en Javascript est que pratiquement toutes les fonctions dont dépend la cryptographie peuvent être remplacées silencieusement par tout élément de contenu utilisé pour créer la page d'hébergement. La sécurité cryptographique peut être annulée tôt dans le processus (en générant de faux nombres aléatoires, ou en falsifiant les constantes et les paramètres utilisés par les algorithmes), ou plus tard (en renvoyant le matériel clé à un attaquant), ou --- dans le scénario le plus probable --- en contournant complètement la crypto.

Il n'y a aucun moyen fiable pour un morceau de code Javascript de vérifier son environnement d'exécution. Le code crypto Javascript ne peut pas demander, "est-ce que j'ai vraiment affaire à un générateur de nombres aléatoires, ou à un fac-similé de celui fourni par un attaquant?" Et il ne peut certainement pas affirmer que "personne n'est autorisé à faire quoi que ce soit avec ce crypto secret sauf d'une manière que moi, l'auteur, j'approuve". Ce sont deux propriétés qui sont souvent fournies dans d'autres environnements utilisant la cryptographie, et elles sont impossibles en Javascript.

Fondamentalement, le problème est le suivant:

  • Vos clients ne font pas confiance à vos serveurs, ils souhaitent donc ajouter un code de sécurité supplémentaire.
  • Ce code de sécurité est fourni par vos serveurs (ceux auxquels ils ne font pas confiance).

Ou bien,

  • Vos clients ne font pas confiance à SSL, ils veulent donc que vous utilisiez un code de sécurité supplémentaire.
  • Ce code de sécurité est fourni via SSL.

Remarque: De plus, SHA-256 ne convient pas à cela, car il est si facile de forcer brutalement des mots de passe non itérés non salés . Si vous décidez quand même de le faire, recherchez une implémentation de bcrypt , scrypt ou PBKDF2 .

Brendan Long
la source
1
Ce n'est pas correct. La sécurité du mot de passe peut être améliorée par hachage côté client. Il est également faux que SHA-256 ne convient pas tant que quelque chose comme PBKDF2 est utilisé côté serveur. Troisièmement, alors que l'article de matasano contient des informations utiles, la ligne de frappe est fausse car nous avons maintenant des applications de navigateur qui changent fondamentalement le problème de la poule et de l'œuf.
user239558
2
Vous avez raison de dire que PBKDF2 doit être utilisé sur le client. La rage contre la crypto javascript côté client suppose que la page initiale et les demandes suivantes ont les mêmes propriétés de sécurité. Ce n'est évidemment pas vrai pour les applications de navigateur où la page initiale est installée séparément et est statique. Ce n'est pas non plus vrai pour les pages avec une sémantique de cache permanent. Dans ces cas, c'est TOFU qui est exactement le même que lors de l'installation d'un programme côté client. Cela peut également être vrai dans les configurations plus complexes où la diffusion de pages statiques et dynamiques est séparée sur le serveur.
user239558
3
Il est légitime d'éviter que les administrateurs système aient accès aux mots de passe des utilisateurs, donc le hachage côté client empêche les DBA ou d'autres professionnels de l'informatique de voir les mots de passe des utilisateurs et d'essayer de les réutiliser pour accéder à d'autres services / systèmes, car de nombreux utilisateurs répètent leurs mots de passe. .
Yamada
1
@Xenland Tout ce que vous faites est de créer un nouveau mot de passe qui est "token + password". Tous les problèmes d'origine s'appliquent toujours. Par exemple, un attaquant peut remplacer le JavaScript par du code pour lui envoyer le jeton + le mot de passe.
Brendan Long
2
@Xenland Le problème de la poule et de l'œuf n'a rien à voir avec les demandes que vous envoyez. Le problème est que votre client utilise du code JavaScript qu'il a téléchargé via une connexion non sécurisée pour effectuer des travaux de sécurité. Le seul moyen d'envoyer en toute sécurité JavaScript à un navigateur Web est de le diffuser via TLS, et une fois que vous faites cela, vous n'avez rien d'autre à faire car votre connexion est déjà sécurisée. Peu importe votre protocole si vous utilisez du code JavaScript qu'un attaquant peut simplement remplacer.
Brendan Long
10

J'ai trouvé cette implémentation très simple à utiliser. Possède également une généreuse licence de style BSD:

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

J'avais besoin d'un moyen rapide d'obtenir la représentation hexadécimale d'un hachage SHA-256. Cela n'a pris que 3 lignes:

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

Outre la bibliothèque de Stanford mentionnée par tylerl. J'ai trouvé jsrsasign très utile (Github repo ici: https://github.com/kjur/jsrsasign ). Je ne sais pas à quel point il est digne de confiance, mais j'ai utilisé son API de SHA256, Base64, RSA, x509, etc. et cela fonctionne plutôt bien. En fait, il inclut également la bibliothèque de Stanford.

Si tout ce que vous voulez faire est SHA256, jsrsasign peut être exagéré. Mais si vous avez d'autres besoins dans le domaine connexe, je pense que c'est une bonne solution.

Loin
la source
2
Il est plus sûr et plus rapide d'utiliser developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API
Vitaly Zdanevich
2
D'accord, mais pour être honnête, j'ai répondu à cette question en 2015 alors que la spécification de l'API crypto Web de Mozilla était publiée en janvier 2017
Faraway