~/keydrop/security
Cómo protege KeyDrop tus secretos, qué se envía al servidor y qué amenazas no quedan cubiertas.
Seguridad
Filosofía
KeyDrop está diseñada para resolver una necesidad concreta sin pedirte nada a cambio. Sin cuentas, sin telemetría, sin historial. Lo que se puede hacer en tu navegador, se hace en tu navegador. El servidor solo aparece cuando un secreto necesita viajar entre dos personas y, cuando aparece, no puede leerlo.
Qué ocurre en el navegador
Generadores (contraseñas, passphrases, API keys, .env por variable o por stack), análisis local (redactor, escáner .env, diff seguro, Safe Share), inspector JWT, HMAC, kit de webhooks y asistente de rotación se ejecutan íntegramente en tu equipo. También el cifrado de One Time Secret, de archivos y del extremo cliente de las solicitudes ECDH. La entropía viene de la Web Crypto API y los algoritmos son AES-GCM 256, ECDH P-256, HMAC SHA-256/384/512 y PBKDF2-SHA256 con 600 000 iteraciones e IVs aleatorios por mensaje.
Análisis local de configuraciones
El redactor (/redact), el escáner .env (/env/scan), el diff seguro (/diff), Safe Share (/safe-share), el inspector JWT (/jwt), la herramienta HMAC (/hmac), el generador por stack (/env/generate), el asistente de rotación (/rotate) y el kit de webhooks (/webhooks) procesan toda la entrada en tu navegador. No hay petición al servidor en ninguna de estas páginas. Los detectores se basan en regex y heurísticas: ayudan pero no garantizan capturarlo todo, así que conviene revisar la salida antes de compartirla.
Qué se envía al servidor
Solo los sobres cifrados de One Time Secret, de los archivos cifrados, de las solicitudes de secretos y de los recibos de estado, su identificador aleatorio y la duración deseada. Nunca llega al servidor la clave de descifrado, ni la contraseña adicional, ni el contenido en claro, ni la clave privada ECDH. Las claves viajan en el fragmento # de la URL, que los navegadores nunca envían en peticiones HTTP.
Qué se almacena en Redis
Sobres cifrados de secretos, archivos y respuestas de solicitudes con TTL nativo. Metadatos opacos de las solicitudes (estado pending/submitted/consumed sin contenido). Recibos de estado cifrados con statusKey que solo indican si el secreto sigue pendiente o fue consumido. Contadores de rate limit con expiración corta. Todo se borra automáticamente al expirar o al primer revelado.
Qué no se almacena
No guardamos contraseñas, passphrases, claves API, archivos .env producidos, .env por stack, texto del redactor, contenido del escáner, comparaciones del diff seguro, salida de Safe Share, JWTs inspeccionados, payloads HMAC, snippets de webhooks, planes de rotación ni el contenido en claro de ningún secreto compartido. Tampoco guardamos las claves de descifrado, las claves privadas ECDH, las statusKey ni las linkKey: viven solo en tu navegador y en el fragmento de las URLs. Sin historial, sin IPs completas, sin user-agent y sin identificadores que permitan relacionar secretos con personas.
Cómo funciona el fragmento de URL
Los enlaces tienen la forma /s/[id]#[clave], /f/[id]#[clave], /request/[id]#[publicKey], /request/[id]/claim/[claimId]#[privateKey] y /status/[id]#[statusKey]. La parte después de # vive solo en el navegador del receptor; los navegadores no la envían al servidor. Si pierdes el enlace completo, el secreto es irrecuperable. Si lo compartes, cualquiera con la URL completa podrá leerlo una vez. Trátalo como tratarías cualquier secreto en tránsito.
One Time Secret
El cliente cifra con una clave aleatoria, opcionalmente envuelve con una contraseña vía PBKDF2 y sube el sobre. El backend hace GETDEL atómico al primer revelado real, así que un segundo intento siempre devuelve expired_or_consumed. La página solo muestra una advertencia hasta que pulsas explícitamente Revelar.
Archivos cifrados
El archivo entero se cifra en tu navegador con una clave aleatoria. El nombre, el tipo MIME y los bytes viven dentro del payload cifrado, así que el servidor solo ve un blob opaco y un id aleatorio. La descarga es única y la lectura destruye el sobre del servidor. Tamaño máximo configurable, por defecto 5 MB.
Solicitudes de secretos
Cuando creas una solicitud, tu navegador genera un par ECDH P-256. La clave pública viaja en el fragmento del enlace público que envías a la otra persona; la privada viaja en el fragmento del enlace privado que guardas tú. La persona que responde genera un par efímero, deriva una clave compartida con la pública del creador y cifra el secreto con AES-GCM. El servidor solo ve identificadores aleatorios y un blob opaco. Solo tú, con tu enlace privado, puedes descifrar la respuesta. La reclamación es única: el GETDEL atómico borra el blob al primer acceso.
Amenazas cubiertas
Evitar enviar secretos en claro por correo o chat, evitar pegarlos por accidente cuando pides ayuda en chats o foros (redactor, Safe Share), reducir el riesgo de que aparezcan en logs intermedios, no almacenar nada de forma persistente, limitar la exposición accidental al compartir configuraciones o errores y permitir que un tercero te entregue un secreto sin que el servidor pueda leerlo.
Amenazas no cubiertas
Un dispositivo o navegador comprometido, malware, extensiones maliciosas, cualquier persona con el enlace completo, capturas de pantalla, phishing y un operador del servidor que sirviera JavaScript modificado. Si eso preocupa, considera cifrado punto a punto fuera del navegador y verifica la integridad del frontend (subresource integrity, builds reproducibles, firma de releases).
Recomendaciones
Envía el enlace y la contraseña adicional por canales separados, usa expiraciones cortas siempre que puedas, verifica la identidad del destinatario, considera rotar credenciales después de compartirlas (el asistente /rotate te da el plan), y antes de pegar cualquier log o .env en un chat externo pásalo por el redactor o por Safe Share. Si despliegas KeyDrop, sirve siempre por HTTPS y, si la instancia es privada, protégela con Cloudflare Access o equivalente.
Limitaciones
No hay recuperación, ni historial, ni copia de seguridad: pierdes el enlace, pierdes el secreto. Reinicios de Redis sin AOF pueden invalidar secretos en vuelo. Los detectores del redactor, escáner, diff y Safe Share ayudan pero no garantizan capturarlo todo: revisa siempre la salida antes de compartirla. El inspector JWT decodifica pero no verifica firmas en V1. Las solicitudes de secretos solo aceptan texto en V1; archivos quedan para versiones futuras.
Operación self-hosted
Redis se sirve en red interna, sin puerto expuesto, con AOF activo y comandos administrativos (FLUSHALL, FLUSHDB, DEBUG) renombrados a vacío. La aplicación arranca en read-only filesystem con tmpfs solo para /tmp. pnpm corre con ignore-scripts y minimum-release-age de 24 horas.