Cómo funciona realmente DocuChan — y cómo comprobarlo
«Confía en nosotros» no es un modelo de seguridad. Esta página es el modelo de amenazas de DocuChan, escrito como nos gustaría que otro ingeniero lo escribiera para nosotros: qué se cifra y dónde, todo lo que guardan nuestros servidores, de qué no podemos protegerte exactamente y cómo comprobar tú mismo lo que afirmamos con las herramientas para desarrolladores del navegador.
El problema de compartir archivos de forma «segura»
Casi todos los servicios para compartir archivos dicen ser seguros, y casi todos se refieren a las mismas dos cosas: TLS durante la transmisión y cifrado en reposo en sus discos. Las dos están bien. Ninguna impide que el propio proveedor lea tu archivo, porque es el proveedor quien tiene las claves. Tu contrato, el escaneo de tu pasaporte o la versión de tu software que aún no has publicado quedan en el servidor de otro, en un formato que ese servidor puede abrir, y la página de descarga a menudo se financia mostrando anuncios al lado.
El cifrado de extremo a extremo es una garantía más sólida: el archivo se cifra antes de salir de tu dispositivo, con una clave que el servicio nunca recibe. A partir de ahí importa bastante menos lo que ocurra en el servidor —un administrador curioso, una orden judicial, una filtración del almacenamiento en la nube—, porque lo que hay allí es texto cifrado. DocuChan está construido para que esto lo imponga el diseño, no lo prometa una política.
En esta historia viajan dos cosas, y nunca toman la misma ruta. Mira los colores: la clave es azul, el texto cifrado es verde.
La clave nunca nos llega, gracias a una vieja regla de los navegadores
Cuando subes un archivo, tu navegador genera una clave AES-GCM de 256 bits aleatoria con la Web Crypto API. Esa clave termina en el enlace compartido, pero en un lugar muy concreto: el fragmento de la URL, la parte después de #.
Los fragmentos son anteriores a JavaScript: se diseñaron para saltar a un encabezado dentro de una página, así que la especificación exige que los navegadores nunca los incluyan en las solicitudes HTTP. Todos los navegadores lo cumplen. Cuando el destinatario abre el enlace, nuestro servidor ve una solicitud de /d/x7Kq0aH2 y nada más; el JavaScript que se ejecuta en su dispositivo lee el fragmento localmente y descifra allí mismo. Es el mismo esquema que usaba Firefox Send, y significa que la promesa de «sin puertas traseras» depende de por dónde viaja la clave, no de nuestras buenas intenciones.
Cómo se cifra un archivo
Los archivos se cifran en bloques de 8 MiB, sobre la marcha: se lee un bloque, se cifra, se sube y se pasa al siguiente. Tu navegador nunca tiene el archivo entero en memoria, y por eso una subida de varios gigabytes funciona en un portátil con poca RAM o en un teléfono. Cada bloque recibe su propio IV aleatorio de 12 bytes y se sella con AES-256-GCM; el formato de transmisión es simplemente [IV | ciphertext + auth tag], repetido.
GCM es un modo autenticado: cada bloque lleva una etiqueta de autenticación que el descifrado verifica. Si alguien altera un solo bit del texto cifrado almacenado —un proveedor de almacenamiento hostil, un intermediario malicioso, un disco dañado—, el descifrado falla de forma evidente y la descarga se niega a generar el archivo. Es imposible que recibas un documento manipulado sin enterarte.
El nombre del archivo recibe el mismo trato. Es un metadato que nuestro servidor no tiene por qué leer, así que se cifra en tu navegador con la misma clave del archivo antes de guardarse (como una cadena con el prefijo e2e:). De nuestro lado, tu subida es un identificador aleatorio, una etiqueta opaca y un número de bytes. El tipo MIME que guardamos es application/octet-stream: no sabemos si es un PDF o una foto.
A dónde van realmente los bytes
Un detalle que conviene explicar: los bloques cifrados se suben directamente al almacenamiento de objetos mediante URL prefirmadas. Nuestro servidor de aplicaciones autoriza la subida y entrega esas URL, pero los bytes del archivo nunca pasan por él: no hay ningún punto intermedio donde pudiera existir texto plano, ni siquiera por accidente. El proveedor de almacenamiento, a su vez, guarda un texto cifrado cuya clave no tiene. Las descargas recorren el mismo camino a la inversa y se descifran sobre la marcha a medida que llegan los bloques (en los navegadores sin File System Access API, a través de un service worker, para que los archivos grandes tampoco disparen el uso de memoria).
Todo lo que guarda nuestro servidor: la lista completa
Ser honestos sobre los metadatos importa más que cualquier eslogan sobre cifrado, así que este es el registro completo que guardamos por cada transferencia, tal como figura en el esquema de la base de datos:
- un identificador de enlace aleatorio e imposible de adivinar (que también es el nombre del objeto en el almacenamiento)
- la etiqueta cifrada con el nombre del archivo (opaca para nosotros)
- el tamaño del texto cifrado en bytes
- la fecha de caducidad, la marca de borrado tras la descarga y el contador de descargas
- si pones una contraseña: un hash verificador PBKDF2, nunca la contraseña (lo explicamos en la siguiente sección)
- la fecha y hora de creación y, si habías iniciado sesión, el identificador de tu cuenta
Para operar el servicio también guardamos durante poco tiempo hashes con sal de las direcciones IP —no las direcciones en sí— para limitar la frecuencia de las solicitudes y frenar abusos, además de los registros habituales del servidor web. Nuestra analítica está alojada en nuestros propios servidores y no usa cookies. No se carga en absoluto en las páginas de descarga, de solicitudes de archivos, de regalos, de la cuenta ni de Rooms. Una página de herramienta PDF envía una sola visita (solo la ruta) desde nuestro propio código: ningún script de seguimiento se ejecuta junto a tu documento y su Content-Security-Policy no permite ningún otro destino. En el resto elimina la cadena de consulta y el fragmento #, así que nunca puede registrar una clave. Sus eventos solo contienen recuentos aproximados y rangos de tamaño, nunca un nombre de archivo. Si mañana recibiéramos una orden judicial, todo lo que tendríamos sobre tu archivo sería la lista anterior más el texto cifrado. Podemos confirmar que se compartió algo; no podemos revelar qué.
La contraseña es una barrera, no la clave
Puedes proteger un enlace con contraseña. Conviene precisar qué hace exactamente: la clave de cifrado no sale de la contraseña; la clave está en el fragmento de todos modos. La contraseña es una barrera de acceso: nuestro servidor no entrega el texto cifrado sin ella. Eso te protege cuando el enlace se filtra por una vía por la que no se filtró la contraseña (un correo reenviado, el historial de un chat, una captura de pantalla).
Tampoco llegamos a conocer la contraseña. Tu navegador deriva de ella un verificador con PBKDF2 (SHA-256, 210.000 iteraciones y una sal aleatoria que viaja en el fragmento junto a la clave), y solo ese verificador se guarda y se compara, en tiempo constante, al descargar. Si se filtrara la base de datos, lo que se obtendría sería un hash muy costoso de romper por fuerza bruta, no tu contraseña.
La autodestrucción, en detalle
La opción «Borrar tras la primera descarga» elimina el archivo cuando esa descarga se ha completado: el navegador del destinatario confirma que lo ha guardado entero antes de que se active el borrado, así que, si la conexión se corta al 90 %, el archivo no se destruye sin haberse leído. Una descarga cancelada tampoco lo borra. Los enlaces con caducidad los elimina, al vencer el plazo, un proceso de limpieza en segundo plano. Eliminar significa que el objeto con el texto cifrado se borra del almacenamiento y el enlace deja de funcionar; como de nuestro lado solo existió texto cifrado, no queda ninguna copia en texto plano que rastrear.
Beam: cuando los archivos no deberían pasar por ningún servidor
En los envíos directos entre dispositivos (Beam), el archivo viaja por un canal de datos WebRTC directamente entre los dos navegadores. Nuestro servidor solo retransmite la negociación inicial de la conexión (SDP/ICE: coordenadas de red, unos pocos KB). Pero el cifrado DTLS integrado en WebRTC tiene un punto débil conocido: quien retransmite esa negociación podría, en principio, interponerse entre ambos extremos. Por eso Beam añade encima su propia capa de cifrado de extremo a extremo, diseñada para que nosotros también formemos parte del modelo de amenazas:
- Cada dispositivo genera un par de claves ECDH P-256 efímero y primero envía un compromiso: un hash SHA-256 que vincula su clave pública efímera con la clave de identidad persistente del dispositivo.
- Solo después de recibir el compromiso del otro dispositivo, cada lado revela esas claves, que se comprueban contra el compromiso. Después, ambas claves se incorporan a la clave de sesión AES-256-GCM y al código corto.
- Ambas pantallas muestran un código de 6 caracteres (una cadena corta de autenticación, o SAS); las personas confirman una sola vez que coincide y pueden fijar el dispositivo para que la próxima vez sea automático.
Comprometerse antes de revelar es lo que hace seguro el código corto: un retransmisor malicioso tendría que elegir sus claves sustitutas antes de ver las tuyas, así que no puede probar combinaciones hasta dar con un par de claves que muestre el mismo código en ambas pantallas. Solo tiene un intento a ciegas, con una probabilidad de acierto de aproximadamente una entre mil millones. Es el mismo esquema que usaba ZRTP para proteger las llamadas de voz. Cuando no es posible una conexión directa y un servidor de retransmisión (TURN) transporta el tráfico, ese servidor solo ve este flujo ya cifrado.
Rooms: el servidor presenta a los participantes, no a los documentos
DocuChan Rooms es un editor de documentos colaborativo, pero no un servicio que aloje documentos. Cada sala tiene dos capacidades independientes: un identificador público aleatorio que sirve como punto de encuentro y un secreto de 256 bits que va después de #key= en el enlace de invitación. La página captura ese fragmento antes de que arranque la aplicación, lo quita de la barra de direcciones y nunca lo envía a la API de Rooms. La analítica está desactivada en todas las rutas de Rooms.
Nuestro servidor guarda registros de presencia de corta duración y registros de señalización WebRTC de tamaño limitado para que los navegadores puedan encontrarse. El contenido del documento, el Markdown, los comentarios, las selecciones, las imágenes insertadas y el código de los diagramas Mermaid viajan entre los navegadores de los participantes por canales de datos WebRTC cifrados. El protocolo de la aplicación autentica el secreto de la sala y cifra sus propios mensajes con claves AES-256-GCM derivadas mediante HKDF; el servicio de señalización no puede leer las actualizaciones del documento, ni modificarlas sin que se detecte.
Quien crea la sala recibe una segunda capacidad aleatoria que solo sirve para bloquearla y decidir quién entra. Se queda en el almacenamiento del sitio del navegador de esa persona y nunca aparece en una URL de invitación ni de reanudación; el servidor solo guarda su hash SHA-256. Una sala desbloqueada admite al instante cualquier solicitud de entrada válida. Una sala bloqueada deja la solicitud pendiente hasta que el anfitrión conectado la apruebe o la rechace. El estado de bloqueo, los hashes de las capacidades de los participantes, el estado de admisión, los nombres que declara cada participante y las marcas de tiempo son metadatos de corta duración en el servidor; aun así, nunca contienen la clave ni el contenido del documento.
La recuperación cambia ese límite de almacenamiento en el propio dispositivo del usuario. Rooms guarda un documento Yjs en texto plano en el IndexedDB de ese navegador y puede guardar el secreto de la sala en el almacenamiento local, para poder recuperar el trabajo si la pestaña se cierra por accidente. Desde la página principal de Rooms se pueden eliminar copias locales concretas o borrar todas las bases de datos de Rooms conocidas y sus claves de recuperación. No hay copia de seguridad en el servidor, y DocuChan no puede recuperar una clave que el usuario pierda.
Portapapeles: conexión directa o nada
Portapapeles pasa un texto o una imagen PNG entre tus propios dispositivos. Es más estricto que Beam: los navegadores usan servidores STUN públicos solo para descubrir una ruta WebRTC directa, y Portapapeles nunca recurre a un servidor de retransmisión TURN; si no se logra una conexión directa, la transferencia falla en lugar de pasar por terceros. Nuestro servidor solo ve registros de presencia y de señalización de corta duración, que caducan solos. Cada elemento se vuelve a cifrar en la capa de aplicación con claves que acuerdan los dos navegadores y, como en Beam, comparas un código corto en ambas pantallas. Los elementos recibidos solo existen en la memoria de esa pestaña —como máximo, los 20 últimos— y desaparecen al cerrarla.
De qué no podemos protegerte
Una página de seguridad que no reconoce ninguna limitación es pura publicidad. Estas son las nuestras:
- Confías en el JavaScript que servimos. El cifrado se ejecuta en código que llega desde nuestro servidor cada vez que se carga la página. Si nos volviéramos maliciosos, o si nos obligaran o alguien comprometiera nuestros sistemas, ese código podría alterarse. Esto vale para cualquier herramienta de cifrado de extremo a extremo que funcione en el navegador —incluidas las versiones web de las aplicaciones de mensajería cifrada—, y preferimos decirlo nosotros antes de que lo descubras en un hilo de comentarios. Estamos estudiando seriamente publicar el código fuente del cliente para que cualquiera pueda revisarlo; es la forma honesta de mitigar este riesgo.
- Aún no hay una auditoría externa. El diseño sigue esquemas bien estudiados (la clave en el fragmento de la URL, como en Firefox Send; la verificación SAS al estilo de ZRTP), pero nadie independiente ha auditado esta implementación. Cuando eso cambie, enlazaremos aquí el informe.
- El enlace es el secreto. Cualquiera que tenga el enlace completo (y la contraseña, si la hay) puede descifrar el archivo. Compártelo por un canal de confianza y usa el borrado tras la descarga para que un enlace filtrado deje de funcionar después del primer uso.
- Tu dispositivo queda fuera de nuestro alcance. Un programa malicioso o una cámara que mire por encima de tu hombro burlan cualquier cifrado, incluido el nuestro.
- Clave perdida, archivo perdido. No podemos recuperar, restablecer ni regenerar la clave de un enlace: nunca la tuvimos. Es el diseño funcionando como debe, pero sorprende a quienes están acostumbrados a la opción «¿Olvidaste tu contraseña?».
¿Encontraste un fallo de seguridad? Avísanos.
Si encuentras una vulnerabilidad, de verdad queremos saberlo. No es una frase hecha: que se nos examine con lupa es la razón de ser de esta página. Escribe a abuse@docuchan.com con «Security» en el asunto (también figura en security.txt). Leemos todos los informes, respondemos y reconocemos públicamente el hallazgo a quien lo desee. Por favor, no hagas pruebas con archivos de otras personas.
— Beki, creador de DocuChan · la explicación más sencilla · pruébalo con un archivo real