¿Puede el userinfo de una URL ocultar el destino de tu API?
El userinfo de URL puede disfrazar un destino de API. Aprende a analizar, rechazar, mostrar y probar userinfo de forma segura antes de que los agentes envíen solicitudes.

Un agente que acepta una URL arbitraria puede dirigirse a un lugar que su operador no pretendía. El userinfo de URL facilita ese error porque coloca un nombre de host conocido antes de un signo @, mientras el destino real queda después. Si tu pantalla de aprobación, lista de permitidos o vista de auditoría trata toda la cadena como una etiqueta que parece un host, una solicitud puede parecer aprobada y aun así salir hacia otro servidor.
Trata userinfo como una entrada no permitida para acciones de API salientes, salvo que tengas un motivo de compatibilidad limitado y documentado para admitirlo. Analiza la URL una sola vez en el límite, rechaza un campo userinfo no vacío antes de que las credenciales entren en la solicitud y toma todas las decisiones posteriores a partir de los campos analizados. No es un truco exótico de URL. Es sintaxis normal frente a una interfaz de revisión que pide a las personas leer demasiada puntuación demasiado deprisa.
El nombre de host está después del último signo arroba
En una URL HTTP absoluta, la autoridad está entre // y el siguiente /, ? o #. RFC 3986 describe esa autoridad como userinfo opcional, luego @, después host y, opcionalmente, puerto. Por tanto, el host no es cualquier texto que aparezca primero tras //.
Considera esta solicitud:
https://[email protected]/v1/charges
Alguien que lee el principio por encima puede reconocer api.example.com y detenerse ahí. Un analizador de URL conforme separa los elementos así:
scheme: https
userinfo: api.example.com
host: collector.invalid
port: 443
path: /v1/charges
La conexión TCP y la verificación del nombre de host TLS usan collector.invalid. La cadena anterior a @ no identifica el servidor remoto. Es userinfo, una parte heredada de la gramática de URL que antes se usaba para nombres y contraseñas.
El mismo problema aparece de una forma más familiar:
https://billing.example.com:[email protected]/invoices
Todo lo anterior a @ sigue siendo userinfo, incluidos los dos puntos y el texto posterior. El host remoto sigue siendo evil.invalid. Un revisor no puede deducir con seguridad el destino a partir de la primera subcadena que parece un nombre de host en una URL sin procesar.
No intentes resolverlo enseñando a los revisores a fijarse en ese carácter. Las personas cometen este error durante la revisión de incidentes, al final de un día largo y cuando un agente genera muchas solicitudes de acción. Un control que depende de un análisis visual perfecto es débil.
El estándar URL que usan los navegadores y muchos entornos de ejecución tiene el mismo resultado práctico para esquemas especiales como HTTPS: los campos de nombre de usuario y contraseña analizados están separados del nombre de host. El comportamiento exacto del analizador puede variar ante entradas malformadas y escapes, otra razón para analizar con el entorno que enviará la solicitud. No valides con una biblioteca y ejecutes con otra que acepte un conjunto distinto de cadenas.
Una regla útil es sencilla: solo un nombre de host analizado puede decidir a qué lugar se permite ir una solicitud. El texto de la URL sin procesar es evidencia, no autoridad.
Userinfo crea un problema de revisión antes de crear uno de red
El cliente de red normalmente sabe adónde va. El fallo ocurre antes, cuando una persona o un control aprueba una representación equivocada de ese destino.
Un flujo típico de agente tiene varios lugares donde puede filtrarse texto sin procesar: el argumento de la llamada a una herramienta, una tarjeta de aprobación, un diario de sesión, un mensaje de error y una notificación. Si una de esas vistas dice Calling api.example.com porque extrae el texto anterior a @, está comunicando algo falso al operador. Si otra registra la cadena completa pero la corta a una anchura fija, el host real puede desaparecer por completo.
Esto importa incluso cuando el agente no tiene credenciales para el destino. Una solicitud saliente puede llevar datos de usuario, un cuerpo de solicitud firmado, un token bearer elegido por una regla amplia o simplemente alcanzar una dirección interna que nunca debería recibir tráfico de agentes. La historia de las credenciales recibe atención porque es concreta. La integridad del destino exige la misma disciplina.
La distinción que suele confundirse es la seguridad al mostrar URL frente a la seguridad al transportarla. Escapar @ en una vista HTML puede hacer que una página resulte menos confusa, pero no determina dónde se conecta un cliente HTTP. A la inversa, un analizador puede hacer correctamente la llamada de red mientras una vista de aprobación mal diseñada sigue animando a alguien a aprobar el host equivocado. Necesitas una decisión de transporte correcta y una presentación honesta.
No sustituyas @ por un carácter inocuo en la solicitud almacenada y sigas adelante. Eso oculta la entrada que provocó la denegación y dificulta la investigación posterior. Conserva la cadena original como entrada sin procesar, marca la solicitud como rechazada y registra el motivo analizado sin escribir credenciales integradas en un diario legible.
La vista debe empezar por un campo de destino independiente, como Host: collector.invalid, y colocar debajo la URL original. Ese orden convierte un acertijo de puntuación en una afirmación directa. También da al revisor un campo estable que comparar con la credencial solicitada o la integración prevista.
Rechazar userinfo es más seguro que repararlo
Para una puerta de enlace de acciones de agentes, el valor predeterminado sensato es rechazar toda URL HTTP saliente cuyo campo de nombre de usuario o contraseña analizado no esté vacío. La mayoría de integraciones de API ya envían las credenciales en encabezados de solicitud o usan un inyector de credenciales. Admitir userinfo de URL amplía la superficie de ataque sin resolver una necesidad habitual de API.
El orden de validación importa. Analiza primero la cadena original. Rechaza URL malformadas, esquemas no admitidos y userinfo antes de aplicar listas de permitidos de hosts, mostrar avisos de aprobación, seguir redirecciones, resolver DNS o elegir credenciales. Esa secuencia evita mostrar una solicitud como apta cuando más tarde descartarás una parte de ella.
Este pseudocódigo expresa la regla:
u = parse_absolute_url(raw_url)
if u.scheme not in {"https", "http"}:
deny("unsupported scheme")
if u.username != "" or u.password != "":
deny("URL userinfo is not accepted")
host = normalize_hostname(u.hostname)
if host == "":
deny("missing hostname")
if not destination_is_allowed(u.scheme, host, u.port):
deny("destination is not allowed")
send(u)
El analizador debe devolver campos estructurados. Dividir por @, quitar un prefijo o buscar una subcadena de nombre de host fallan ante variaciones normales. Una autoridad puede contener más de un @ en la entrada sin procesar. Un analizador decide qué delimitador es sintáctico y si los caracteres anteriores pertenecen a userinfo. También trata de forma más coherente los literales IPv6 entre corchetes, puertos explícitos, codificación porcentual y componentes vacíos que una lógica de cadenas escrita a mano.
Rechazar userinfo da al solicitante una corrección clara: usa https://api.example.com/path y proporciona después la autenticación HTTP a través de la vía de credenciales designada. No deja al solicitante adivinando si cambiaste silenciosamente https://name@host por https://host.
Hay un caso de compatibilidad que conviene reconocer. Algunas URL antiguas integran autenticación Basic como https://name:secret@host/path. Si una migración debe gestionar esas URL, hazlo en una ruta de importación de una sola vez que extraiga las credenciales a almacenamiento protegido, confirme el host analizado y elimine la cadena de origen del registro de importación cuando la política lo permita. No permitas que la API de acciones en tiempo de ejecución las acepte para siempre. El código de compatibilidad temporal tiende a convertirse en superficie de ataque permanente.
Las listas de permitidos de hosts necesitan etiquetas analizadas, no cadenas de aspecto amigable
Una lista de permitidos de destinos debe comparar nombres de host analizados y normalizados, no buscar subcadenas en la URL sin procesar. La regla raw_url.includes("api.example.com") acepta tanto https://[email protected] como https://api.example.com.evil.invalid. Ninguna es una solicitud a api.example.com.
La coincidencia exacta de host es la regla menos sorprendente. Si una integración necesita solo api.example.com, permite ese nombre y rechaza cualquier otro. Si realmente necesita subdominios, compara etiquetas DNS: permite example.com y los nombres que terminan en .example.com, pero rechaza badexample.com y example.com.evil.invalid.
Una implementación clara tiene esta forma:
function allowedHost(host, root) {
const h = host.toLowerCase().replace(/\.$/, "")
const r = root.toLowerCase().replace(/\.$/, "")
return h === r || h.endsWith("." + r)
}
Ese código asume que el analizador de URL ya ha proporcionado un nombre de host y que quien llama ya ha rechazado userinfo. No debe recibir una URL completa. Mantener estas responsabilidades separadas impide que otro llamador pase más tarde una cadena de autoridad que contenga un puerto, un nombre de usuario o un signo @.
Los dominios internacionalizados también requieren una decisión. Los navegadores suelen serializar nombres de host en ASCII mediante procesamiento IDNA, mientras una persona puede ver texto Unicode. Usa para la comparación la misma forma canónica que usa tu cliente de solicitudes y muestra tanto el host canónico como una forma legible cuando difieran. No afirmes que dos cadenas identifican el mismo dominio porque se parecen con una fuente proporcional.
Las direcciones IP merecen una regla propia. Una lista de permitidos de nombres de host no convierte automáticamente en seguro un literal IP, y la resolución DNS posterior a la aprobación puede cambiar la dirección a la que llega un nombre de host. Si tu modelo de amenazas incluye acceso a servicios locales, decide explícitamente si se permiten direcciones privadas, de bucle local, enlace local y locales IPv6. Rechazar userinfo es necesario, pero por sí solo no resuelve la falsificación de solicitudes del lado del servidor.
Los puertos también tienen significado. https://api.example.com:8443 puede ser un endpoint válido de un socio o un servicio de administración inesperado. Registra el puerto efectivo e inclúyelo en las decisiones de aprobación cuando la integración restrinja alguno. Una etiqueta de host por sí sola no describe todo el destino de red.
Las redirecciones deben pasar por la misma puerta
Una URL inicial permitida no hace que todos los destinos de redirección estén permitidos. Las redirecciones HTTP son nuevas instrucciones de destino proporcionadas por el servidor remoto, y una puerta de enlace de agentes debe analizar y autorizar cada una antes de seguirla.
Supón que un agente solicita https://api.example.com/export. Ese host devuelve una respuesta 302 con esta ubicación:
https://[email protected]/download?id=42
Un cliente que siga redirecciones automáticamente se conectará después a receiver.invalid. Si la puerta de enlace aprobó solo la primera URL, su lista de permitidos y su pantalla de aprobación ya no describen la acción de red que ocurrió.
Gestiona las redirecciones como un bucle con un límite elegido para tu cliente. Para cada valor de ubicación, resuelve una referencia relativa respecto a la URL actual aprobada, analiza la URL absoluta resultante, aplica las mismas reglas de esquema, userinfo, host, puerto y dirección, y decide si continuar. Registra tanto la respuesta de origen como el destino de redirección analizado.
No reenvíes credenciales entre hosts de forma predeterminada. Las bibliotecas de clientes HTTP difieren en si conservan un encabezado Authorization tras una redirección entre hosts, y los encabezados personalizados pueden comportarse de manera diferente. El comportamiento más seguro de una puerta de enlace asocia una credencial a un destino aprobado concreto y construye una nueva solicitud saliente solo después de que el destino de redirección pase la autorización. Una redirección entre dos hosts del mismo proveedor puede ser esperada, pero debe ser una regla explícita y no un accidente de los valores predeterminados de la biblioteca.
El tratamiento de los métodos también requiere atención. Una respuesta 303 suele cambiar una solicitud posterior a GET, mientras 307 y 308 conservan el método y el cuerpo. Si un agente publica un cuerpo sensible, una redirección que conserva el método puede enviarlo a otro lugar. Registra el método usado en cada salto y muestra el destino final en el resultado de la acción.
Un cliente también puede configurarse para no seguir redirecciones. Es una opción sensata para herramientas de API limitadas. Devuelve la respuesta de redirección al agente y exige que solicite explícitamente el siguiente destino. Esto genera otro evento de aprobación, pero da al operador un punto claro para evaluar un cambio de host. Para admitir HTTP de forma amplia, las redirecciones automáticas solo son aceptables cuando cada salto cruza la misma puerta.
Las credenciales deben seleccionarse después de validar el destino
El orden peligroso es fácil de describir: elegir una credencial porque la URL sin procesar contiene un nombre de servicio conocido, analizar después la URL y enviar la solicitud. Un truco con @ puede convertir el texto familiar en userinfo mientras el secreto seleccionado viaja a un host controlado por un atacante.
El orden más seguro es igual de sencillo. Primero analiza y valida la URL. Después autoriza su esquema, host, puerto y cualquier estado de redirección. Solo entonces busca una credencial vinculada a ese destino aprobado e insértala en la solicitud saliente. Mantén ese secreto fuera de los argumentos de herramientas, la memoria del agente y los valores devueltos.
Esto también resuelve un error de configuración menos llamativo, pero común. Una credencial asociada a api.example.com no debe enviarse automáticamente a uploads.example.com, aunque ambos nombres estén bajo el mismo dominio principal. Los distintos hosts suelen tener diferente propiedad, terminación TLS, registro o ámbitos de permisos. Empieza con una vinculación exacta de host. Amplía la coincidencia solo cuando la integración documente por qué lo necesita.
Insertar encabezados es preferible a usar userinfo de URL porque separa el destino de la autenticación. Un registro de solicitud puede indicar que se insertó un encabezado de autorización sin guardar su valor. El agente recibe la respuesta necesaria para continuar su trabajo, no un secreto reutilizable.
Sallyport mantiene esa separación al guardar credenciales de API y SSH en su bóveda cifrada y ejecutar la acción saliente sin exponer esas credenciales al agente. En cualquier puerta de enlace con ese modelo, rechazar userinfo antes de buscar credenciales cierra la brecha entre el destino previsto por el operador y el host que recibe la solicitud.
Tampoco confíes en un encabezado de solicitud proporcionado por el agente para identificar su destino. El encabezado Host, la autoridad HTTP/2, el host de la URL, la configuración del proxy y el nombre de servidor TLS pueden interactuar de formas específicas de cada cliente. Una puerta de enlace debe controlar los ajustes de conexión y derivarlos de la URL analizada y validada. Si permites encabezados personalizados, trátalos como contenido de la solicitud, no como permiso para reescribir el enrutamiento.
Las tarjetas de aprobación deben mostrar primero el destino analizado
Una tarjeta de aprobación debe responder tres preguntas concretas sin pedir a quien la lee que reconstruya una URL: qué proceso lo solicitó, qué operación se ejecutará y qué host la recibirá. Coloca el nombre de host y el puerto analizados en una línea de destino dedicada. Pon cerca el método HTTP y la ruta. Muestra la URL original como evidencia de apoyo, no como la única señal de destino.
Para la solicitud de aspecto malformado usada antes, una tarjeta útil diría:
Process: signed agent process
Action: POST /v1/charges
Host: collector.invalid:443
Result: blocked because URL userinfo is present
Input: https://[email protected]/v1/charges
No debe mostrar api.example.com como una insignia derivada del lado izquierdo de la autoridad. Tampoco debe decir solo External HTTP request, que no da a nadie una base útil para decidir.
La aprobación por sesión y la aprobación por llamada resuelven problemas humanos distintos. Una aprobación de sesión indica que un proceso concreto en ejecución puede usar una puerta de enlace durante toda su vida. Una aprobación por llamada indica que una credencial o acción especialmente sensible necesita una nueva decisión humana. Ninguna sustituye la validación básica de URL. Un proceso en el que confías puede seguir siendo manipulado por un comentario de incidencia no confiable, un campo de metadatos de paquete o un archivo de configuración generado.
La escalera de decisiones de Sallyport mantiene una bóveda bloqueada como un bloqueo absoluto y después usa autorización de sesión y confirmación opcional por secreto. Esta estructura funciona mejor cuando los destinos malformados fallan antes de llegar a una tarjeta de aprobación, porque un operador no debería tener que decidir si un signo de puntuación de URL cambió el endpoint.
Sé específico en los mensajes de denegación. Userinfo is not allowed in outbound URLs indica al desarrollador del agente qué debe corregir. Invalid request provoca reintentos, escapes improvisados y presión para debilitar la validación. Evita repetir una contraseña si el analizador extrajo una. El mensaje puede nombrar el componente prohibido sin reproducir su contenido.
Las pruebas deben usar entradas engañosas, no solo URL correctas
Una suite de pruebas de validación necesita ejemplos diseñados para engañar a lectores humanos y comprobaciones de cadenas simplistas. Aprobar https://api.example.com/v1 demuestra muy poco sobre el límite en el que un agente puede enviar texto arbitrario.
Empieza con casos como estos y comprueba el host analizado, la decisión y el motivo:
ALLOW https://api.example.com/v1 host=api.example.com
DENY https://[email protected]/v1 reason=userinfo
DENY https://name:[email protected]/v1 reason=userinfo
DENY https://api.example.com.evil.invalid/v1 reason=host
DENY https://api.example.com:444/v1 reason=port
DENY https://[::1]/v1 reason=address
Añade una prueba de redirección. Haz que un servidor de pruebas permitido emita una redirección cuya ubicación contenga userinfo y comprueba que el cliente registra un segundo salto bloqueado sin enviar una solicitud al servidor de destino. Así detectas el error frecuente en el que la validación de la URL inicial vive en una ruta de código y el manejo de redirecciones está dentro de una devolución de llamada de la biblioteca.
Prueba la codificación porcentual de forma deliberada, pero no supongas que cada @ codificado equivale al carácter real. En muchos analizadores, %40 en una ruta sigue siendo datos de ruta, mientras que un @ real en la autoridad actúa como delimitador. Introduce la cadena sin procesar exacta en el analizador que usas en producción y comprueba sus campos. Lo importante no es una regla casera de descodificación. Es que ningún campo userinfo analizado y no vacío pueda llegar al emisor.
Prueba también el renderizado de registros y aprobaciones. Un control de seguridad puede denegar correctamente una solicitud y aun así crear un mal registro operativo si la vista corta el host real o expone texto de contraseña analizado. Merece la pena tener pruebas de instantáneas para las líneas de destino, porque las regresiones visuales suelen llegar mediante cambios de diseño que parecen inocuos.
Por último, prueba la cadena de auditoría y la ruta de revocación alrededor de una llamada denegada. Una denegación debe ser suficientemente visible para investigar, pero nunca debe incluir secretos insertados. El registro útil contiene la solicitud sin procesar bajo controles de acceso adecuados, el esquema y host analizados, el motivo de la decisión, la identidad del proceso que llama y el hecho de que no se realizó ninguna acción saliente.
La sintaxis de URL no es un lenguaje de políticas
Algunos equipos responden a los casos límite de URL añadiendo una pila creciente de excepciones: permitir un nombre de usuario para este proveedor, aceptar un puerto especial para ese entorno, confiar en una redirección solo si un encabezado parece correcto y parchear un comparador de cadenas para cada incidente. Este enfoque parece flexible porque evita decir no a una solicitud extraña. También crea reglas que nadie puede revisar de forma fiable.
Mantén la regla pequeña. Las solicitudes salientes tienen un destino analizado. Userinfo se rechaza. El esquema, host, puerto y clase de dirección permitidos son explícitos. Las redirecciones vuelven a pasar por las mismas comprobaciones. Las credenciales se seleccionan solo después de que el destino sea aprobado. Cada decisión genera un registro que expresa claramente el host analizado.
Esa regla rechazará algunas URL antiguas que un navegador podría aceptar. Bien. Un agente autónomo no necesita todas las concesiones históricas de la barra de direcciones de un navegador. Necesita una interfaz limitada que haga difícil confundir la relación entre acción, destino y credencial.
Si necesitas cambiar esa interfaz, haz visible la excepción como una capacidad con nombre, pruebas y una decisión de caducidad. No la escondas en código de limpieza de URL. La primera entrada hostil encontrará la diferencia entre el texto que parece un host y el host al que realmente se conecta tu cliente.
FAQ
¿Puede un signo @ en una URL cambiar el host de destino?
Sí. En una URL como https://[email protected]/path, el host de destino es evil.example, no api.example.com. El texto anterior a @ es userinfo, así que aceptarlo en URL proporcionadas por agentes facilita los errores de revisión humana.
¿Es válida la sintaxis userinfo en una URL?
Es una sintaxis de URL válida, aunque muchos clientes de API no tienen motivo para aceptarla. RFC 3986 permite userinfo opcional en una autoridad, pero que la gramática lo permita no justifica que cruce el límite de acciones de un agente.
¿Debe una puerta de enlace de API rechazar URL con userinfo?
Recházala en el límite, salvo que tengas una necesidad de compatibilidad muy concreta. No la elimines y sigas en silencio, porque cambiarías la solicitud original y dejarías un registro engañoso.
¿user:[email protected] envía tráfico a user?
No. https://user:[email protected]/v1 tiene como nombre de host api.example.com; user:pass es userinfo. Un analizador expone estos campos por separado, y las comprobaciones de seguridad deben usar el nombre de host analizado, no la cadena sin procesar.
¿Debo poner credenciales de autenticación Basic en una URL?
La autenticación Basic debe ir en un encabezado HTTP Authorization, no integrada en una URL. Las credenciales en URL se filtran a registros, historiales, comandos copiados y mensajes de error con mucha más facilidad que los encabezados.
¿Las redirecciones dificultan la validación de userinfo en URL?
Cada destino de redirección necesita las mismas comprobaciones de análisis y autorización que la URL original. Comprobar solo la primera URL permite que un endpoint público autorizado redirija a un agente hacia un host no aprobado.
¿api.example.com.evil.example es lo mismo que api.example.com?
No. api.example.com.evil.example es un subdominio de evil.example, mientras que [email protected] se dirige realmente a api.example.com. Analiza primero el host y después compara las etiquetas de dominio según tu regla explícita de lista de permitidos.
¿Cómo valido una URL de API saliente?
Evita las expresiones regulares sobre la URL completa. Usa un analizador compatible con los estándares, rechaza userinfo de forma explícita, exige HTTPS cuando corresponda, normaliza el nombre de host analizado y compáralo con los hosts o sufijos de dominio permitidos.
¿Qué debe registrar una auditoría de una solicitud HTTP de un agente?
Un buen registro de auditoría conserva la entrada sin procesar para investigar y guarda los campos analizados por separado: esquema, nombre de host, puerto, ruta, destino de redirección y decisión. Muestra de forma destacada el nombre de host analizado para que nadie tenga que descifrar mentalmente la puntuación.
¿Cómo pruebo una herramienta de agente frente a destinos URL disfrazados?
La corrección suele ser una regla de validación pequeña, pero debe ejecutarse antes de las aprobaciones y de insertar credenciales. Añade casos de prueba para @, delimitadores codificados por porcentaje, varios caracteres @, redirecciones, hosts con mayúsculas y puntos finales para que una refactorización futura no reabra el problema.