O userinfo de URL pode ocultar seu destino de API?
O userinfo de URL pode disfarçar um destino de API. Saiba como analisar, rejeitar, exibir e testar userinfo com segurança antes que agentes enviem solicitações.

Um agente que aceita uma URL arbitrária pode ser direcionado a um lugar que seu operador não pretendia. O userinfo de URL facilita esse erro porque coloca um hostname conhecido antes de um sinal @, enquanto o destino real fica depois dele. Se sua tela de aprovação, lista de permissões ou visualização de auditoria trata a string inteira como um rótulo que parece um host, uma solicitação pode parecer aprovada e ainda assim seguir para outro servidor.
Trate userinfo como entrada não permitida para ações de API de saída, salvo se houver um motivo restrito e documentado de compatibilidade para aceitá-lo. Analise a URL uma vez na fronteira, rejeite um campo userinfo não vazio antes de inserir credenciais na solicitação e tome todas as decisões posteriores a partir dos campos analisados. Isso não é um truque exótico de URL. É uma sintaxe comum diante de uma interface de revisão que pede às pessoas que leiam pontuação demais, rápido demais.
O hostname fica depois do último sinal de arroba
Em uma URL HTTP absoluta, a autoridade fica entre // e o próximo /, ? ou #. A RFC 3986 descreve essa autoridade como userinfo opcional, depois @, depois host e, por fim, porta opcional. Portanto, o host não é qualquer texto que apareça primeiro após //.
Considere esta solicitação:
https://[email protected]/v1/charges
Quem examina o início pode notar api.example.com e parar por aí. Um analisador de URL compatível separa assim:
scheme: https
userinfo: api.example.com
host: collector.invalid
port: 443
path: /v1/charges
A conexão TCP e a verificação do hostname TLS usam collector.invalid. A string antes de @ não identifica o servidor remoto. Ela é userinfo, uma parte antiga da gramática de URL que antes era usada para nomes e senhas.
O mesmo problema aparece em uma forma de aparência mais familiar:
https://billing.example.com:[email protected]/invoices
Tudo antes de @ continua sendo userinfo, inclusive os dois-pontos e o texto depois deles. O host remoto continua sendo evil.invalid. Quem revisa não pode inferir com segurança o destino a partir da primeira substring que parece um hostname em uma URL bruta.
Não tente resolver isso ensinando quem revisa a perceber esse caractere. As pessoas cometem esse erro durante a análise de incidentes, no fim de um dia longo e quando um agente produz muitas solicitações de ação. Um controle que depende de interpretação visual perfeita é fraco.
O padrão de URL usado por navegadores e muitos runtimes produz o mesmo resultado prático para esquemas especiais como HTTPS: os campos de nome de usuário e senha analisados ficam separados do hostname. O comportamento exato do analisador pode variar diante de entradas malformadas e escapes, outro motivo para analisar com o runtime que enviará a solicitação. Não valide com uma biblioteca e execute com outra que aceite um conjunto diferente de strings.
Uma regra útil é simples: apenas um hostname analisado pode decidir para onde uma solicitação pode ir. O texto bruto da URL é evidência, não autoridade.
Userinfo cria um problema de revisão antes de criar um problema de rede
Em geral, o cliente de rede sabe para onde está indo. A falha acontece antes, quando uma pessoa ou um controle aprova a representação errada desse destino.
Um fluxo típico de agente tem vários pontos em que o texto bruto pode vazar: o argumento da chamada de ferramenta, um cartão de aprovação, um diário de sessão, uma mensagem de erro e uma notificação. Se uma dessas visualizações diz Calling api.example.com porque extrai o texto antes de @, ela informa algo falso ao operador. Se outra registra a string completa, mas a corta após uma largura fixa, o host real pode desaparecer por completo.
Isso importa mesmo quando o agente não tem credencial para o destino. Uma solicitação de saída pode levar dados do usuário, um corpo de solicitação assinado, um token bearer escolhido por uma regra ampla ou simplesmente alcançar um endereço interno que nunca deveria receber tráfego de agentes. A questão das credenciais recebe atenção por ser concreta. A integridade do destino exige a mesma disciplina.
Muitas pessoas confundem segurança de exibição da URL com segurança de transporte da URL. Escapar @ em uma visualização HTML pode tornar uma página menos confusa, mas não determina onde um cliente HTTP se conecta. Por outro lado, um analisador pode fazer corretamente a chamada de rede enquanto uma tela de aprovação mal projetada ainda incentiva uma pessoa a aprovar o host errado. Você precisa de uma decisão de transporte correta e de uma exibição honesta.
Não substitua @ por um caractere inofensivo na solicitação armazenada e prossiga. Isso esconde a entrada que causou a recusa e dificulta a investigação posterior. Preserve a string original como entrada bruta, marque a solicitação como rejeitada e registre o motivo analisado sem gravar credenciais embutidas em um diário legível.
A exibição deve começar com um campo de destino separado, como Host: collector.invalid, e colocar a URL original abaixo. Essa ordem transforma um quebra-cabeça de pontuação em uma afirmação direta. Também dá a quem revisa um campo estável para comparar com a credencial solicitada ou a integração pretendida.
Rejeitar userinfo é mais seguro do que corrigi-lo
Para um gateway de ações de agente, o padrão mais limpo é rejeitar toda URL HTTP de saída cujo campo de nome de usuário ou senha analisado não esteja vazio. A maioria das integrações de API já envia credenciais em cabeçalhos de solicitação ou usa um injetor de credenciais. Aceitar userinfo amplia a superfície de ataque sem atender a uma necessidade comum de API.
A ordem da validação importa. Primeiro analise a string original. Rejeite URLs malformadas, esquemas sem suporte e userinfo antes da lista de permissões de hosts, dos pedidos de aprovação, dos redirecionamentos, da resolução DNS ou da seleção de credenciais. Essa sequência evita exibir uma solicitação como elegível quando você depois descartará parte dela.
Este pseudocódigo expressa a regra:
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)
O analisador deve retornar campos estruturados. Dividir em @, remover um prefixo ou procurar uma substring de hostname falham em variações comuns. Uma autoridade pode conter mais de um @ na entrada bruta. Um analisador decide qual delimitador é sintático e se os caracteres anteriores pertencem ao userinfo. Ele também trata literais IPv6 entre colchetes, portas explícitas, codificação por porcentagem e componentes vazios de modo mais consistente que lógica manual de strings.
Rejeitar userinfo dá ao chamador uma correção clara: use https://api.example.com/path e forneça a autenticação HTTP pelo caminho de credenciais indicado. Isso não deixa o chamador tentando descobrir se você mudou silenciosamente https://name@host para https://host.
Há um caso de compatibilidade que vale reconhecer. Algumas URLs antigas incorporam autenticação Basic como https://name:secret@host/path. Se uma migração precisar tratar essas URLs, faça isso em um caminho de importação único que extraia as credenciais para um armazenamento protegido, confirme o host analisado e exclua a string de origem do registro de importação quando a política permitir. Não deixe a API de ação em runtime continuar aceitando isso para sempre. Código temporário de compatibilidade costuma virar superfície de ataque permanente.
Listas de permissões de hosts precisam de rótulos analisados, não de strings amigáveis
Uma lista de permissões de destinos deve comparar hostnames analisados e normalizados, não executar uma busca de substring na URL bruta. A regra raw_url.includes("api.example.com") aceita tanto https://[email protected] quanto https://api.example.com.evil.invalid. Nenhuma delas é uma solicitação para api.example.com.
A correspondência exata de host é a regra menos surpreendente. Se uma integração precisa apenas de api.example.com, permita esse nome e rejeite todos os outros hostnames. Se ela realmente precisa de subdomínios, compare rótulos DNS: permita example.com e nomes que terminem em .example.com, mas rejeite badexample.com e example.com.evil.invalid.
Uma implementação clara tem esta forma:
function allowedHost(host, root) {
const h = host.toLowerCase().replace(/\.$/, "")
const r = root.toLowerCase().replace(/\.$/, "")
return h === r || h.endsWith("." + r)
}
Esse código pressupõe que o analisador de URL já forneceu um hostname e que o chamador já rejeitou userinfo. Ele não deve receber uma URL completa. Manter essas responsabilidades separadas impede que alguém chame depois passando uma string de autoridade com porta, nome de usuário ou sinal @.
Domínios internacionalizados também exigem uma decisão. Os navegadores normalmente serializam hostnames em ASCII usando processamento IDNA, enquanto uma pessoa pode ver texto Unicode. Use a mesma forma canônica que seu cliente de solicitações usa na comparação e mostre tanto o host canônico quanto uma forma legível quando forem diferentes. Não afirme que duas strings identificam o mesmo domínio só porque parecem semelhantes em uma fonte proporcional.
Endereços IP merecem sua própria regra. Uma lista de permissões de hostname não torna automaticamente seguro um literal IP, e a resolução DNS após a aprovação pode alterar o endereço alcançado por um hostname. Se seu modelo de ameaça inclui acesso a serviços locais, decida explicitamente se endereços privados, de loopback, link-local e locais IPv6 são permitidos. Rejeitar userinfo é necessário, mas não resolve sozinho a falsificação de solicitações do lado do servidor.
As portas também têm significado. https://api.example.com:8443 pode ser um endpoint válido de parceiro ou um serviço de administração inesperado. Registre a porta efetiva e inclua-a nas decisões de aprovação quando a integração restringir uma delas. Um rótulo de host sozinho não descreve todo o destino de rede.
Redirecionamentos devem passar pelo mesmo controle
Uma URL inicial permitida não torna permitido todo destino de redirecionamento. Redirecionamentos HTTP são novas instruções de destino fornecidas pelo servidor remoto, e um gateway de agente deve analisar e autorizar cada uma antes de segui-la.
Suponha que um agente solicite https://api.example.com/export. Esse host devolve uma resposta 302 com esta localização:
https://[email protected]/download?id=42
Um cliente que segue redirecionamentos automaticamente se conectará em seguida a receiver.invalid. Se o gateway aprovou apenas a primeira URL, sua lista de permissões e sua tela de aprovação já não descrevem a ação de rede realizada.
Trate os redirecionamentos como um loop com um limite escolhido para seu cliente. Para cada valor de localização, resolva uma referência relativa contra a URL aprovada atual, analise a URL absoluta resultante, aplique as mesmas regras de esquema, userinfo, host, porta e endereço e decida se deve prosseguir. Registre tanto a resposta de origem quanto o destino de redirecionamento analisado.
Não encaminhe credenciais entre hosts por padrão. Bibliotecas de cliente HTTP diferem quanto a manter um cabeçalho Authorization após um redirecionamento entre hosts, e cabeçalhos personalizados podem se comportar de outro modo. O comportamento mais seguro para o gateway associa uma credencial a um destino específico aprovado e cria uma nova solicitação de saída apenas depois que o destino do redirecionamento passa pela autorização. Um redirecionamento entre dois hosts do mesmo fornecedor pode ser esperado, mas deve ser uma regra explícita, não um acidente dos padrões da biblioteca.
O tratamento do método também exige atenção. Uma resposta 303 normalmente muda a solicitação seguinte para GET, enquanto 307 e 308 preservam o método e o corpo. Se um agente envia um corpo sensível, um redirecionamento que preserva o método pode enviá-lo a outro lugar. Registre o método usado em cada salto e mostre o destino final no resultado da ação.
Um cliente também pode ser configurado para não seguir redirecionamentos. Essa é uma escolha sensata para ferramentas de API restritas. Devolva a resposta de redirecionamento ao agente e exija que ele solicite explicitamente o próximo destino. Isso cria outro evento de aprovação, mas oferece ao operador um ponto claro para avaliar uma mudança de host. Para suporte HTTP amplo, redirecionamentos automáticos só são aceitáveis quando cada salto passa pelo mesmo controle.
Credenciais devem ser selecionadas após a validação do destino
A ordem perigosa é simples de descrever: escolher uma credencial porque a URL bruta contém um nome de serviço conhecido, analisar a URL e então enviar a solicitação. Um truque com @ pode transformar o texto conhecido em userinfo enquanto o segredo selecionado viaja para um host controlado por um invasor.
A ordem mais segura é igualmente direta. Primeiro, analise e valide a URL. Em seguida, autorize seu esquema, host, porta e qualquer estado de redirecionamento. Só então encontre uma credencial vinculada àquele destino aprovado e injete-a na solicitação de saída. Mantenha esse segredo fora dos argumentos de ferramentas, da memória do agente e dos valores de retorno.
Isso também resolve um erro de configuração menos dramático, mas comum. Uma credencial associada a api.example.com não deve ir automaticamente para uploads.example.com, mesmo que ambos os nomes estejam sob o mesmo domínio pai. Hosts diferentes costumam ter propriedade, terminação TLS, registros ou escopos de permissão distintos. Comece com a vinculação exata ao host. Amplie a correspondência apenas quando a integração documentar por que precisa disso.
A injeção de cabeçalho é preferível ao userinfo em URL porque separa destino de autenticação. Um registro de solicitação pode dizer que um cabeçalho de autorização foi injetado sem armazenar seu valor. O agente recebe a resposta necessária para continuar seu trabalho, não um segredo reutilizável.
O Sallyport segue essa separação ao manter credenciais de API e SSH em seu cofre criptografado e executar a ação de saída sem expor essas credenciais ao agente. Em qualquer gateway com esse modelo, rejeitar userinfo antes da busca de credenciais fecha a lacuna entre o destino pretendido por um operador e o host que recebe a solicitação.
Também não confie em um cabeçalho de solicitação fornecido pelo agente para identificar seu destino. O cabeçalho Host, a autoridade HTTP/2, o host da URL, a configuração de proxy e o nome de servidor TLS podem interagir de formas específicas do cliente. Um gateway deve controlar as configurações de conexão e derivá-las da URL analisada e validada. Se permitir cabeçalhos personalizados, trate-os como conteúdo da solicitação, não como permissão para reescrever o roteamento.
Cartões de aprovação devem mostrar primeiro o destino analisado
Um cartão de aprovação deve responder a três perguntas concretas sem pedir que seu leitor reconstrua uma URL: qual processo solicitou, qual operação será executada e qual host a receberá. Coloque o hostname e a porta analisados em uma linha de destino dedicada. Deixe o método HTTP e o caminho próximos. Mostre a URL original como evidência de apoio, não como o único sinal de destino.
Para a solicitação de aparência malformada usada antes, um cartão útil diria:
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
Ele não deve exibir api.example.com como um selo derivado do lado esquerdo da autoridade. Também não deve dizer apenas External HTTP request, pois isso não dá a uma pessoa base relevante para decidir.
Aprovação por sessão e aprovação por chamada resolvem problemas humanos diferentes. Uma aprovação por sessão diz que um processo em execução específico pode usar um gateway durante sua vida útil. Uma aprovação por chamada diz que uma credencial ou ação especialmente sensível exige uma nova decisão humana. Nenhuma delas substitui a validação básica de URL. Um processo em que você confia ainda pode ser manipulado por um comentário não confiável em uma issue, um campo de metadados de pacote ou um arquivo de configuração gerado.
A escada de decisões do Sallyport mantém um cofre bloqueado como interrupção absoluta e, depois, usa autorização por sessão e confirmação opcional por segredo. Essa estrutura funciona melhor quando destinos malformados falham antes de chegar a um cartão de aprovação, pois um operador não deveria ter de decidir se um elemento de pontuação da URL mudou o endpoint.
Seja específico nas mensagens de recusa. Userinfo is not allowed in outbound URLs informa a quem desenvolve o agente o que corrigir. Invalid request provoca novas tentativas, escapes improvisados e pressão para enfraquecer a validação. Evite repetir uma senha se o analisador a extraiu. A mensagem pode mencionar o componente proibido sem reproduzir seu conteúdo.
Os testes devem usar entradas enganosas, não apenas URLs ideais
Uma suíte de testes de validação precisa de exemplos feitos para enganar leitores humanos e verificações simples de strings. Aprovar https://api.example.com/v1 quase não prova nada sobre a fronteira em que um agente pode enviar texto arbitrário.
Comece com casos como estes e confirme o host analisado, a decisão e o 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
Adicione um fixture de redirecionamento. Faça um servidor de teste permitido emitir um redirecionamento cuja localização contenha userinfo e confirme que o cliente registra um segundo salto bloqueado sem enviar uma solicitação ao servidor de destino. Isso detecta o erro frequente em que a validação da URL inicial fica em um caminho de código e o tratamento de redirecionamentos fica dentro de um callback da biblioteca.
Teste a codificação por porcentagem deliberadamente, mas não suponha que todo @ codificado tenha o mesmo efeito. Em muitos analisadores, %40 em um caminho continua sendo dado de caminho, enquanto um @ real na autoridade funciona como delimitador. Envie a string bruta exata ao analisador usado em produção e confirme seus campos. O invariante importante não é uma regra caseira de decodificação. É que um campo userinfo analisado e não vazio não pode chegar ao emissor.
Teste também a renderização de logs e aprovações. Um controle de segurança pode fazer a recusa correta e ainda criar um registro operacional ruim se a exibição cortar o host real ou expuser texto de senha analisado. Vale ter testes de snapshot para linhas de destino, pois regressões visuais costumam chegar por mudanças de design aparentemente inofensivas.
Por fim, teste a cadeia de auditoria e o caminho de revogação em torno de uma chamada recusada. Uma recusa deve ficar visível o bastante para investigação, mas nunca deve incluir segredos injetados. O registro útil contém a solicitação bruta sob controles de acesso adequados, o esquema e o host analisados, o motivo da decisão, a identidade do processo chamador e o fato de que nenhuma ação de saída ocorreu.
A sintaxe de URL não é uma linguagem de política
Algumas equipes respondem a casos extremos de URL adicionando uma pilha cada vez maior de exceções: permitir um nome de usuário para determinado fornecedor, aceitar uma porta especial para aquele ambiente, confiar em um redirecionamento apenas quando um cabeçalho parece correto e corrigir um comparador de strings a cada incidente. Essa abordagem parece flexível porque evita recusar uma solicitação estranha. Ela também cria regras que ninguém consegue revisar com segurança.
Mantenha a regra pequena. Solicitações de saída têm um destino analisado. Userinfo é rejeitado. O esquema, host, porta e classe de endereço permitidos são explícitos. Redirecionamentos passam novamente pelas mesmas verificações. Credenciais são selecionadas apenas depois que o destino é aprovado. Cada decisão produz um registro que informa claramente o host analisado.
Essa regra recusará algumas URLs antigas que um navegador aceitaria. Ótimo. Um agente autônomo não precisa de todos os recursos históricos da barra de endereços de um navegador. Ele precisa de uma interface restrita que dificulte confundir a relação entre ação, destino e credencial.
Se você precisar mudar essa interface, torne a exceção visível como uma capacidade nomeada, com testes e uma decisão de expiração. Não a esconda em código de limpeza de URL. A primeira entrada hostil encontrará a diferença entre o texto que parece um host e o host que seu cliente realmente contata.
FAQ
Um sinal @ em uma URL pode mudar o host de destino?
Sim. Em uma URL como https://[email protected]/path, o host de destino é evil.example, não api.example.com. O texto antes de @ é userinfo, portanto aceitá-lo em URLs fornecidas por agentes abre espaço para erros fáceis de revisão humana.
Userinfo em URL é uma sintaxe válida?
É uma sintaxe de URL válida, embora muitos clientes de API não tenham motivo para aceitá-la. A RFC 3986 permite userinfo opcional em uma autoridade, mas algo ser permitido pela gramática não é motivo para levá-lo através da fronteira de uma ação de agente.
Um gateway de API deve rejeitar URLs com userinfo?
Rejeite-a na fronteira, a menos que exista uma necessidade de compatibilidade bem delimitada. Não a remova e prossiga silenciosamente, pois isso altera a solicitação enviada pelo chamador e deixa um registro enganoso.
user:[email protected] envia tráfego para user?
Não. https://user:[email protected]/v1 tem como hostname api.example.com; user:pass é userinfo. Um analisador expõe esses campos separadamente, e as verificações de segurança devem usar o hostname analisado, não a string bruta.
Devo colocar credenciais de autenticação Basic em uma URL?
A autenticação Basic deve ficar em um cabeçalho HTTP Authorization, não embutida em uma URL. Credenciais na URL vazam para logs, históricos, comandos copiados e mensagens de erro com muito mais facilidade do que cabeçalhos.
Redirecionamentos tornam mais difícil validar userinfo em URL?
Todo destino de redirecionamento precisa passar pelas mesmas verificações de análise e autorização da URL original. Verificar apenas a primeira URL permite que um endpoint público autorizado redirecione um agente para um host não aprovado.
api.example.com.evil.example é o mesmo que api.example.com?
Não. api.example.com.evil.example é um subdomínio de evil.example, enquanto [email protected] de fato aponta para api.example.com. Primeiro analise o host e depois compare os rótulos de domínio segundo sua regra explícita de lista de permissões.
Como devo validar uma URL de API de saída?
Evite regexes aplicadas à URL inteira. Use um analisador compatível com os padrões, rejeite userinfo explicitamente, exija HTTPS quando fizer sentido, normalize o hostname analisado e compare-o com os hosts ou sufixos de domínio permitidos.
O que um log de auditoria deve registrar para uma solicitação HTTP de agente?
Um bom registro de auditoria preserva a entrada bruta para investigação e armazena campos analisados separadamente: esquema, hostname, porta, caminho, destino de redirecionamento e decisão. Exiba o hostname analisado em destaque para que quem revisa não precise interpretar mentalmente a pontuação.
Como testo uma ferramenta de agente contra destinos de URL disfarçados?
A correção costuma ser uma regra de validação pequena, mas ela deve ficar antes das aprovações e da injeção de credenciais. Adicione casos de teste para @, delimitadores codificados por porcentagem, vários caracteres @, redirecionamentos, hosts com maiúsculas e minúsculas misturadas e pontos finais, para que uma refatoração futura não reabra a falha.