Descripción original del plugin
| Resumen | Discourse Encrypt habilita mensajes privados y cifrados entre usuarios finales. Toda la información sensible se almacena de forma segura en el servidor y solo se cifra y descifra en el lado del cliente. | |
| Enlace al repositorio | https://github.com/discourse/discourse-encrypt | |
| Guía de instalación | Cómo instalar plugins en Discourse |
Tres pasos sencillos para usar este plugin
- Habilita el cifrado y activa el dispositivo actual.
- Envía un mensaje cifrado. El destinatario también debe haber habilitado el cifrado.
Opcionalmente, puedes determinar el tiempo después del cual todo el mensaje o una publicación específica será destruido permanentemente.
- Lee mensajes secretos. El cifrado debe estar activado primero para poder leerlos.
Nota: En este ejemplo, se le pidió al usuario la clave en papel nuevamente porque el cifrado se desactivó (al cerrar sesión o desactivarlo explícitamente desde la pantalla de preferencias).
Información técnica
Este plugin ofrece a los usuarios la posibilidad de comunicarse de forma segura a través de Discourse, utilizando un esquema de cifrado de extremo a extremo. La mayor parte de la lógica del plugin se implementa en el lado del cliente y el servidor solo maneja información pública o cifrada. No cifra ningún metadato de la publicación, como los nombres de los participantes en la conversación, la hora de publicación, los «me gusta», las pequeñas acciones, etc.; las subidas están cifradas, pero su presencia no lo está porque el sistema debe asociar las subidas con las publicaciones para evitar eliminarlas.
Todo el código es de código abierto y los entusiastas de la seguridad son bienvenidos a revisarlo. Para cualquier información adicional, no dudes en contactarme o al equipo. ![]()
leer más...
Resumen
El objetivo de este plugin es ofrecer integridad y confidencialidad a los contenidos cifrados, y protegerlos contra fugas de información y usuarios no autorizados. Las siguientes secciones describen el modo de operación habitual, los algoritmos utilizados y los modelos de amenazas.
Para usar este sistema, los usuarios se registran una vez generando una «identidad de usuario» que consta de dos claves RSA de 4096 bits, una para cifrado y otra para firma. Los usuarios pueden exportar su «identidad» para guardarla de forma segura o almacenarla en el servidor, cifrada después de generar una clave en papel. Estos dos métodos sirven como copias de seguridad o se utilizan para registrar nuevos dispositivos.
Las claves en papel (inspiradas en RFC 1751 y BIP-39) son claves legibles por humanos que se utilizan para almacenar de forma segura la «identidad de usuario» en el servidor. Una clave en papel consta de 12 palabras aleatorias, seleccionadas de una lista de 2048 palabras, que ofrecen 121 bits de entropía (la primera palabra se usa como etiqueta). Para cifrar la «identidad de usuario» con una clave en papel, el sistema primero derivará la clave de cifrado utilizando PBKDF2 para estirar la clave en papel en una clave de 256 bits AES-GCM.
Crear una publicación cifrada
Para crear una nueva publicación, el usuario (su navegador) hará lo siguiente:
-
firmar el contenido de la publicación actual utilizando su clave de firma privada;
-
generar una nueva «clave de tema» (una clave AES-256-GCM) que se utilizará para cifrar la publicación, algunos metadatos de la publicación y el título del nuevo tema (si está disponible);
-
obtener las claves públicas de todos los participantes y cifrar la «clave de tema» para cada uno de ellos;
-
enviar al servidor la publicación cifrada (codificada en Base64) y las claves de tema cifradas (también codificadas en Base64) de cada participante.
El pseudocódigo para la operación de cifrado sería algo así:
signature = rsa_pss_sign(current_user.identity.sign_key.private, post.raw) # 1
topic_key = topic.key || generate_aes_256_gcm_key() # 2
encrypted_title = aes_256_gcm_encrypt(topic.title, topic_key) if topic.blank?
encrypted_post = aes_256_gcm_encrypt(signature + post.raw, topic_key)
encrypted_topic_keys = recipients.map { |r| rsa_oaep_encrypt(topic_key, r.identity.encryption_key.public) } # 3
$.put("/posts/create", { title: encrypted_title, raw: encrypted_post, keys: encrypted_topic_keys }) # 4
Leer una publicación cifrada
Para leer una publicación, el usuario (su navegador) hará lo siguiente:
-
obtener la carga útil de la publicación cifrada (texto plano de la publicación y firma) y la clave de tema cifrada;
-
usar su clave de cifrado privada para descifrar la clave de tema cifrada;
-
usar la clave de tema descifrada para descifrar la carga útil de la publicación cifrada;
-
obtener la clave de firma pública del autor y verificar la firma de la publicación.
Suite de algoritmos
Este plugin hace un uso extensivo de los primitivos criptográficos implementados en la API Web Crypto, que están disponibles en cualquier navegador moderno que Discourse admite (excepto Internet Explorer).
-
getRandomValuesPRNG: Genera claves en papel e IVs aleatorios de 96 bits. -
PBKDF2: Estira las claves en papel de 132 bits a claves de 256 bits, utilizadas para cifrar «identidades de usuario».
-
AES-256-GCM: Se utiliza para cifrar el contenido de cada publicación. También ofrece autenticación al producir una etiqueta de autenticación de 128 bits, pero este es un aspecto menos importante porque las publicaciones se verifican mediante una firma generada por el autor (véase el paso 1 anterior).
-
RSA-OAEP: Se utiliza para cifrar «claves de tema» e «identidades de usuario» para su almacenamiento seguro en el servidor. Todas las claves RSA-OAEP tienen 4096 bits de longitud.
-
RSA-PSS: Se utiliza para firmar el contenido de cada publicación para verificar su autenticidad. Todas las claves RSA-PSS tienen 4096 bits de longitud.
Primitivas
El sistema utiliza un conjunto de primitivas construidas sobre las proporcionadas por el navegador a través de la API de criptografía web.
-
encrypt y decrypt: Se utilizan para cifrar y descifrar el contenido de las publicaciones.
encrypttoma un JSON, una clave AES-256-GCM y una clave pública RSA-PSS y genera una sola cadena codificada en Base64;decrypttoma una cadena codificada en Base64 y una clave AES-256-GCM y genera el objeto JSON inicial; -
verify: Se utiliza para verificar el contenido de las publicaciones después del descifrado;
-
exportKey y importKey: Se utilizan para exportar e importar «claves de tema»;
-
exportIdentity y importIdentity: Se utilizan para exportar e importar «identidades de usuario».
Tipos de claves:
-
claves de tema (AES-256-GCM)
- se utilizan para cifrar cada publicación en un tema (las publicaciones se cifran individualmente)
- se generan por tema, en el lado del cliente por el autor original utilizando la primitiva API WebCrypto
generateKey - se cifran con la clave pública del usuario para cada usuario que tiene acceso al tema específico utilizando la primitiva API WebCrypto
wrapKey - se almacenan (cifradas) en el lado del servidor para cada usuario en
PluginStore
-
par de claves RSA (claves pública y privada) (RSA-OAEP y RSA-PSS, 4096 bits)
- se utilizan para cifrar todas las claves de tema a las que un usuario tiene acceso
- se generan por usuario en el lado del cliente por el autor original utilizando la primitiva API WebCrypto
generateKeyy se comparten entre todos los dispositivos del usuario - lado del servidor: la identidad pública se almacena tal como fue exportada por el cliente, pero la identidad privada siempre estará cifrada con la clave de contraseña
- lado del cliente: las claves pública y privada se almacenan como
CryptoKeyen IndexedDb; si no es posible, utilizaráwindow.localStorage(en Safari)
-
claves de contraseña (derivadas utilizando PKBDF2 con 128.000 iteraciones)
- se utilizan para cifrar «identidades de usuario» para su almacenamiento seguro en el servidor
- derivadas de una clave en papel (o la contraseña del usuario por razones de compatibilidad)
Modelos de amenazas
Instancia de Discourse comprometida
Un atacante que pueda inyectar código podría, en teoría, acceder a información cifrada sirviendo código malicioso, que descifra el contenido cifrado y envía las publicaciones en texto plano a otro servidor. Para hacer esto posible, es suficiente tener acceso a una cuenta de administrador y crear un componente de tema con el código malicioso.
Los mecanismos de protección predeterminados, como CSP, pueden detectar y mitigar los ataques de cross-site scripting (XSS) que también podrían representar una forma de inyectar código malicioso.
Ataque de intermediario
En los ataques de intermediario, el atacante intercepta la comunicación entre el usuario y el servidor, lo que le permite leerla o alterarla. Dado que el plugin cifra todo antes de enviarlo, un atacante no puede descifrar nada simplemente escuchando. De manera similar, como la información se autentica, el atacante no puede alterarla.
Sin embargo, el atacante podría servir código malicioso de vuelta al usuario y seguir un ataque similar al presentado en la sección anterior. Esto se mitiga parcialmente mediante HTTPS, lo que reduce considerablemente la probabilidad de ataque.
Notas
El plugin ya tiene una pequeña historia y eso se puede ver al examinar el código fuente y notar las dos implementaciones del protocolo v0 (inicial, versión alfa-beta) y v1. El protocolo v0 ya no se utiliza para cifrar nuevas publicaciones, pero se mantiene para continuar descifrando las antiguas. El nuevo protocolo incluye la autenticidad de los textos cifrados y todas las publicaciones se firman con la clave privada del autor.
