Nos gustaría encargar un plugin que habilite el mensajería privada y cifrada entre los usuarios finales.
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:
-
Construir la solución sobre la Web Crypto API.
-
Que el servidor almacene una clave privada cifrada y una clave pública por usuario.
-
Que el servidor almacene una clave de conversación cifrada por participante en mensajes privados (cifrada utilizando la clave privada del usuario final).
-
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).
-
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.


