Messages personnels cryptés

Nous souhaitons commander un plugin permettant la messagerie privée et chiffrée entre les utilisateurs finaux.

:mega: Ce plugin existe désormais et est officiellement pris en charge, consultez Discourse Encrypt (deprecated)

À l’heure actuelle, il existe très peu de plateformes en ligne pour une communication longue, sécurisée et privée. Les outils existants sont généralement assez obscurs et nécessitent des compétences techniques extrêmes pour être utilisés. Par conséquent, la majorité des communications “privées” se font de manière “courte” sur Telegram, Skype ou WhatsApp. De plus, ce mode de communication de masse n’est pas correctement auditable car il repose sur des outils propriétaires et souvent sur des protocoles fermés.

Nous souhaitons développer une solution hautement utilisable et auditable pour la messagerie chiffrée dans un plugin dédié à Discourse.

Pour y parvenir, nous souhaitons nous appuyer sur quelques principes :

  1. Construire la solution sur l’API Web Crypto.

  2. Faire en sorte que le serveur stocke une clé privée chiffrée et une clé publique par utilisateur.

  3. Faire en sorte que le serveur stocke une clé de conversation chiffrée par participant aux messages privés (chiffrée à l’aide de la clé privée de l’utilisateur final).

  4. Stocker tout le Markdown et les titres sous forme brute et chiffrée pour chaque message (chiffré à l’aide de la clé de conversation).

  5. Accepter la limitation selon laquelle le serveur sait qui a parlé à qui et quand (nous ne protégeons que le “quoi”). Accepter la limitation selon laquelle la recherche ne fonctionnera pas pour ce contenu.

Expérience utilisateur proposée

Jane se rend sur sa page utilisateur et clique sur le bouton “activer la messagerie chiffrée”. Une fois cliqué, elle est invitée à saisir un secret. L’interface explique clairement que si elle oublie ce secret, elle n’aura JAMAIS à nouveau accès à ses messages chiffrés.

Une fois la messagerie chiffrée activée, une nouvelle case [ ] apparaîtra dans l’interface de création de message avec le texte [ ] chiffrer le message. Cette case ne sera cliquable que si tous les destinataires ont l’option “activer la messagerie chiffrée” activée. Si certains n’ont pas de clés publiques, lorsque nous tenterons de cliquer dessus, nous afficherons : “Désolé, mais certains participants n’ont pas activé la messagerie chiffrée”.

Jane décide qu’elle souhaite parler en privé à Pete. Elle l’ajoute à la liste des participants, clique sur chiffrer le message et envoie un message à Pete.

Pete reçoit une notification indiquant que Jane lui a envoyé un message chiffré. Il n’y a pas de titre, juste l’information qu’un message chiffré existe, avec des liens vers le message (à la fois dans la notification par e-mail et dans la notification web).

Si Pete est sur l’appareil original où il a activé la messagerie chiffrée, sa phrase secrète a déjà été saisie et les identifiants sont déjà stockés dans IndexedDB. Si Pete est sur un nouvel appareil, il sera invité à saisir sa phrase secrète pour activer la messagerie chiffrée.

L’interface affiche clairement une superposition ou un indice visuel pour indiquer que le message est chiffré.

Une fois la communication initiée entre Jane et Pete, l’un ou l’autre peut inviter de nouvelles personnes au message, à condition qu’elles disposent de clés publiques.

Pete ou Jane peuvent modifier leur phrase secrète quand ils le souhaitent. Dans ce cas, la clé privée sera re-chiffrée avec la nouvelle phrase secrète et envoyée au serveur. (Pour la version 1, nous ne permettrons pas de modifications de la clé privée elle-même, seulement de la phrase secrète).

Détails techniques

À aucun moment, aucune conversation privée non chiffrée ni aucune identité de connexion privée ne sera envoyée au serveur. La seule confiance accordée au serveur est que personne n’a altéré le chargeur JavaScript envoyé au client.

Les paires de clés privées et publiques seront générées à 100 % côté client, puis la clé privée sera chiffrée à l’aide du chiffrement symétrique AES avant d’être remise au serveur pour sauvegarde. Nous étirerons la phrase secrète à l’aide d’un sel généré aléatoirement et stocké sur le serveur. L’étirement de la clé sera effectué côté client à l’aide de PBKDF2 (disponible dans l’API Web Crypto). La paire de clés privée/publique sera stockée côté client dans IndexedDB en utilisant un objet CryptoKey non exportable. Les données CryptoKey exportables seront supprimées de la mémoire dès que possible.

La clé privée chiffrée et la clé publique seront stockées dans des champs personnalisés d’utilisateur. Seul current_user sera autorisé à lire/écrire la clé privée chiffrée ; tous les utilisateurs connectés seront autorisés à lire les clés publiques de tous les utilisateurs.

Les clés de conversation seront générées côté client lors du début de la conversation ou d’une invitation, puis chiffrées à l’aide de toutes les clés publiques impliquées dans la conversation. Ces données seront stockées dans une table dédiée (user_id, topic_id, encrypted_conversation_key). Cette ligne sera créable par n’importe quel utilisateur de la conversation, mais ne sera lisible que par current_user == user_id.

Des précautions spéciales seront prises pour ne jamais divulguer de données non chiffrées au serveur, ce qui inclut s’assurer que les brouillons ne sont pas stockés en clair.

Pour éviter une grande classe d’erreurs, de nouveaux éléments DOM (avec de nouveaux identifiants) seront utilisés pour l’édition et l’aperçu du compositeur. L’actuelle .d-editor-input ne contiendra que des blobs chiffrés et l’aperçu traditionnel sera désactivé. (ce qui signifie que deux zones de texte seront en jeu : chiffrée et non chiffrée). Le chiffrement sera fortement désactivé (debounced) pour éviter une classe de problèmes de performance.

Pour la version 1 (et la portée de cette tâche), nous sommes prêts à éliminer certaines fonctionnalités, notamment le support des téléchargements, le oneboxing du contenu chiffré et d’autres cas complexes.

Nous acceptons de déchiffrer, de compiler et de faire passer le HTML à travers un whitelisteur à la volée lors du rendu de ce contenu privé.

Exigences de documentation

Avant la mise en œuvre, une spécification très détaillée sera créée, incluant des maquettes web et, surtout, un aperçu de sécurité détaillé.

Exigences de test

Ce plugin doit inclure un ensemble complet de tests unitaires et d’intégration basés sur le client et le serveur. Si vous prenez ce projet en charge, écrivez des tests dès le début ; n’attendez pas la fin du projet pour ajouter des tests.

Préoccupations

La plupart des préoccupations soulevées dans : https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/august/javascript-cryptography-considered-harmful/ ne sont plus pertinentes étant donné l’API Web Crypto (génération de bytes aléatoires véritables, stockage de clés privées, et la liste est longue). Le seul vecteur majeur qui existe est le serveur envoyant au client du JavaScript malveillant. À court terme, les utilisateurs seront simplement censés faire confiance au serveur, tout comme ils font confiance à 1Password, LastPass et autres interfaces de sécurité web. À plus long terme (v3/v4 - l’année prochaine ou après), nous pourrons envisager de publier une extension de navigateur qui place un cadenas sur les sites où tous les hachages de stabilité des chargeurs JS sont bien connus.

L’API WebCrypto est encore relativement jeune mais assez largement prise en charge ; le plugin doit fonctionner sur tous nos navigateurs pris en charge.

Maquettes simples et approximatives

* Lors du début d’un message, l’utilisateur peut choisir de chiffrer la conversation ; un avertissement sera affiché si l’un des participants manque de clés publiques.

* Les utilisateurs peuvent activer les messages chiffrés en cliquant sur ce bouton et en suivant une interface qui génère la paire de clés.

Si les messages chiffrés sont activés, l’interface devrait simplement afficher : “Vous avez activé la messagerie chiffrée”.

* Une sorte d’icône ou de superposition indiquera qu’un message est chiffré.

Budget

Il s’agit d’une pièce très complexe à construire, nécessitant une intégration très approfondie et un examen attentif. Nous nous attendons à ce qu’elle soit extrêmement bien testée (en utilisant à la fois des tests côté client et côté serveur). Notre budget actuel pour un MVP est de 10 000 USD.

34 « J'aime »

If the implementation is open source with the rest of discourse I could probably get our lot to throw in £1000 or thereabouts to your budget.

12 « J'aime »

Cool, yes, this will be open source under the MIT license, with an additional full license assignment to Discourse.

Regarding browser support, we want to make sure it works on every platform: Chrome + Safari is a minimum (so it works on iPhone / Android / Desktop), but my preference is that we hit Firefox as well. Edge is in the nice-to-have and IE11 is in the I don’t really care department, especially if it is missing crypto stuff.

7 « J'aime »

I’ll get the ball rolling now then, couple of questions though.

  • Is it plausible for this project to accommodate group encrypted chat, even if up to a point?
  • Would “Force every PM encrypted” option be on the cards?

Not for the initial version. Maybe for version 2 or 3, a PM between 20 users should absolutely work for V1.

Yes this is fine as an option. Basically would disallow PM to users that don’t have encryption enabled (but still allow admins to PM them unconditionally). We would need to think this through, but I don’t want to add this to the scope of the spec quite yet.

4 « J'aime »

Thanks i’m obligated to sample an arbitrarily acceptable number of our PMs and reading people’s intimate conversations if only skimming is not only something I don’t want to do it takes time I haven’t got.

a PM between 20 users should absolutely work for V1.

That’s group chat in my context (Forgot where I am :slight_smile: ) and will be fine. I’d expect strong support from our lot.

Keep in mind with this plugin it is technically impossible for you to read members private conversations provided they have a reasonably strong passphrase.

They are encrypted in the database and only decrypted client side. You would have to add a code exploit to your server for you to be able to swing reading encrypted PMs. Longer term (in v3 / v4) this code exploit would result in a giant red flag on the screen for people who install the “confirm my encrypted discourse conversations have not been exploited” browser plugin.

1 « J'aime »

Yes that’s ideal, i’m obligated to look at them because I can look at them.

1 « J'aime »

This is fascinating. It would be refreshing to do something really technically challenging. But, at least for me, the cost / benefit of taking the time to master (assuming, optimistically, that I could) all the relevant pieces doesn’t make sense right now.

That said, I just did a little reading on cryptography and have a few questions / thoughts:

  • Am I correct in understanding that you would need to generate a new conversation key every time you added or removed a member from the PM?

  • You would need some protection or warning against using other plugins as well. I can imagine a scenario in which you install this plugin, then someone writes another plugin that exposes or stores the entered text in some way before it is encrypted.

  • Wouldn’t you need to also only allow this feature on sites that force https? From the article you linked.

    You can’t simply send a single Javascript file over SSL/TLS. You have to send all the page content over SSL/TLS. Otherwise, attackers will hijack the crypto code using the least-secure connection that builds the page.

    Otherwise, in addition to the issue of the server sending malicious javascript you could also have the issue of ‘hijacking’.

  • Searchable semantic encryption is possible (by no means do I mean to imply that I understand the description in that paper). I assume you’re excluding it as the cost / benefit isn’t worth it, at least for the MVP.

  • Why not use the Signal Protocol? (javascript library).

ps. for whoever takes this on

15 « J'aime »

Great questions:

The encrypted conversation key does not need to change on add. All members of the conversation have a copy of the decrypted conversation key. So to add a member you would encrypt this conversation key using the “invitee” public key and teach the server about that.

Technically it would be correct to amend the conversation key if you are removing members from a conversation, but we can wait on v2 for that. Cause it would be a very expensive operation from the client side, entire conversation would have to be re-encrypted, or complex multi conversation key systems need to be built.

Yes, the long term V3 / V4 goal here is to have a whitelist of JS integrity hashes stored in a browser extension. That is really the only way a client can know 100% that the “server is not messing with stuff”. However for V1 we trust the server and server operator not to be malicious.

Yes, if SSL is off all bets are off. We do not expect this feature to even remotely work if SSL is off.

Yes search is off the cards for now cause it is just going to be too hard to get right for the MVP.

Maybe, not against leaning on existing standards, but I suspect the Discourse integration work here is going to be enormous regardless.

11 « J'aime »

Congrats! @dan will be taking this project! We will update here as we make progress!

29 « J'aime »

Signed up to comment on this because I’m curious on the implementation as a developer myself.

I’m personally no cryptography pro, so forgive me if these are silly questions.

Would it not be viable to encrypt the message on the server-side using the public keys of people currently in the conversation, in this case?
Essentially I guess this would lead to a double layer of encryption - the data is encrypted client-side for true E2E encryption as described with the conversation key, and then encrypted on the way out by the server to only the public keys active in the conversation. At that point, removing a member from the conversation would mean any future messages wouldn’t be decryptable by them.

I’m not too sure on the security implications of nested encryption like that, however.

My second question is how well this would work cross-device? Does it require a private key on the device, or is that handled by the passphrase encrypted private key being stored?

4 « J'aime »

Interesting idea the “double” encrypting. It does though add more responsibility to the server and a lot of this is about removing responsibility from the server so I am not sure it is a great idea protocol wise.

Cross device is handled by retrieving the encrypted private key from the server and decrypting using the key phrase.

Longer term we can introduce more “security” modes with a super strict one when say the server never even stores the private key encrypted (like say 1 password), but short term this is out of scope.

10 « J'aime »

Welcome! :discourse: :heart:

Those are not silly questions at all! If you want to dig deeper, I highly recommend Dan Boneh’s courses on Coursera.org.

This depends a lot on the particularities of the system you use. Check out double DES (i.e. vulnerable to meet-in-the-middle) and triple DES.

Let’s say Eve used to be a part of a group discussion with Alice and Bob. Even if Alice or Bob kick Eve out she will still have the conversation key and theoretically, she could intercept the messages before they get to the server and decrypt them.

If the conversation key gets compromised, the safest approach is to revoke it and use a new one for new messages. Ideally, you’d probably want to re-encrypt the old messages.

7 « J'aime »

That makes sense to me, thanks for the explanation! (@sam too)

So adding or removing users from the conversation would involve decrypting everything client-side, revoking the conversation key and generating a new one, then re-encrypting messages before sending to the server?
Plus encrypting the new conversation key with the user passphrase and storing it.

1 « J'aime »

I’ve been talking to my wife about this feature. While this may raise an eyebrow at mealtime conversation in our family, she’s a security engineer for Thales eSecurity.

She’s of the opinion that the Specification needs some use cases with bad actors and how the system should respond. For example: Malicious Admin, Malicious SysOps.

This would then give rise to a security statement that could be presented to users of the system.

9 « J'aime »

I would love it if your wife or anyone interested in this problem at her work would pop by and give feedback.

@dan has made tremendous progress here, I am testing v1 with him this week.

To answer your specific question of bad sys admins. Overall if you can commandeer the web site and deliver malicious payloads our current v1 can not offer privacy. This is by design.

Our plans for v3/v4 is to have a browser plugin that validates every single JavaScript file on the page matches a particular hash in the whitelist. That way if you would be running this plugin you would be able to disallow unrecognized JS payloads.

Overall the underlying design means that passwords can not be fished out after the fact even if the server is malicious, but it can trick the client if it wishes to decrypt arbitrary payloads. Native browser plugins or a more refined web standard longer term will help eliminate this

14 « J'aime »

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.