# Por qué las credenciales en cadenas de consulta se filtran antes de que alguien las lea

Un secreto en una URL ya ha dado un paseo de lo más pintoresco. Puede haber pasado por una biblioteca cliente, un proxy, un registro de acceso, un sistema de trazas, una base de datos de historial del navegador y un CSV exportado antes de que la API lo reciba. HTTPS protege esa solicitud en tránsito. No hace que todas las máquinas y servicios que manejan la URL la olviden.

Por eso un agente autónomo puede evitar ver un token y aun así provocar una filtración de credenciales. Si el agente pide a una puerta de enlace de acciones que llame a `https://api.example.test/v1/builds?access_token=...`, la puerta puede mantener el token fuera de la transcripción del agente y aun así construir una URL que otros sistemas registran por costumbre. El secreto salió del alcance del modelo, pero terminó en un conjunto mucho mayor de lugares.

La solución es menos llamativa que buscar secretos. Limita la URL a la identidad del recurso y a filtros habituales. Coloca las credenciales en el encabezado de la solicitud o en el cuerpo cuando el protocolo lo requiera. Luego haz que el componente que guarda el secreto lo inyecte en el último momento posible. Así separas una solicitud que se puede nombrar con seguridad, repetir en una prueba e incluir en un registro de auditoría de una solicitud que lleva autorización.

## Una URL es un registro, no solo una ruta

Una URL está pensada para copiarse, mostrarse, compararse, almacenarse en caché, guardarse como marcador y registrarse. Esas propiedades la hacen útil para identificar recursos y pésima para transportar material bearer. Un parámetro de consulta pasa a formar parte del destino de la solicitud, que muchas capas tratan como datos operativos rutinarios.

CWE-598 denomina a esta debilidad «Use of HTTP Request With Sensitive Query String». Sus notas de contexto mencionan las vías habituales de escape: historial del navegador, encabezados Referer, registros web y otras fuentes de registro. La mitigación es igual de sencilla: envía la información sensible en encabezados o en el cuerpo de la solicitud. Ese consejo no prohíbe usar GET. Advierte que poner un secreto en la URI aumenta el número de personas y sistemas que pueden recuperarlo.

RFC 9110 hace la misma distinción en sus consideraciones de seguridad. Advierte que los campos de consulta de una URI construidos a partir de la entrada del usuario pueden contener datos sensibles y dice que una URI distinta generada por el servidor puede quitar datos sensibles de enlaces posteriores. Para las credenciales de API iría un paso más allá: no crees una URI que contenga secretos desde el principio. Sustituirla después deja copias atrás.

A veces se llama a un parámetro de URL «solo una clave API» o «solo un token de corta duración». Ninguna de las dos etiquetas cambia su superficie de exposición. Un token de corta duración puede seguir siendo válido cuando un agente de envío de registros lo reenvíe. Una clave API puede autorizar un endpoint limitado, pero ese endpoint puede bastar para leer datos, generar costes u obtener una credencial mejor. Trata los datos de autorización como secretos hasta que el servicio que los emitió indique lo contrario.

## Los registros de acceso conservan la parte que la gente olvida

La mayoría de los registros de acceso HTTP incluyen el método, el destino de la solicitud, el estado, el tamaño y los tiempos porque los operadores necesitan esos campos para diagnosticar el tráfico. El destino de la solicitud incluye la ruta y la cadena de consulta. Una línea típica tiene este aspecto:

```
203.0.113.24 - - [14/Jun/2026:12:42:18 +0000] "GET /v1/builds?access_token=sk_live_example HTTP/1.1" 200 481
```

La parte dañina no es una configuración de depuración exótica. Es el campo normal que un operador busca cuando una ruta empieza a devolver errores. Ocultar un encabezado `Authorization` es habitual porque los equipos esperan que contenga secretos. Ocultar claves de consulta arbitrarias es más difícil: una fuente ascendente lo llama `token`, otra usa `api_key`, una tercera acepta `sig` y una cuarta coloca la credencial dentro de un blob firmado.

No aceptes «ocultamos los registros» como respuesta hasta que alguien pueda mostrar el destino exacto de la solicitud después de la redacción, incluidos los valores de consulta, en cada salto. Una regla que oculta `token` no detecta `access_token`. Una regla que oculta `access_token` no detecta a un proveedor que llama `key` al mismo valor. Una regla que oculta nombres conocidos no hace nada ante una URL prefirmada cuya credencial se distribuye entre varios parámetros.

La diferencia práctica es esta: ocultar encabezados protege alrededor de una solicitud que contiene secretos, mientras que la inyección de encabezados evita que la URL contenga secretos. Sigues necesitando controles de registro para los encabezados. También puedes dejar de mantener una lista interminable de excepciones para nombres de consulta que siempre va a remolque de las API de proveedores.

## Los proxies inversos convierten una solicitud en varios registros

Un proxy inverso ve la solicitud completa antes de reenviarla. También los equilibradores de carga, las puertas de enlace de API, las mallas de servicios, los WAF, los servicios perimetrales de CDN y los agentes de observabilidad que instrumentan el ciclo de vida de la solicitud. No todos registran el mismo formato, conservan los datos durante el mismo tiempo ni los envían a la misma cuenta.

Esto importa porque un registro de aplicación limpio demuestra muy poco. Un registro de entrada puede contener la URL original. Un registro de errores de proxy puede repetirla si falla la conexión con el servicio ascendente. Un tramo de traza puede adjuntar `http.target` o el valor de una ruta. Un paquete de soporte puede agrupar varios de esos archivos porque alguien necesitaba ayuda con un tiempo de espera. Cada copia tiene sentido operativo por separado. Juntas convierten una clave filtrada en un problema de inventario.

Los equipos suelen intentar resolverlo con un filtro global de redacción. Usa filtros, pero conoce su límite. Un filtro solo funciona después de que un componente ha recibido la URL, la ha analizado correctamente y ha reconocido todas las formas de escribir el secreto. También presiona para conservar credenciales en las URL, porque quitarlas exigiría cambiar el código cliente. La solución a largo plazo está en el límite de la llamada, donde la credencial se une a la solicitud.

Supón que un agente crea una solicitud de despliegue. Puede producir esta descripción de acción segura:

```
GET https://deploy.example.test/v2/releases?project=docs-site&limit=20
credential: deploy-read
```

Una puerta de enlace que posee la credencial resuelve `deploy-read` dentro de su bóveda y envía `Authorization: Bearer ...` al servicio ascendente. El proxy sigue viendo una solicitud, claro. Su destino contiene `project` y `limit`, no el valor bearer. Si su registro de encabezados expone `Authorization` por accidente, es un defecto aparte, con una prueba clara y limitada. No ocultes ese defecto, pero tampoco lo multipliques filtrando por la URL.

## El historial del navegador es una filtración local que dura mucho

El historial del navegador no es la ruta principal para un agente sin interfaz, pero revela un error en los flujos de trabajo humanos. Los ingenieros pegan una URL que falla en el navegador para inspeccionar una página de error, reproducir una devolución de llamada o comprobar que una ruta de API funciona. El navegador guarda la dirección completa, la sugiere más tarde y puede sincronizar el historial según la configuración local. Una grabación de pantalla, una sesión compartida o un compañero que use la máquina pueden volver a mostrarla.

El encabezado Referer añade otra ruta. Cuando un navegador carga una página cuya dirección contiene un secreto en la consulta y esa página solicita un recurso o sigue un enlace, un servidor posterior puede recibir un valor de referencia según la política del navegador. Las políticas modernas de referente reducen algunos casos, pero no convierten una URL secreta en un diseño sólido. La credencial no debería estar presente para que la política tenga que protegerla.

Por eso «el agente nunca la vio» no basta. Un desarrollador puede ver la URL en el resultado de un agente, pegarla en un ticket o usarla en una comprobación manual. Cualquier sistema que muestra una URL fomenta que se copie. Pon un nombre de secreto opaco en la acción visible para las personas, no un valor secreto.

Hay una excepción que conviene mencionar: una URL firmada concede acceso deliberadamente mediante la propia URL. Puede ser el protocolo que ofrece un servicio de almacenamiento para una descarga temporal. Trátala como una capacidad limitada, no como una credencial de API normal. Haz que caduque pronto, limítala a un objeto y método cuando el proveedor lo permita, evita imprimirla y no dejes que un agente elija sus propios parámetros de consulta arbitrarios. Una URL firmada sigue siendo sensible en el historial y los registros. Su naturaleza temporal limita el daño, pero no elimina la vía de filtración.

## Las exportaciones de auditoría convierten un incidente en un evento de distribución

Un registro de auditoría debería ayudarte a responder quién solicitó una acción, qué identidad de credencial la autorizó, qué destino la recibió y qué ocurrió. No debería convertirse en un segundo almacén de credenciales. El esquema de auditoría peligroso registra una URL completa porque parece fiel. ¿Fiel a qué? Conserva fielmente un valor que el investigador nunca debería necesitar.

Usa un registro de auditoría que separe identificadores estables de material secreto. Un registro útil de acción HTTP puede incluir el método, el esquema, el host, la ruta, nombres y valores de consulta no sensibles, el alias de credencial, la identidad de sesión, la decisión, el estado, la clasificación de respuesta y las marcas de tiempo. Puede guardar un resumen criptográfico de componentes seleccionados de la solicitud si necesitas evidencia de manipulación. Debe excluir valores de autorización, contenido de cookies y valores secretos de consulta.

Esta forma también hace más seguras las exportaciones. JSON, CSV y los archivos de soporte salen de su límite de acceso original. Alguien los envía a un proveedor, los adjunta a un error, los guarda en una unidad compartida o los carga en una hoja de cálculo. Esa es la vida normal de una exportación. Diseñala para que responda una investigación sin convertirse en una cola de rotación.

Sallyport proyecta sesiones de agentes y llamadas individuales desde un único registro de auditoría cifrado y encadenado mediante hashes. Su comando `sp audit verify` verifica esa cadena sin conexión sobre texto cifrado y sin una clave. Eso demuestra que el registro no se ha alterado. No justifica registrar URL secretas. La integridad y la confidencialidad resuelven problemas distintos, y los equipos los confunden a menudo porque ambos se llaman «seguridad de auditoría».

Un registro a prueba de manipulaciones que contiene una credencial activa puede demostrar con precisión cuándo se filtró la credencial. Un registro oculto sin una historia de integridad puede ser seguro para compartir, pero difícil de confiar. Necesitas ambas propiedades, aplicadas a campos distintos.

## La inyección de encabezados mantiene el secreto fuera de la descripción de la acción

La inyección de credenciales bearer y de encabezados personalizados funciona porque quien llama puede describir el destino sin poseer la credencial. La puerta de enlace conserva la relación entre una etiqueta de credencial y su entrada cifrada en la bóveda. Construye la solicitud, añade el encabezado, la envía y devuelve el resultado. El agente no recibe ni el valor del encabezado ni un marcador de posición que pueda expandir.

Para una API bearer, la forma es conceptualmente sencilla:

```
solicitud del agente
  method: GET
  url: https://metrics.example.test/v1/usage?team=infra
  credential: metrics-production

solicitud saliente de la puerta de enlace
  GET /v1/usage?team=infra HTTP/1.1
  Host: metrics.example.test
  Authorization: Bearer [vault value]
```

El texto entre corchetes es una notación explicativa, no un valor que deba aparecer en una transcripción real. En un límite bien diseñado, el agente no puede pedir que se revele, guardarlo en un archivo ni moverlo a una cadena de consulta. La puerta de enlace trata la credencial como datos que puede usar, no como datos que puede devolver.

Los encabezados personalizados merecen el mismo trato. Algunos servicios usan `X-API-Key`, `Api-Key` o un encabezado específico del proveedor en lugar de `Authorization`. El nombre exacto cambia, pero la regla no: la descripción de solicitud visible para el cliente debe referirse a una identidad de credencial y la puerta de enlace debe inyectar el valor al ejecutar. La autenticación básica también pertenece en el encabezado, aunque debes preferir un método más sólido compatible con el proveedor cuando exista.

Sallyport admite inyección de credenciales bearer, básicas y en encabezados personalizados para llamadas HTTP. Es útil aquí porque permite que un agente compatible con MCP solicite una acción HTTP sin guardar la clave API en su propio contexto. Ese mismo límite no es un sanitizador mágico: la URL y los campos que proporciona el agente aún pueden contener secretos si lo permites. Valida la forma de la solicitud y rechaza valores con aspecto de secreto en lugares que deberían seguir siendo públicos.

## POST no corrige una credencial en la URL

Cambiar `GET` por `POST` y dejar `?api_key=...` en la URL casi no cambia nada respecto a esta filtración. Los proxies siguen recibiendo el destino de la solicitud. Los registros de acceso todavía suelen guardarlo. Los navegadores y las herramientas aún pueden mostrarlo. CWE-598 señala expresamente que una cadena de consulta puede aparecer con métodos distintos de GET.

Mover una credencial al cuerpo de la solicitud puede reducir el registro accidental en algunas pilas, porque los registros de acceso normalmente no incluyen cuerpos de forma predeterminada. Eso no equivale a inyectar encabezados. El middleware de depuración, los clientes de API, los informadores de errores y las herramientas de registro de solicitudes suelen capturar cuerpos. También hacen que una credencial forme parte de la carga de la acción que un agente podría intentar construir o repetir.

Usa el método y el cuerpo que requiere la API. Coloca un secreto en el cuerpo solo cuando el protocolo lo exija explícitamente, como un intercambio de tokens que especifica parámetros de formulario. Después limita el registro de cuerpos, excluye el endpoint de la captura general de solicitudes y conserva el intercambio dentro del componente que posee la credencial. No conviertas por superstición cada solicitud de lectura en POST. Conserva la semántica HTTP y elimina el secreto de la URI.

Otra recomendación equivocada es «codifícalo en la URL y los registros serán inofensivos». La codificación porcentual solo cambia la representación. Cualquiera que tenga la URL puede decodificarla, y muchos visores de registros ya lo hacen. Base64 tiene el mismo problema. La codificación puede hacer que una consulta sea más difícil de detectar a simple vista y que las reglas de redacción pasen más fácilmente por alto el secreto.

## Una migración segura empieza con pruebas, no con una edición masiva

No sustituyas todos los parámetros de consulta. Parámetros como `page`, `sort`, `project` y `fields` suelen ser legítimos, útiles y no secretos. Empieza por localizar dónde entran realmente las credenciales en las URL y cambia después esas llamadas con una prueba que observe la solicitud saliente.

Sigue esta secuencia:

1. Busca en el código fuente, indicaciones para agentes, comandos curl guardados, datos de prueba, paneles y guías operativas nombres como `token`, `key`, `secret`, `signature` y `credential`. Busca también URL completas que contengan `?`. Los nombres varían.
2. Reúne registros de acceso y datos de trazas representativos de cada capa de entrada. Confirma si cada destino de solicitud registra valores de consulta, no solo nombres de parámetros. Trata las exportaciones conservadas y los paquetes de soporte como otra capa.
3. Pregunta al responsable de la API qué mecanismo de encabezado o cuerpo admite. Si solo acepta una credencial en la consulta, documenta la excepción, limita mucho el alcance de la credencial y aísla esa llamada de los agentes de propósito general.
4. Cambia la interfaz de acción de `url with secret` a `url plus credential alias`. Añade una prueba que rechace una URL que contenga un token de prueba conocido y compruebe que el encabezado saliente lo contiene.
5. Rota todas las credenciales que aparecieron en una URL y elimina registros y exportaciones antiguos conforme a tu proceso de retención. Rotar sin limpiar deja exposición histórica; limpiar sin rotar deja una clave aún activa en circulación.

La prueba es donde fallan muchas migraciones. Una prueba unitaria que solo verifica la respuesta HTTP final no puede decirte si la clave fue en el encabezado o en la cadena de consulta. Coloca un servidor de prueba local detrás del cliente y captura por separado el método, la ruta, la consulta y los encabezados. Comprueba que la consulta no contenga el valor de prueba y que solo el encabezado previsto lo contenga.

Para una puerta de enlace, añade un caso de rechazo. Dale `https://api.example.test/v1/jobs?access_token=test-canary` con cualquier alias de credencial y haz que falle antes de la llamada de red. Así detectarás una futura plantilla de indicaciones o un contenedor de conveniencia que intente volver a poner secretos en las URL. Un token canario debe ser único e inutilizable fuera de la prueba.

## Seguir ocultando información es necesario después de corregir el diseño

La inyección de encabezados reduce el conjunto de filtraciones probables. No vuelve seguros los registros por decreto. Una aplicación puede repetir un encabezado de autorización en una excepción, un proxy puede registrar todos los encabezados durante un incidente de depuración o un agente puede pegar datos de respuesta que contienen un secreto en un ticket. Mantén la redacción, el control de acceso, los límites de retención y la respuesta a incidentes.

Pero ordena bien esos controles. Primero, mantén las credenciales fuera de las URL y de las descripciones de solicitudes visibles para el agente. Después, oculta encabezados y cuerpos sensibles conocidos en todos los registradores que puedan capturarlos. Luego restringe quién puede recuperar registros sin procesar y cuánto tiempo se conservan. Por último, practica la rotación y la limpieza de exportaciones para que el equipo pueda actuar cuando falle un control.

Este orden evita una trampa popular: tratar un patrón de redacción como permiso para pasar secretos por todas partes. Las reglas de redacción son frágiles porque dependen de nombres, formatos y analizadores. Un límite de credenciales es más sólido porque controla quién llega a recibir el valor.

## La autorización del agente debe incluir el destino de salida

Un agente que no puede leer un token aún puede gastar su autoridad. Si puede elegir cualquier URL, podría enviar una credencial válida a un host no previsto mediante una configuración confusa, un error tipográfico, una ruta SSRF o una indicación que aproveche una interfaz de acción permisiva. No divulgar secretos y controlar el destino son requisitos distintos.

Vincula un alias de credencial al servicio previsto y define de forma explícita el host, el esquema y la forma de ruta permitida. Una puerta de enlace debe rechazar una credencial elegida para `metrics.example.test` cuando la acción indica `metrics.example.test.evil.invalid`. También debe rechazar trucos de información de usuario, redirecciones inesperadas que reenvíen credenciales y URL que oculten un cambio de host mediante codificación. Estas comprobaciones pertenecen al lugar donde se inyecta el encabezado, porque es el último punto que conoce tanto la identidad de la credencial como el destino analizado.

La tarjeta de autorización por sesión de Sallyport identifica el nuevo proceso de agente mediante su autoridad de firma de código, y las claves por llamada pueden requerir un clic o Touch ID en cada uso. Esos controles responden si ese proceso puede invocar una acción. No hacen seguro un destino arbitrario, así que mantén la configuración de acción lo bastante limitada para que una aprobación signifique algo concreto.

## El artefacto útil es un registro de solicitud que puedes compartir

Una prueba práctica de este diseño es sencilla: ¿puedes pegar el registro de acción en un ticket de incidente sin iniciar una rotación de credenciales? Si la respuesta es no, el registro contiene demasiado.

Aspira a un registro como este:

```
request_id: 01J...
agent_session: signed-process-42
method: GET
destination: https://metrics.example.test/v1/usage
query: team=infra
credential_alias: metrics-production
authorization: injected, value omitted
result: 200, 481 bytes
```

Este registro da a un investigador suficiente información para correlacionar la llamada, reproducir la ruta con una credencial de prueba segura y preguntar por qué el agente eligió `metrics-production`. Deliberadamente no puede autenticar a nadie. Si un investigador necesita el secreto, ese es un proceso de recuperación privilegiado aparte, no un campo de telemetría rutinaria.

La primera acción suele ser una búsqueda aburrida de URL en código fuente, indicaciones y registros exportados. Hazla de todos modos. La credencial que encuentres puede haber sido copiada por sistemas cuya existencia olvidaste, y la única respuesta limpia es dejar de crear ese tipo de URL.
