Mensajes personales cifrados

Nos gustaría encargar un plugin que habilite el mensajería privada y cifrada entre los usuarios finales.

:mega: Este plugin ya existe y cuenta con soporte oficial. Consulte Discourse Encrypt (deprecated)

Actualmente, existen muy pocos espacios en línea para una comunicación segura, privada y de formato largo. Las herramientas existentes suelen ser bastante arcaicas y requieren habilidades técnicas extremas para su uso. Como resultado, la mayoría de la comunicación “privada” se realiza de forma “corta” en Telegram, Skype o WhatsApp. Además, este modo de comunicación masiva no es adecuadamente auditable, ya que se basa en herramientas de código cerrado y, a menudo, en protocolos de código cerrado.

Nos gustaría construir una solución altamente usable y auditable para el mensajería cifrada en un plugin dedicado de Discourse.

Para lograr esto, nos gustaría basarnos en los siguientes principios:

  1. Construir la solución sobre la Web Crypto API.

  2. Que el servidor almacene una clave privada cifrada y una clave pública por usuario.

  3. Que el servidor almacene una clave de conversación cifrada por participante en mensajes privados (cifrada utilizando la clave privada del usuario final).

  4. Que todo el contenido en Markdown y los títulos se almacenen en la forma cruda cifrada por mensaje (cifrado utilizando la clave de conversación).

  5. Aceptar la limitación de que el servidor sabe quién habló con quién y cuándo (solo estamos protegiendo el “qué”). Aceptar la limitación de que la búsqueda no funcionará para este contenido.

Experiencia propuesta para el usuario final

Jane va a su página de usuario y hace clic en el botón “habilitar mensajería cifrada”. Al hacer clic, se le solicita que ingrese un secreto; la interfaz de usuario explica claramente que si olvida este secreto, NUNCA tendrá acceso a sus mensajes cifrados nuevamente.

Una vez habilitada la mensajería cifrada, aparecerá un nuevo [ ] en la interfaz de creación de mensajes con el texto [ ] cifrar mensaje. Esto solo será clickable si todos los receptores tienen “habilitar mensajería cifrada” activado; si algunos no tienen claves públicas, al intentar hacer clic, diremos: “Lo sentimos, pero algunos de los participantes no tienen habilitada la mensajería cifrada”.

Jane decide que quiere hablar en privado con Pete, lo agrega a la lista de participantes, hace clic en cifrar mensaje y envía un mensaje a Pete.

Pete recibe una notificación indicando que Jane le envió un mensaje cifrado. No hay título, solo información de que existe un mensaje cifrado y enlaces al mensaje (tanto en notificaciones por correo electrónico como en la web).

Si Pete está en el dispositivo original donde habilitó la mensajería cifrada, su frase de contraseña secreta ya fue ingresada y las credenciales ya están almacenadas en IndexedDB. Si Pete está en un nuevo dispositivo, se le pedirá que ingrese su frase de contraseña secreta para activar la mensajería cifrada.

La interfaz de usuario muestra claramente una superposición o una pista visual para indicar que el mensaje está cifrado.

Una vez que comienza la comunicación entre Jane y Pete, cualquiera de ellos puede invitar a nuevas personas al mensaje, siempre que tengan claves públicas.

Pete o Jane pueden cambiar su frase de contraseña secreta cuando lo deseen; si lo hacen, la clave privada se volverá a cifrar utilizando la nueva frase de contraseña y se enviará al servidor. (Para la versión 1, no permitiremos cambios en la clave privada real, solo en la frase de contraseña).

Detalles técnicos

En ningún momento se enviará al servidor ninguna conversación privada no cifrada ni credenciales privadas. La única confianza que se depositará en el servidor es que nadie haya manipulado la carga útil de JavaScript enviada al cliente.

Los pares de claves pública y privada se generarán 100% en el lado del cliente y luego la clave privada se cifrará utilizando cifrado simétrico AES antes de entregarla al servidor para su custodia. Estiraremos la frase de contraseña utilizando una sal generada aleatoriamente que se almacenará en el servidor. El estiramiento de claves se realizará en el cliente utilizando PBKDF2 (disponible en la Web Crypto API). El par de claves privada/pública se almacenará en el cliente en IndexedDB utilizando un objeto CryptoKey no exportable. Los datos de CryptoKey exportables se eliminarán de la memoria tan pronto como sea posible.

La clave privada cifrada y la clave pública se almacenarán en campos personalizados del usuario. Solo el current_user tendrá permiso para leer/escribir la clave privada cifrada; todos los usuarios conectados podrán leer las claves públicas de todos los usuarios.

Las claves de conversación se generarán en el lado del cliente al iniciar una conversación o al invitar, y se cifrarán utilizando todas las claves públicas involucradas en la conversación. Estos datos se almacenarán en una tabla dedicada (user_id, topic_id, encrypted_conversation_key); esta fila podrá ser creada por cualquier usuario en la conversación, pero solo será legible por current_user == user_id.

Se tendrá un cuidado especial para nunca filtrar datos no cifrados al servidor, lo que incluye asegurarse de que los borradores no se almacenen sin cifrar.

Para evitar una gran clase de errores, se utilizarán nuevos elementos DOM (con nuevos IDs) para la edición y vista previa del compositor. El actual .d-editor-input solo contendrá bloques cifrados y la vista previa tradicional se deshabilitará (lo que significa que habrá dos áreas de texto en juego: cifrada y no cifrada). El cifrado se someterá a un debouncing intenso para evitar problemas de rendimiento.

Para la versión 1 (y el alcance de este elemento de trabajo), estamos dispuestos a eliminar ciertas funciones, incluido el soporte de carga, la visualización de enlaces (oneboxing) de contenido cifrado y otros casos complejos.

Estamos de acuerdo en descifrar, generar y pasar HTML a través de un filtro de lista blanca en tiempo real al renderizar este contenido privado.

Requisitos de documentación

Antes de la implementación, se creará una especificación muy detallada que incluya maquetas web y, lo más importante, una descripción general detallada de seguridad.

Requisitos de pruebas

Este plugin debe incluir un conjunto exhaustivo de pruebas unitarias y de integración basadas en el cliente y en el servidor. Si aceptan este proyecto, escriban las pruebas desde el principio; no esperen a que el proyecto esté terminado para agregarlas.

Preocupaciones

Muchas de las preocupaciones mencionadas en: https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/august/javascript-cryptography-considered-harmful/ ya no son relevantes gracias a la Web Crypto API (generación de bytes aleatorios verdaderos, almacenamiento de claves privadas, etc.). El único vector importante que existe es que el servidor envíe al cliente JavaScript malicioso. A corto plazo, se esperará que los usuarios confíen simplemente en el servidor, tal como confían en 1Password, LastPass y otras interfaces de seguridad web. A más largo plazo (v3/v4, el próximo año o después), podremos considerar enviar una extensión del navegador que coloque un candado en sitios donde todos los hashes de estabilidad de las cargas útiles de JS sean conocidos.

La API WebCrypto aún es relativamente joven, pero tiene un soporte bastante amplio; el plugin debe funcionar en todos los navegadores que soportamos.

Maquetas simples y aproximadas

* Al iniciar un mensaje, el usuario puede optar por cifrar la conversación; se mostrará una advertencia si alguno de los participantes carece de claves públicas.

* Los usuarios pueden habilitar mensajes cifrados haciendo clic en ese botón y siguiendo una interfaz de usuario que genera el par de claves.

Si los mensajes cifrados están habilitados, simplemente debe mostrar “Tiene habilitada la mensajería cifrada”.

* Algún tipo de icono o superposición indicará que un mensaje está cifrado.

Presupuesto

Se trata de una pieza muy compleja de construir que requiere una integración muy profunda y una revisión cuidadosa. Esperamos que esté extremadamente bien probada (utilizando tanto pruebas del lado del cliente como del servidor). Nuestro presupuesto actual para un MVP es de 10 000 USD.

34 Me gusta

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 Me gusta

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 Me gusta

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 Me gusta

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 me gusta

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

1 me gusta

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 Me gusta

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 Me gusta

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

29 Me gusta

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 Me gusta

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 Me gusta

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 Me gusta

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 me gusta

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 Me gusta

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 Me gusta

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