Cuando un administrador tiene que pasar a un compañero la contraseña de una base de datos, una clave de API o un código de recuperación, lo más rápido es pegarla en el chat o en el correo. El mensaje se puede buscar. Se sincroniza en todos los dispositivos. Seis meses después sigue ahí. Al pasar a un «enlace de un solo uso», mucha gente escribe la clave de descifrado como ?key= detrás del identificador del texto cifrado. Entonces el servidor, el proxy inverso y el registro de acceso ven la clave. El cifrado queda a medias.

Este texto no es un recorrido por los botones de un formulario que se autodestruye. Eso pertenece a la página de la herramienta. La pregunta es más estrecha: cuando envías una contraseña una sola vez, por qué la clave de descifrado va detrás del # en la URL, qué deja de ver el servidor por ese motivo y dónde se acaba esa protección. En una nota anterior se explica cómo comprobar en Red que el texto plano no se puede subir. Aquí el mismo panel responde a otro objeto: en qué trozo de la URL vive la clave. El enlace de un solo uso de MyPassGen trabaja dentro de ese límite: AES-256-GCM termina en el navegador, el servidor solo guarda texto cifrado, la clave se queda detrás de #, y crear o leer no pide cuenta.

Primero, dos trozos distintos de la URL

Lo que va detrás de ? es la consulta. Entra en la línea de petición HTTP y acaba en los registros de acceso del otro extremo. Lo que va detrás de # es el fragmento. El protocolo lo reserva al cliente. Si pones la clave en el trozo equivocado, cualquier promesa posterior de «conocimiento cero» se cae.

Tres formas de enviar la misma contraseña

Toma una contraseña de prueba desechable, orange-lake-7. Se puede enviar de al menos tres maneras. La diferencia no está en el eslogan de la página. Está en si el texto plano se convirtió primero en texto cifrado, si la clave entró en HTTP y si dentro de medio año todavía puedes buscar el original.

Método Qué recibe el servidor o el chat Qué puedes encontrar después
Pegar la contraseña en el chat El texto plano completo El original, y se puede buscar
Enlace cifrado, clave escrita como ?key= Texto cifrado y clave (ambos en la petición) La clave en los registros; la URL completa en el chat
Enlace cifrado, clave detrás de # El servidor solo ve un id de texto cifrado; el chat puede guardar la URL entera Sin clave en los registros del servidor; el enlace completo puede seguir en el historial

La tercera fila solo responde «la máquina que hospeda el texto cifrado no debería recibir la clave». No responde «¿el chat guardará la URL entera?». El enlace completo es una credencial: quien copie la dirección con # puede descifrar en el navegador. Poner la clave detrás de la almohadilla resuelve la confianza en el servidor. No resuelve un enlace reenviado.

El texto corto es la forma adecuada para este camino. MyPassGen limita una nota a 32 KB: contraseñas, trozos de clave y una instrucción breve. Un paquete de certificados, una hoja exportada o cualquier cosa por encima de ese tamaño corresponde a Cifrar: cifra en local a .lock / .enc y envía la contraseña por otro canal. El caso de archivo está en quién puede leer el texto plano antes de subirlo a la nube.

Qué viaja de verdad en HTTP

RFC 3986 §3.5 llama identificador de fragmento a lo que va detrás de #. Señala una posición secundaria que el cliente interpreta después de que llegue el recurso principal. El navegador pide primero por ruta y consulta. Cuando el documento aterriza, el fragmento decide un desplazamiento o se entrega al script de la página. La petición HTTP no necesita ese trozo.

La semántica HTTP vigente es más estricta. RFC 9110 §7.1 dice que el URI de destino excluye el componente de fragmento de la referencia, porque el fragmento queda reservado al procesamiento en el cliente. Una origin-form válida en la línea de petición es la ruta más una consulta opcional. No hay producción de #. En la barra de direcciones puedes ver s.html?id=abc#la-clave. La petición del documento que sale de la pestaña debería ser GET /es/s.html?id=abc. El trozo detrás de la almohadilla se queda en esta máquina.

Referer también recorta el fragmento. MDN Referer indica que la cabecera puede llevar origen, ruta y consulta. No debe llevar un fragmento de URL ni un nombre de usuario y contraseña. La Referrer Policy del W3C vacía el fragmento en el paso de preparación para usarlo como referrer. Así que «pon la clave detrás de # y el servidor que guarda el texto cifrado no la recibe por HTTP» es comportamiento del protocolo. No es una promesa de un proveedor.

El signo de interrogación y la almohadilla no se intercambian

Según el estándar de URL de WHATWG, al abrir http o https, lo que va detrás de ? entra en la línea de petición. Si escribes s.html?id=abc&key=la-clave, la clave aparece en registros de acceso, proxies inversos y algunos registros de CDN. Qué parámetros de seguimiento puedes quitar al compartir un enlace público es otro problema: véase qué parámetros de URL quitar. Una clave de descifrado no debe pasar nunca a la consulta.

Hay además una trampa de codificación. %23 es la codificación por ciento de #. Un %23 escrito en la ruta o en la consulta sí se envía al servidor y se decodifica como un carácter almohadilla literal. No es un fragmento. La clave debe ir detrás de un # sin codificar en la barra de direcciones, no detrás de un %23 metido en la ruta.

Qué no aparece en los registros del servidor

Un envío de un solo uso se parte en dos pasos. Al crear, la pestaña genera una clave aleatoria de 32 bytes, cifra el texto plano con AES-256-GCM (IV de 12 bytes, guardado con el texto cifrado) y hace POST del texto cifrado, un TTL y un número de lecturas. Al leer, el script toma la clave de location.hash, pide al servidor solo el texto cifrado y descifra en este dispositivo. El enlace tiene la forma s.html?id={id}#{key}: la consulta solo lleva el id; la clave vive solo detrás de la almohadilla.

NIST SP 800-38D define GCM como cifrado autenticado: si el texto cifrado se altera, el descifrado debe fallar. No obtienes un cuerpo ilegible que puedas tomar por la nota. RFC 5116 AEAD_AES_256_GCM usa una clave de 32 bytes, un nonce de 12 bytes y una etiqueta de autenticación de 16 bytes. AesGcmParams en MDN recomienda también un IV de 96 bits, y hay que usar un IV nuevo en cada cifrado con la misma clave. Esos números se pueden contrastar en una implementación.

Por tanto, los registros del servidor sí deberían mostrar: el id del texto cifrado, los campos JSON de creación ciphertext / ttl_hours / max_reads, y el GET posterior del texto cifrado. No deberían mostrar: el texto plano, la clave de 32 bytes ni el trozo detrás de # en la barra de direcciones. El TTL puede ser 1 hora, 24 horas o 7 días, o la nota puede destruirse solo por número de lecturas. Las lecturas van de 1 a 10. Son restricciones de la página de creación. No son un SLA inventado a posteriori.

El protocolo no vigila lo que el script de la página reporta por su cuenta

Que HTTP no envíe el fragmento no significa que la clave no salga nunca de este ordenador. El script de la página puede leer window.location.hash y hacer fetch por su cuenta. Si analytics reporta el location.href completo como URL de página, el trozo detrás de la almohadilla entra en el registro de estadísticas. Mira la petición de analytics en Red. No te quedes en «HTTP no lo envía».

El enlace completo sigue siendo una credencial

Poner la clave detrás de # solo la oculta al servicio HTTP que hospeda el texto cifrado. Los chats, el cliente de correo, el historial del navegador y las pestañas sincronizadas guardan la URL que vio el usuario, almohadilla incluida. Quien tenga el enlace completo puede abrir la página de lectura y descifrar. Es una URL al portador: el propio enlace es la credencial. No hay una segunda «contraseña que solo conoce el destinatario» salvo que la añadas a mano.

En una entrega más sensible puedes partir las dos mitades: un mensaje lleva solo s.html?id=…; otro canal —una llamada, una conversación en el escritorio u otro mensajero— lleva solo el trozo detrás de #. Ninguna mitad descifra sola. Es un patrón de uso, no el valor por defecto del producto. Por defecto se emite un enlace completo para que la otra persona lo abra una vez.

Tampoco puede impedir una captura de pantalla, una copia o el reenvío del texto plano. Un enlace de un solo uso reduce las aperturas repetidas y el texto plano que vive mucho tiempo en el servidor. Si el destinatario hace una captura o pega la nota en otro canal, el protocolo no ayuda. Úsalo cuando confías en que la otra persona leerá y parará, y cuando el secreto se puede rotar y volver a enviar. Una contraseña maestra de larga duración, una clave privada o una frase semilla no deberían viajar así.

Compruébalo en la pestaña Red

Los pasos de abajo no dependen de una promesa de marca. Usa una frase de prueba desechable. No practiques con la contraseña real de una base de datos.

  1. Abre las herramientas para desarrolladores, pestaña Red, y activa «Conservar registro». Abre la página de creación de un solo uso, escribe la frase de prueba orange-lake-7, pon las lecturas a 1 y genera el enlace.
  2. Inspecciona el POST de creación. El cuerpo debería tener un campo de texto cifrado. No debería contener orange-lake-7. Anota la URL generada: debería verse como s.html?id=…#…, con un trozo detrás de la almohadilla y solo id detrás del signo de interrogación.
  3. Pega el enlace completo en una pestaña nueva. La línea de petición del documento debería ser …/s.html?id=…. No debería mostrar # ni la clave que va detrás.
  4. Luego mira las llamadas Fetch / XHR siguientes. La petición del texto cifrado debería llevar el id, no la clave. Analytics no debería llevarse el trozo detrás de # ni la frase de prueba original.
  5. La página debería descifrar la frase de prueba. En otra pestaña, pega solo la parte anterior a la almohadilla. La página debería decir que el enlace está incompleto, no imprimir el original.
  6. Recarga el primer enlace, ya leído. Deberías ver destruido o caducado, no el original. Para enviar de nuevo, crea un enlace nuevo. No hay copia de seguridad en texto plano en el servidor.

El enlace de un solo uso de MyPassGen trabaja dentro de ese límite: AES-256-GCM, tope de 32 KB de texto plano, clave detrás de #, y sin cuenta para crear o leer. Lo que debes fiarte sigue siendo Red y «quita la almohadilla y no descifra», no las palabras «conocimiento cero» de la página. La lista completa de restricciones está en las notas de seguridad.

Lo que # no protege

El correo de empresa y algunos mensajeros despliegan una vista previa: un backend hace GET de la página una vez para tomar el título o un extracto. Una vista previa que pide HTML y no ejecuta el script de la página no ve el fragmento y, por lo general, tampoco llama a la API de texto cifrado. Un escáner que sí ejecuta el script puede completar una lectura antes que el destinatario, y entonces el texto cifrado se destruye. No es un fallo del protocolo. Es el coste de «abrir la página de lectura pide el texto cifrado en un script». Si la otra persona dice que el enlace ya no está, pregunta si una vista previa ganó la carrera. Para enviar de nuevo, crea un enlace nuevo.

El historial del navegador, los informes de fallo y algunas cuentas sincronizadas guardan la URL completa. Dejar un enlace de un solo uso en la barra de direcciones es dejar la clave en el historial local. Después de leerlo, no pegues la dirección con # en una plantilla de ticket y no la guardes como «copia permanente». Un enlace perdido no se recupera. No hay un restablecimiento de soporte, y el pie no publica un buzón que pueda restaurar el texto cifrado.

Cuando envías un texto más largo, el enlace y el cuerpo son otro trabajo. Quita utm_source y los identificadores de clic con la tabla de parámetros; enmascara teléfonos y documentos en el ticket con qué campos enmascarar antes de enviar. Un enlace de un solo uso responde «el servidor no puede ver la clave ni el texto plano». No responde «esta conversación no debería llevar un número de cuenta completo».

Errores frecuentes

«La clave está en la URL, así que el servidor tiene que verla.» Depende del trozo. Detrás de ?, sí. Detrás de #, RFC 9110 dice que la petición del documento y Referer deben omitirla. Lee la línea de petición en Red. No discutas por intuición.

«Si va detrás de #, el historial del chat está a salvo.» El chat guarda la cadena que pegaste. Que falte la clave en el registro del servidor no significa que falte para todo el canal, ni para quien desbloquee ese teléfono más adelante.

«Un enlace que se autodestruye impide las capturas.» No. Reduce las aperturas repetidas y el texto plano en el servidor. No reduce una copia ni una foto de la pantalla. Envía solo una contraseña que puedas rotar.

«El destinatario tiene que registrarse para leerlo.» En este sitio, crear y leer funcionan sin cuenta. La página de lectura es pública para el destinatario. «Se abre sin iniciar sesión» no es «quien tiene el enlace debe entrar primero».

Por dónde empezar

Empieza por la contraseña que ibas a enviar hoy. Recorre primero los seis pasos de arriba con una frase de prueba desechable: la petición de creación no lleva el original, la línea de petición de la página de lectura no lleva la clave detrás de #, quitar la almohadilla no descifra, y una segunda apertura muestra destruido. Después envía el secreto de verdad.

Genera la contraseña real en esta máquina con el generador de contraseñas. El modo aleatorio admite de 6 a 128 caracteres, 16 por defecto; por debajo de 8 debe tratarse como débil. Envía el enlace completo. En una entrega más sensible, parte la almohadilla y la ruta en dos canales. No pegues la misma contraseña en el chat como «copia de seguridad». Después de ese envío ya puedes responder al título: la clave de descifrado va detrás de # para que el servicio HTTP que hospeda el texto cifrado no la vea. No protege el historial del chat ni una captura de pantalla.

Preguntas frecuentes

Si la clave va detrás de #, ¿el servidor de verdad no la ve?

Según RFC 9110, el URI de destino de la petición del documento excluye el fragmento. Según MDN, Referer también lo omite. Abre Red: la línea de petición de la página de lectura no debería mostrar la clave detrás de la almohadilla. Si el script de la página reporta location.href por su cuenta, eso es una fuga de implementación: búscalo en la lista Fetch.

¿El destinatario necesita una cuenta para abrir el enlace?

No. Crear y leer funcionan sin cuenta. El destinatario abre la página de lectura y descifra. Tras el número de lecturas configurado, o al cumplirse el TTL, una apertura posterior muestra destruido o caducado, no el original.

Solo copié la parte anterior a la almohadilla. ¿Puedo abrirlo igual?

No puedes descifrar. Sin la clave detrás de #, la página de lectura debería decir que el enlace está incompleto. Pide al remitente la URL completa. Un enlace perdido o ya destruido no se recupera. Para enviar de nuevo, crea uno nuevo.

¿Esto impide que el chat guarde un registro?

No. Los chats suelen guardar la URL entera que pegaste, clave incluida. La almohadilla solo deja la clave fuera del servicio HTTP que hospeda el texto cifrado. Si no quieres que un canal de larga vida tenga la credencial, no pegues el enlace completo en ese canal.