Il existe de nombreux articles sur Internet critiquant la cryptographie JavaScript dans le navigateur:
Ils soulèvent quelques points justes, mais j'aimerais les analyser dans une perspective de 2017 et connaître vos opinions. Pour ce faire, je définirai une architecture possible pour un site de notes chiffrées afin que vous puissiez évoquer d'éventuels problèmes et solutions concernant l'aspect sécurité:
Bien sûr, nous utiliserons SSL.
La première fois qu'un utilisateur se connecte à notre application de notes, nous lui envoyons notre clé publique. Cette clé sera utilisée pour vérifier l'authenticité de notre crypto.js
script. La clé publique sera stockée dans le navigateur de l'utilisateur.
UNE checker.js
le script est également téléchargé et stocké. Ce script ne changera jamais et il sera chargé de vérifier l'intégrité de crypto.js
et (2).
En (2) et (3), nous établissons une relation de confiance lors de la première utilisation (TOFU) entre l'utilisateur et notre site. La clé publique et checker.js
sont mis en cache à l'aide d'un service worker ou similaire. Nous utiliserons également SRI pour essayer de maximiser l'intégrité.
Même si nous utilisons SSL, une attaque MITM pourrait se produire lors du téléchargement (2) et (3), nous pourrions donc proposer un moyen de vérifier que la clé publique et checker.js
ne sont pas compromis. Par exemple, en comparant les hachages de la copie locale de (2) et (3) avec les vrais de notre site ou d'un site tiers. Cette solution n'est pas parfaite et c'est probablement le maillon faible, mais je pense qu'une attaque similaire pourrait être effectuée sur les applications de bureau.
Lors de la première connexion, nous envoyons également à l'utilisateur sa clé privée. Cette clé privée sera utilisée pour crypter et signer les notes. Cette clé privée sera cryptée.
La clé requise pour déchiffrer (6) est envoyée par e-mail à l'utilisateur. De cette façon, nous établissons une authentification à deux canaux.
En utilisant Web Crypto nous décryptons (6) avec (7). De cette façon (6) n'est jamais stocké dans le navigateur décrypté et il n'est pas accessible par JavaScript grâce à l'API Web Crypto.
Maintenant, nous pouvons commencer avec la fonctionnalité de notre application Web: créer des notes chiffrées. Pour ce faire, l'utilisateur écrit une note et cliquez sur le bouton Enregistrer. Le serveur envoie crypto.js
signé avec la clé privée du serveur (voir 2).
La signature est vérifiée à l'aide de la clé publique téléchargée en (2) avec (3) et si elle est correcte, la note est cryptée. Si checker.js
a été modifié, SRI doit arrêter ce processus.
La note est renvoyée au serveur et stockée.
Selon la fonctionnalité requise, le serveur doit supprimer la clé privée de l'utilisateur et ne conserver que la clé publique ou non.
Que pensez-vous de cette configuration?
Je ne suis pas satisfait de (5), mais c'est quelque chose qui peut arriver avec un logiciel natif. Ce n'est pas la première fois que nous voyons les installateurs sont compromis . Un MITM lors du téléchargement du programme d'installation peut également se produire. Et la signature de code natif est pas parfait .
Pensez-vous que la crypto web est encore pire que la crypto native?
Des suggestions qui pourraient améliorer la cryptographie du navigateur?
Le principal problème avec la cryptographie dans les pages Web est que, parce que le code que vous exécutez est chargé à partir d'un serveur Web, ce serveur a un contrôle total sur ce qu'est ce code et peut le changer chaque fois que vous actualisez la page. À moins d'inspecter manuellement le code que vous exécutez chaque fois que vous chargez une nouvelle page sur ce site (de préférence avant que ce code ne soit réellement exécuté), vous n'avez aucun moyen de savoir ce que ce code fera réellement.
L'API Web Cryptography peut atténuer les risques de cela en stockant en toute sécurité les clés cryptographiques d'une manière à laquelle les scripts exécutés sur la page ne peuvent pas accéder, mais toutes les opérations pouvant être effectuées avec ces clés (décryptage, signature, etc.) seront toujours disponibles pour ces scripts (potentiellement malveillants).
Tant que vous faites confiance au serveur pour ne pas se comporter de manière malveillante, la cryptographie dans le navigateur peut être très utile, mais dans de nombreuses applications où la cryptographie est utilisée, ce niveau de faire confiance à un serveur distant que vous ne contrôlez pas est inacceptable.
Pour votre régime en particulier:
- Bien sûr, nous utiliserons SSL
C'est bon. Sans SSL, toutes les mesures de sécurité ultérieures seraient inutiles car un attaquant pourrait simplement remplacer votre code par le sien et faire ce qu'il veut avec les données de l'utilisateur.
- La première fois qu'un utilisateur se connecte à notre application de notes, nous lui envoyons notre clé publique. Cette clé sera utilisée pour vérifier l'authenticité de notre script "crypto.js". La clé publique sera stockée dans le navigateur de l'utilisateur.
Cela semble inutile. TLS envoie déjà au client la clé publique de votre serveur et l'utilise pour vérifier l'authenticité de tout scripts que vous chargez sur cette connexion. Il n'y a aucune raison de recommencer la même chose en JavaScript.
- Un script "checker.js" est également téléchargé et stocké. Ce script ne changera jamais et il sera chargé de vérifier l'intégrité de "crypto.js" et (2).
Cela est également inutile, car il n'y a aucun moyen de faire respecter votre exigence que "ce script ne changera jamais". Vous pouvez envoyer un en-tête Cache-Control avec un max-age long, mais rien ne garantit que l'agent utilisateur respectera toujours cette valeur; la mise en cache n'est pas destinée à être invoquée pour des raisons de sécurité.
- En (2) et (3), nous établissons une relation de confiance lors de la première utilisation (TOFU) entre l'utilisateur et notre site. La clé publique et "checker.js" sont mis en cache à l'aide d'un service worker ou similaire.
Juste pour être clair: la mise en cache de ces fichiers avec des travailleurs de service n'a aucun impact sur la sécurité de l'ensemble du système. Lorsque l'utilisateur reviendra plus tard sur votre site, le navigateur vérifiera avec le serveur si le technicien de service a mis à jour et installe la nouvelle version si c'est le cas. Ainsi, le serveur a toujours le contrôle total du code exécuté dans le navigateur de l'utilisateur. Il n'y a pas de "relation de confiance lors de la première utilisation (TOFU)" ici.
- Même si nous utilisons SSL, une attaque MITM pourrait se produire lors du téléchargement (2) et (3), nous pourrions donc offrir un moyen de vérifier que la clé publique et "checker.js" ne sont pas compromis.
C'est un beau geste, mais comme je l'ai déjà dit, même si ces fichiers ne sont pas actuellement compromis, le serveur ou un MITM (qui a réussi à compromettre votre connexion TLS) peut facilement mettre à jour ces fichiers à tout moment pour les compromettre sans que l'utilisateur ne s'en rende compte, donc je ne vois pas vraiment l'intérêt de cette fonctionnalité.
Lors de la première connexion, nous envoyons également à l'utilisateur sa clé privée. Cette clé privée sera utilisée pour crypter et signer les notes. Cette clé privée sera cryptée.
La clé requise pour déchiffrer (6) est envoyée par e-mail à l'utilisateur. De cette façon, nous établissons une authentification à deux canaux.
En utilisant Web Crypto ( https://www.w3.org/TR/WebCryptoAPI/ ) nous décryptons (6) avec (7). De cette façon (6) n'est jamais stocké dans le navigateur décrypté et il n'est pas accessible par JavaScript grâce à l'API Web Crypto.
La mise en œuvre de cela nécessiterait que le serveur ait accès à une version en texte brut de la clé privée de l'utilisateur. Selon l'utilisation exacte de ces clés, cela pourrait être problématique si le serveur était compromis. Au lieu de cela, vous devez envisager d'utiliser l'API Web Crypto pour générer une paire de clés privée-publique sur le périphérique de l'utilisateur et demander au navigateur d'envoyer la partie publique de cette clé au serveur. De cette façon, le serveur n'a jamais accès à la clé privée de l'utilisateur.
Maintenant, nous pouvons commencer avec la fonctionnalité de notre application Web: créer des notes chiffrées. Pour ce faire, l'utilisateur écrit une note et cliquez sur le bouton Enregistrer. Le serveur envoie "crypto.js" signé avec la clé privée du serveur (voir 2).
La signature est vérifiée à l'aide de la clé publique téléchargée en (2) avec (3) et si elle est correcte, la note est cryptée. Si "checker.js" a été modifié, SRI doit arrêter ce processus.
À moins que vous ne chargiez checker.js
à partir d'un serveur tiers non fiable, l'intégrité des sous-ressources n'est pas nécessaire dans ce scénario. Quiconque peut compromettre votre serveur ou sa connexion au client pour modifier checker.js
peut également modifier les valeurs des hachages d'intégrité de la sous-ressource afin que le navigateur accepte le script modifié sans se plaindre. Ou ils pourraient simplement modifier la page pour ne pas charger checker.js
du tout, et utilisez à la place un script complètement différent de leur propre fabrication. Quoi qu'il en soit, l'intégrité des sous-ressources n'aide pas.
- La note est renvoyée au serveur et stockée.
C'est bien tant que vous corrigez le problème que j'ai déjà mentionné avec 6, 7 et 8 afin que le serveur ne dispose pas des clés nécessaires pour déchiffrer les fichiers de l'utilisateur. Si vous êtes d'accord avec le serveur ayant les clés pour accéder aux fichiers de l'utilisateur, il n'y a aucun besoin de cryptage côté client; laissez simplement le serveur gérer le chiffrement.
- Selon la fonctionnalité requise, le serveur doit supprimer la clé privée de l'utilisateur et ne conserver que la clé publique ou non.
Ou, comme je l'ai suggéré, ne donnez tout d'abord pas au serveur la clé de l'utilisateur. En dehors de cela, cette partie est très sécurisée, car elle empêche le serveur d'accéder aux fichiers de l'utilisateur pendant que l'utilisateur n'utilise pas le site.
Une fois que l'utilisateur visite le site, le navigateur de l'utilisateur charge le code de ce serveur qui aura la possibilité d'utiliser les clés de l'utilisateur pour décrypter les notes de l'utilisateur. Donc, pour l'utilisateur moyen, accéder à ses notes sans donner à votre serveur la possibilité de les lire est impossible.
Il existe également des problèmes d'utilisation avec cette implémentation, car cela signifie que les utilisateurs ne pourront pas se connecter à leur compte à partir d'un nouveau navigateur et auront toujours accès à leurs notes. Une meilleure implémentation serait de dériver les clés de cryptage des utilisateurs à partir de leurs mots de passe en utilisant un algorithme de dérivation de clé comme PBKDF2 (disponible via l'API Web Cryptography) avec un facteur de travail élevé. Cela leur permettrait d'accéder à leurs notes depuis n'importe quel navigateur. (Mais aurait toujours les mêmes inconvénients de sécurité mentionnés dans mes commentaires ci-dessus.)
Les choses qui se démarquent vraiment pour moi sont 6 et 7. C'est de loin la chose qui fait le plus grincer des dents dans cette description.
L'intérêt de la configuration de TOFU est qu'il existe une confiance à deux sens. Bien sûr, la première utilisation de la confiance a ses propres problèmes, et je pense que vous avez déjà souligné que la plupart de ces cas, bien que moins susceptibles de se produire, sont possibles.
Mais vous me dites que le site va générer une clé privée pour moi , et me remettre cette clé cryptée, puis me donner un moyen de décrypter une clé privée clé par e-mail? m'envoyant essentiellement un moyen de décrypter ma façon de décrypter par e-mail.
Je veux dire que lorsque j'utilise un service, je recherche généralement une exposition égale. Je ne veux pas que le site Web soit le seul point d'échec pour tout ce que je fais. Cela crée également une situation où les messages destinés à moi peuvent être déchiffrés par toute personne ayant un accès administrateur aux clés privées générées par l'utilisateur. Ce qui signifie que je ne peux pas lui faire confiance.
Il sape complètement tout l'intérêt de la cryptographie asymétrique. D'autant plus que créer ma propre clé privée et envoyer le serveur à la clé publique est une affaire simple. Même pour les utilisateurs qui ne sont pas techniquement enclins, cela pourrait être inclus dans le client. Il n'y a absolument aucune raison pour qu'une autre partie crée une clé privée pour moi IMO ou pour que cette clé jamais touche Internet.
Je laisse les autres répondre aux autres points, je pense que 6 et 7 sont les plus dangereux. Sauf MITM que vous avez déjà mentionné dans le PO.
Le seul contexte où je fais de la cryptographie côté client est le hachage des informations d'identification du client (mot de passe, informations de carte de crédit, etc.) afin que le serveur ne les connaisse pas. Oui, le serveur n'a pas besoin de connaître le mot de passe simple dont il doit vérifier la validité. Le serveur applique son propre hachage sur la chaîne de mot de passe qu'il reçoit. Tant que le client envoie le même hachage, le schéma fonctionne correctement. Un mot de passe valide est authentifié lorsque le serveur est indépendant de l'algorithme de hachage de mot de passe client/client. J'avais l'habitude de crypter/décrypter les noms d'utilisateur dans le passé lorsque certains de nos clients n'obéissaient pas à notre recommandation d'implémenter le système sur https/tls. Le cryptage/décryptage des noms d'utilisateur est inutile sur la communication SSL car le serveur connaît/stocke le nom d'utilisateur. Qu'est ce que je veux dire? Je ne recommande pas la cryptographie côté client tant que le contexte ne rend pas le serveur indépendant des données secrètes du client. Il s'agit de protéger le secret client du serveur ou de protéger le serveur d'une vulnérabilité de taille sacrée. Dans tous les autres contextes, ne vous éloignez pas de la route principale (https, tls, etc.)
P.S. Certains utilisateurs sont stupides d'utiliser des mots dans leurs noms d'utilisateur dans leurs mots de passe lorsqu'il s'agit de la gestion des enregistrements, des contextes de gestion des documents. Mon attaque préférée consiste à chronométrer l'attaque sur les noms d'utilisateur, puis à utiliser les noms d'utilisateur comme une ressource précieuse sur l'attaque du dictionnaire sur les mots de passe si le chronométrage de l'attaque sur les mots de passe échoue directement. Il est intéressant de voir que peu de personnes dans le monde du développement de logiciels de gestion de documents/enregistrements se soucient du timing des attaques sur les noms d'utilisateur.