Seguridad

    Por qué las URL filtran datos sensibles (y cómo evitarlo)

    El historial, los registros, las CDN y el Referer guardan la URL completa, incluso con HTTPS. Dónde se filtra y cómo diseñar URL que no importe filtrar.

    W. Miller · Actualizado el · Sobre LinkPilot

    Una URL parece privada porque solo deberían tenerla las personas a las que se la envías. En la práctica, una URL es uno de los datos que más se registran en la web: la anotan, la copian y la reenvían sistemas que nunca te pidieron permiso y que no puedes auditar. Aquí verás exactamente dónde se filtra y cómo diseñar enlaces que aguanten esa filtración. Es también la razón por la que un generador de enlaces secretos de un solo uso guarda la clave de descifrado después del # del enlace: esa parte nunca llega al registro de un servidor.

    Dónde se filtra

    • Historial del navegador. Cada URL que visita el usuario se guarda en local y puede sincronizarse con otros dispositivos y con el servicio de sincronización del fabricante del navegador.
    • Registros de acceso del servidor HTTP. La mayoría de los servidores registran por defecto la ruta completa de la petición y la cadena de consulta. Cualquiera con acceso a los registros tiene la URL.
    • Proxies inversos y CDN. Cloudflare, Fastly, tu balanceador de carga: todos registran la URL, cada uno con su propia política de conservación.
    • Canales de agregación de registros. Los registros de acceso rara vez se quedan en una sola máquina; se envían a servicios centralizados de registro y monitorización, lo que multiplica el número de sistemas que guardan la URL.
    • Encabezados Referer. Cuando la página enlaza a un recurso de un tercero, el navegador puede enviar la URL de origen en el encabezado Referer. Los scripts de analítica, los vídeos incrustados y los píxeles publicitarios la reciben.
    • Extensiones del navegador. Las extensiones con permiso para «leer el historial de navegación» ven todas las URL, incluida la parte que va después del #.
    • Sincronización de marcadores. Las URL guardadas como marcadores se sincronizan entre los dispositivos del usuario y con los servidores del fabricante del navegador.
    • Generadores de vistas previas de URL. Slack, Teams, iMessage, LinkedIn y los clientes de correo abren las URL para mostrar vistas previas, sin avisar y en el momento en que se envía el mensaje.
    • Proxies corporativos y pasarelas de correo. Muchas organizaciones inspeccionan, reescriben y registran todas las URL que cruzan su perímetro.

    ¿HTTPS mantiene la URL en privado?

    No. HTTPS cifra la URL en tránsito y nada más; los dos extremos la siguen guardando. Merece la pena precisarlo, porque «la conexión está cifrada» se malinterpreta a menudo como «la URL es privada».

    TLS cifra la ruta y la cadena de consulta en tránsito: alguien que observe la red entre el navegador y el servidor no puede leer /reset?token=.... Lo que TLS no oculta es el nombre del host de destino (visible en la resolución DNS y, hoy en día, en el campo SNI de TLS en la mayoría de las conexiones), ni nada de lo que ocurre en los extremos: el navegador sigue guardando la URL completa en el historial y el servidor sigue escribiéndola en su registro de acceso. El cifrado en tránsito protege el cable, no los dos extremos, y es en los extremos donde se filtran las URL.

    Un ejemplo completo: la vida de una URL

    Sigamos una sola URL con datos confidenciales, por ejemplo un enlace para restablecer la contraseña, https://example.com/reset?token=8f3a91..., desde que se envía hasta que se hace clic en ella:

    1. El proveedor de correo guarda el mensaje (con la URL) de forma indefinida, y su escáner de seguridad puede abrir la URL al entregarlo.
    2. La pasarela de correo del destinatario reescribe y registra la URL, y después la vuelve a abrir cuando se hace clic.
    3. El DNS resuelve example.com: el nombre del host (no la ruta) queda ya en los registros del resolvedor.
    4. El servidor web escribe la línea completa de la petición en su registro de acceso. Una entrada con el formato «combined» por defecto de nginx o Apache tiene este aspecto:
    203.0.113.7 - - [14/Jul/2026:09:12:45 +0000]
      "GET /reset?token=8f3a91... HTTP/1.1" 200 5123 "-" "Mozilla/5.0 ..."
    
    1. La CDN que tiene delante registra la misma URL con su propia política de conservación.
    2. El script de analítica de la página guarda la URL de la página, con la cadena de consulta incluida, en un sistema de terceros, salvo que se haya configurado de forma explícita para quitarla.
    3. El navegador guarda la URL en el historial y la sincroniza.

    Un envío, siete copias, en manos de al menos cinco organizaciones. Nada de esto ha necesitado un atacante: es la web funcionando tal como se diseñó.

    El encabezado Referer en la práctica

    Si la página donde se muestra tu secreto enlaza a cualquier recurso externo (una fuente de una CDN, un script de analítica, una imagen incrustada), el navegador puede enviar la URL de la página en el encabezado Referer a ese origen.

    Los navegadores actuales usan por defecto strict-origin-when-cross-origin (MDN: Referrer-Policy), que recorta el referente entre orígenes distintos y deja solo el origen (https://example.com/ en lugar de la ruta completa). Es un avance real, pero no algo en lo que confiar: las peticiones del mismo origen siguen llevando la URL completa, una política definida en la página puede relajar el valor por defecto, y los clientes antiguos y las webviews integradas no se comportan todos igual.

    Dos medidas prácticas:

    <!-- Strip the referer for outbound requests on the page -->
    <meta name="referrer" content="no-referrer" />
    
    Referrer-Policy: no-referrer
    

    La versión con encabezado se aplica a todo el sitio; la metaetiqueta, a esa página concreta. Usa las dos para tener varias capas de defensa. Mejor aún: sirve las páginas con datos confidenciales sin ningún recurso de terceros, para que no haya nadie a quien enviar un referente.

    ¿Es más seguro poner el token en la ruta, en la cadena de consulta o después del #?

    Solo la parte del enlace que va después del # queda fuera de la petición, y por eso solo esa parte queda fuera de los registros del servidor; la ruta y la cadena de consulta son equivalentes. El lugar de la URL donde pones un token cambia quién lo ve:

    Ubicación¿Se envía al servidor?¿En los registros del servidor?¿En el Referer?¿En el historial del navegador?
    Ruta (/s/abc123)SíSíDepende de la políticaSí
    Cadena de consulta (?token=abc123)SíSíDepende de la políticaSí
    Fragmento (#abc123)NoNoNoSí

    El fragmento es realmente distinto: según la RFC 3986, sección 3.5, se procesa en el cliente y nunca se incluye en la petición HTTP. Por eso los servicios para compartir texto con cifrado de extremo a extremo ponen ahí la clave de descifrado: el servidor nunca la recibe, literalmente. Aun así, no es invisible: el historial del navegador, las extensiones y cualquier JavaScript que se ejecute en la página pueden leerla. Elegir entre ruta y cadena de consulta, en cambio, no cambia nada en cuanto a privacidad: las dos acaban en los mismos registros.

    El problema de los tokens para restablecer contraseñas

    Los tokens confidenciales en las URL son una clase de vulnerabilidad reconocida. La CWE-598 de MITRE («Use of GET Request Method With Sensitive Query Strings») trata justo esto: los datos confidenciales de la cadena de consulta acaban en registros, historiales y referentes, aunque el transporte vaya cifrado. Por eso los procesos de restablecimiento de contraseña bien hechos nunca dependen solo de que la URL sea secreta. Hacen que el token sea de un solo uso, de corta duración y vinculado a una acción posterior (definir una nueva contraseña), de modo que un token recuperado de un registro tres semanas después no sirva para nada.

    Esa es la lección general: normalmente no puedes impedir que una URL acabe en los registros, pero sí puedes hacer que la URL registrada no sirva para nada cuando alguien la lea.

    Cómo diseñar URL que no importe filtrar

    Da por hecho que cada URL que generes acabará apareciendo tarde o temprano donde no debería, y diséñala para que eso no importe:

    • Un solo uso. Destruye el secreto después de la primera lectura correcta, para que una URL filtrada ya no funcione cuando aparezca en el historial de alguien.
    • Expiración corta. Limita el tiempo durante el que la filtración importa. Una URL que funcionó durante cinco minutos vale mucho menos que una que funcionó durante una semana.
    • Frase de contraseña por otro canal. Exige un secreto que nunca haya estado en la URL. Así, la URL sola (lo único que tienen los registros) no basta.
    • Separa la consulta de la entrega del contenido. Devuelve los metadatos con GET y entrega el contenido solo con un POST explícito, para que los bots de vista previa y los escáneres no puedan consumir el secreto.
    • Ningún recurso de terceros en las páginas con datos confidenciales, además de Referrer-Policy: no-referrer y <meta name="robots" content="noindex">.
    • Oculta las rutas secretas en tus propios registros. En nginx, transforma la URI de la petición antes de registrarla (consulta la referencia de ngx_http_log_module para log_format):
    map $request_uri $loggable_uri {
        ~^/s/   "/s/[redacted]";
        default $request_uri;
    }
    log_format redacted '$remote_addr - $remote_user [$time_local] '
                        '"$request_method $loggable_uri" $status';
    access_log /var/log/nginx/access.log redacted;
    

    Solo controlas tus propios registros (las copias de la CDN, del proxy y del navegador siguen ahí), y precisamente por eso los tres primeros puntos de esta lista son los que más peso tienen.

    Este modelo de amenazas explica por qué los enlaces secretos de LinkPilot funcionan como funcionan: se parte de que la URL se acabará filtrando, así que la URL sola nunca basta. Un GET devuelve solo metadatos; para obtener el contenido hace falta un POST explícito para mostrarlo, que puede estar protegido por una frase de contraseña cuyo hash SHA-256 se calcula en el cliente. Los secretos destruidos, expirados o revocados devuelven HTTP 410 con el contenido sobrescrito de forma irreversible, las respuestas que muestran el contenido llevan Cache-Control: no-store y los eventos de visualización registran un hash de la IP con una sal que cambia cada día, en lugar de la dirección sin procesar. Y un límite que reconocemos abiertamente, indicado en la página de arquitectura de seguridad: la clave de descifrado va en la parte del enlace que va después del #, así que hay que tratar el enlace completo como el secreto, que es justo el riesgo del que trata este artículo. Una expiración corta, la destrucción tras la lectura y una frase de contraseña enviada por otro canal son lo que limita el daño cuando se filtra un enlace.

    Actualización del 29 de septiembre de 2026: este párrafo decía originalmente que LinkPilot no ofrecía cifrado de extremo a extremo. Ahora cifra el texto en el navegador, archivos adjuntos incluidos.

    Pruébalo gratis, sin registro

    Mensaje que se autodestruye

    Envía una nota que desaparece después de leerse una vez.

    Abrir herramienta

    Límites

    El diseño de las URL no lo arregla todo. No controla lo que hace el destinatario después de una lectura legítima, no puede eliminar las copias que ya tienen terceros y no salva una URL que lleva datos personales a la vista: si la dirección de correo de un usuario está en la cadena de consulta, ninguna expiración evita que quede registrada. La regla que se mantiene es esta: no pongas nunca en una URL datos que tengas que eliminar más adelante, porque eliminar una URL de todos los sitios donde se ha copiado es prácticamente imposible. Diseña, en cambio, URL que no sirvan para nada cuando se filtren.

    Referencias

    Herramientas para este artículo

    Preguntas frecuentes

    LinkPilot envía un enlace secreto que desaparece en cuanto se abre.

    Cifrado en tu navegador, se muestra una vez y después se destruye. Sin cuenta.

    Crear un enlace secreto

    Sigue leyendo