Secret Sharing sin dependencias: guía práctica con Web Crypto API

Jul 18, 2026 web-crypto end-to-end-encryption javascript security cryptography secret-sharing browser-api

Cómo compartir secretos de forma segura sin que nadie más pueda leerlos

Vamos directo al grano: compartir información sensible por internet es un dolor de cabeza. Envías una contraseña por email y luego te arrepientes. Usas un servicio de terceros y rezas para que no estén fisgoneando. Lo mandas por Slack sabiendo que tu empresa guarda ese historial para siempre.

¿No tendría que ser más fácil?

La buena noticia es que sí lo es. Y lo mejor: puedes construirlo enteramente en el navegador.

El problema de depender de terceros

Servicios como OneTimeSecret popularizaron la idea de enlaces que se autodestruyen. Pegas tu secreto, te dan un enlace de un solo uso, lo compartes, y listo. Pero hay un detalle que pocos usuarios consideran: ¿qué pasa realmente con tus datos?

Incluso con servicios "temporales", tu secreto en texto plano a menudo pasa por sus servidores. Prometen que no lo leen. Prometen que lo borran. Pero, ¿por qué confiar en promesas cuando puedes eliminar esa posibilidad por completo?

Aquí es donde el cifrado de extremo a extremo (E2EE) lo cambia todo. Con E2EE verdadero, el servidor simplemente no puede leer tu secreto. Solo almacena texto cifrado—bytes aleatorios inútiles sin la clave. El servidor se convierte en una caja de almacenamiento tonta, no en guardián de tus secretos.

El proceso paso a paso

Imagina que Bob quiere compartir una contraseña de base de datos con Alice. Lo interesante es esto: el cifrado ocurre en el navegador de Bob antes de que nada toque el servidor.

El flujo funciona así:

  1. Bob escribe su secreto y elige una frase de paso
  2. Su navegador deriva una clave criptográfica a partir de esa frase
  3. El secreto se cifra localmente usando esa clave
  4. Bob envía a Alice un enlace (que contiene el texto cifrado y la sal) más la frase de paso (por un canal separado)

¿Por qué canales separados? Si el enlace y la frase viajan juntos y alguien intercepta el enlace, lo tiene todo. Envía el enlace por Slack; la frase por Signal. O SMS. O paloma mensajera. El punto es: dos superficies de ataque diferentes.

Alice recibe el enlace, introduce la frase de paso, su navegador deriva la clave de nuevo, y el secreto se descifra—todo sin que el servidor vea jamás datos legibles.

La parte técnica sin morbo

Aquí es donde entra la matemática, pero no te asustes—es elegante.

Derivación de claves: PBKDF2

Los humanos somos pésimos generando claves aleatorias de alta entropía. Escribimos "contraseña123" y nos sentimos creativos. Por eso necesitamos una función que transforme frases débiles en claves criptográficas fuertes—lentamente, a propósito.

PBKDF2 (Password-Based Key Derivation Function 2) hace exactamente esto. Toma tu frase, añade una sal aleatoria (que se almacena públicamente), y la pasa por 600,000 iteraciones de hash SHA-256. Cada intento en un ataque de fuerza bruta cuesta tiempo de CPU. Con 600k iteraciones, descifrar una frase mediocre se vuelve computacionalmente inviable.

async function deriveKey(passphrase, salt) {
  const keyMaterial = await crypto.subtle.importKey(
    "raw",
    new TextEncoder().encode(passphrase),
    "PBKDF2",
    false,
    ["deriveKey"]
  );
  
  return crypto.subtle.deriveKey(
    {
      name: "PBKDF2",
      salt: salt,
      iterations: 600_000,
      hash: "SHA-256"
    },
    keyMaterial,
    { name: "AES-GCM", length: 256 },
    false,
    ["encrypt", "decrypt"]
  );
}

Cifrado simétrico: AES-GCM

Para el cifrado en sí, usamos AES-256-GCM. No es el AES teórico que quizás has escuchado—es la versión autenticada. El modo GCM no solo cifra tus datos sino que también genera una etiqueta de autenticación. Si alguien manipula el texto cifrado en tránsito, el descifrado falla con un error en lugar de producir basura silenciosamente.

También requiere un vector de inicialización (IV) único para cada cifrado. Piénsalo como la sal: público, aleatorio, y esencial para la seguridad. AES-GCM es sólido—es lo que la NSA recomienda para información clasificada.

async function encrypt(plaintext, key) {
  const iv = crypto.getRandomValues(new Uint8Array(12));
  const encoded = new TextEncoder().encode(plaintext);
  
  const ciphertext = await crypto.subtle.encrypt(
    { name: "AES-GCM", iv: iv },
    key,
    encoded
  );
  
  return { iv, ciphertext };
}

Por qué cero dependencias es un movimiento inteligente

Este enfoque es radical: sin npm packages. Sin librerías de cifrado. Sin confiar en código de terceros.

Estás usando lo que ya está en tu navegador. La Web Crypto API está estandarizada desde 2015 y viene incluida en todos los navegadores modernos. Detrás están los gigante—Apple, Google, Microsoft, Mozilla—que han invertido fuerte en implementaciones correctas y de tiempo constante.

Piensa en la superficie de ataque de aplicaciones típicas con muchas dependencias. Log4Shell pasó por una dependencia. El incidente de left-pad rompió internet por una dependencia. Cada npm install es una decisión de confianza.

Con Web Crypto API, confías en la implementación nativa del navegador. Eso es una superficie de ataque mucho menor que confiar en algún paquete npm random con 2 millones de descargas semanales.

El lado del servidor: tonto es mejor

En el backend, solo almacenas:

  • El texto cifrado (inútil sin la clave)
  • La sal (no es secreta, necesaria para derivar la clave)
  • El IV (no es secreto, necesario para descifrar)
  • Metadatos (hora de creación, expiración, número de vistas)

Configura tu Redis (o Valkey) con save "" y appendonly no—es decir, sin persistencia whatsoever. Cuando el proceso muere, todo se evapora. Configura el swap correctamente para evitar que ningún dato toque el disco.

El servidor se convierte en un almacenamiento temporal de blobs cifrados. Impone las reglas de acceso (una sola vista, expiración) pero no puede leer el contenido ni aunque quisiera.

Qué significa esto para tu infraestructura

Puedes integrar esto directamente en cualquier aplicación web. Un campo de formulario aquí, unas pocas funciones JavaScript allá, y de repente tu app tiene compartición de secretos efímera y segura incorporada. Sin cambios en el backend—tu servidor simplemente almacena y sirve blobs cifrados.

Esto es particularmente útil para:

  • Herramientas internas que necesitan compartir credenciales temporalmente
  • Sistemas de soporte al cliente que transfieren datos sensibles
  • Flujos de trabajo de desarrollo compartiendo API keys o contraseñas de base de datos

Los secretos quedan criptográficamente aislados de tu infraestructura. Aunque alguien comprometa tu base de datos, obtiene texto cifrado que no puede descifrar sin la frase de paso.

Los compromisos valen la pena

¿PBKDF2 con 600k iteraciones es más lento que Argon2? Sí. Argon2, el estándar moderno para hash de contraseñas, aún no está disponible en Web Crypto API. Pero considera el contexto: son secretos de un solo uso, generalmente vistos en minutos. El costo computacional es aceptable.

El compromiso—KDF ligeramente más débil a cambio de cero dependencias—vale la pena para la mayoría de casos. No estás protegiendo códigos de lanzamiento nuclear. Estás compartiendo una contraseña de base de datos que de todas formas cambiarás mañana.

Construye, no confíes

La belleza del E2EE es que no necesitas confiar en el operador del servicio. Puedes auditar el código. Puedes auto-hospedarlo. Puedes verificar que el servidor jamás ve texto plano.

En un mundo donde las filtraciones de datos son noticia diaria, construir sistemas donde el servidor es criptográficamente ciego no es solo ingenioso—es responsable.

Así que la próxima vez que necesites compartir un secreto por internet, recuerda: la mejor forma de proteger datos es nunca dejarlos salir de las manos del usuario sin cifrar. Y con Web Crypto API, tienes las herramientas para hacerlo posible, justo en el navegador.


¿Listo para implementar? La Web Crypto API está disponible en todos los navegadores modernos. Sin instalación, sin pasos de build, sin dependencias—solo criptografía nativa del navegador esperando ser usada.

Read in other languages:

FR DE DA ZH-HANS EN