# Isolamento de credenciais em pools de conexões HTTP para agentes de IA

Uma conexão HTTP reutilizada não é automaticamente um vazamento de credenciais. Tratá-la como inofensiva faz as equipes ignorarem a falha real: um objeto de cliente acumula cookies, respostas a desafios, cabeçalhos de redirecionamento, identidade de proxy ou identidade TLS, e depois uma solicitação usa esse estado com outra credencial.

Isolar credenciais em um pool de conexões HTTP significa decidir exatamente qual estado pode passar de uma solicitação para outra e impedir que o restante seja compartilhado. Só o host de destino costuma ser um limite amplo demais para um executor de ações ou um agente que pode agir por várias contas. Um cliente rápido que, ocasionalmente, envia o cookie da conta errada é pior que um cliente mais lento, porque o erro parece uma solicitação bem-sucedida normal.

## Um soquete é transporte, não propriedade de conta

Uma conexão TCP transporta bytes para uma origem. Ela não oferece à aplicação um limite confiável entre contas. O HTTP/1.1 reutiliza a conexão de forma sequencial, e o HTTP/2 pode executar muitos fluxos ao mesmo tempo, mas nenhum dos dois protocolos diz que todas as solicitações em uma conexão pertencem ao mesmo usuário ou conta de serviço.

Essa diferença importa porque bibliotecas de cliente costumam reunir questões sem relação sob uma interface conveniente chamada sessão, agente, cliente ou transporte. Então, desenvolvedores criam um desses objetos por host e adicionam um token bearer antes de cada chamada. O cabeçalho bearer pode estar correto sempre, enquanto um armazenamento de cookies, cache de desafios digest ou contexto de proxy pertence silenciosamente à última conta que usou o objeto.

A RFC 9110 trata a autenticação como um comportamento de solicitação e espaço de proteção. Um cliente responde a um desafio para uma origem, esquema e domínio específicos. Ela não diz que uma conexão aberta pertence a uma pessoa ou conta de serviço. Se o cliente presume essa propriedade, a premissa vem do seu código ou da biblioteca, não do HTTP.

Reutilizar conexões continua sendo desejável. Isso evita handshakes repetidos, reduz a rotatividade de portas e ajuda um serviço remoto sob carga. A regra correta é mais restrita: reutilize uma conexão apenas entre solicitações cujo estado no nível da conexão e estado gerenciado pelo cliente sejam compatíveis de forma intencional.

Os estados que vale separar se dividem em dois grupos:

- O estado da solicitação inclui Authorization, Cookie, cabeçalhos da conta, corpos de solicitação e valores de idempotência. Quem chama deve criar tudo isso de novo para cada ação.
- O estado da conexão e do cliente inclui uma entrada do pool, sessão de proxy, seleção de certificado de cliente, comportamento de redirecionamento, cookies, caches de desafios e configurações de protocolo. O cliente precisa delimitar ou desativar cada um deles deliberadamente.

Uma credencial bearer usada somente em um cabeçalho Authorization pode compartilhar uma conexão TCP com outra credencial bearer se a biblioteca enviar cabeçalhos por solicitação e não guardar estado de conta. Essa é uma afirmação limitada, não uma autorização geral. Quando o serviço também define um cookie de sessão, redireciona para outro host ou pede um certificado de cliente, o projeto do pool precisa ser revisto.

## Cookies são estado de conta, mesmo quando seus nomes parecem inofensivos

Um armazenamento de cookies é a fonte mais comum de cruzamento acidental entre contas. As equipes veem uma API com token bearer e presumem que cookies não importam, até que um balanceador de carga, endpoint de login interativo ou serviço antigo acrescente `Set-Cookie` a uma resposta. Um cliente HTTP genérico normalmente o armazena, a menos que você mande que não faça isso.

A RFC 6265 define como um agente de usuário seleciona cookies por domínio, caminho, atributos de segurança e regras relacionadas. Seu modelo de armazenamento não tem um campo para "o registro de credencial que recebeu este cookie". Duas credenciais bearer que chamam o mesmo host e caminho podem, portanto, se qualificar para o mesmo cookie armazenado. O protocolo segue suas regras perfeitamente enquanto sua aplicação cruza contas.

Considere este rastreamento de um serviço de teste. A conta alpha usa um token bearer e recebe um cookie de afinidade:

```http
GET /v1/whoami HTTP/1.1
Host: api.example.test
Authorization: Bearer alpha-token

HTTP/1.1 200 OK
Set-Cookie: route=alpha-node; Path=/; Secure; HttpOnly
Content-Type: application/json

{"account":"alpha"}
```

A próxima ação seleciona beta. Se um armazenamento compartilhado enviar o cookie guardado, a solicitação terá dois sinais de propriedade:

```http
GET /v1/whoami HTTP/1.1
Host: api.example.test
Authorization: Bearer beta-token
Cookie: route=alpha-node
```

O melhor resultado é o serviço rejeitar a incompatibilidade. Serviços menos cuidadosos encaminham a solicitação de beta pela sessão persistente de alpha, associam-na ao contexto errado do lado do servidor ou aceitam o cabeçalho que prevalecer. O cliente não pode contar com o serviço remoto para corrigir uma solicitação misturada.

Para APIs de máquina para máquina, minha regra padrão é direta: desative o armazenamento automático de cookies. Se uma API realmente exigir cookies, crie um armazenamento de cookies para um registro de credencial e um contexto de serviço pretendido. Não o compartilhe apenas porque a string do host é igual.

Verifique também as entradas da solicitação antes de injetar a credencial. Um agente, plugin ou chamador não pode poder fornecer seu próprio cabeçalho `Cookie` que permaneça em uma chamada com credenciais. Remova cabeçalhos de autenticação e cookies do ambiente, depois adicione apenas os cabeçalhos permitidos pela definição da ação. Caso contrário, você criou isolamento em torno de uma porta que os chamadores podem contornar.

## Desafios de autenticação precisam do chamador atual

Uma resposta 401 não é apenas um código de erro. Ela pode convidar o cliente a tentar de novo após selecionar credenciais, e essa lógica de seleção costuma ficar fora do código que criou a solicitação original. Manipuladores de autenticação Basic e Digest são especialmente propensos a esse padrão, mas um middleware personalizado pode cometer o mesmo erro com tokens bearer.

Uma implementação ruim mantém um `currentCredential` mutável em um cliente compartilhado. A solicitação A recebe um desafio, o manipulador carrega o segredo de alpha e o cliente tenta de novo. A solicitação B começa antes de a nova tentativa terminar e muda `currentCredential` para beta. Agora, o manipulador de desafios tem uma condição de corrida que as suítes de teste não percebem porque executam solicitações uma de cada vez.

Não corrija isso colocando um bloqueio em torno de um campo global de credencial. O bloqueio serializa o trabalho e ainda deixa o objeto errado responsável pela identidade. Vincule o registro de credencial ao contexto da solicitação, leve-o por cada nova tentativa e rejeite uma nova tentativa se o contexto estiver ausente.

A autenticação Digest merece desconfiança extra. Ela envolve nonces, domínios, contadores e uma resposta calculada. Esses valores estão vinculados ao espaço de proteção que apresentou o desafio, não a um cliente compartilhado genérico. A autenticação Basic é mais simples no protocolo, mas um cache que a adiciona automaticamente a solicitações posteriores ainda precisa do mesmo escopo de credencial.

Use um endpoint de teste que devolva um domínio ou desafio distinto para cada conta. Em seguida, execute solicitações simultâneas e confirme que cada nova tentativa usa o registro de credencial anexado à própria ação. Um teste que verifica apenas o status final 200 é fraco demais. Registre a identidade da solicitação no servidor e falhe quando o desafio de alpha produzir uma tentativa de autorização de beta.

Não confunda um 403 com um desafio. Servidores usam 403 para muitas decisões de autorização, e clientes não devem tratá-lo como permissão para procurar outra credencial. Fazer fallback automático entre credenciais transforma uma falha de acesso em sondagem de contas, algo inseguro e difícil de auditar.

## O HTTP/2 facilita ocultar erros de propriedade

O HTTP/2 permite que vários fluxos compartilhem uma conexão TLS. Isso é eficiente, mas elimina o antigo sinal visual de uma solicitação esperando atrás de outra. Um cliente pode enviar ações de alpha e beta no mesmo instante, e seus cabeçalhos só permanecem separados se a biblioteca os modelar como solicitações distintas até o fim.

A RFC 9113 proíbe campos de cabeçalho específicos de conexão HTTP/1.1, como `Connection`, em solicitações HTTP/2. Essa regra não dá escopo de conta a cookies ou autenticação. Ela apenas impede que uma classe de comportamento entre saltos seja expressa como cabeçalho comum de solicitação. O cliente continua responsável por selecionar cookies, novas tentativas, redirecionamentos e qualquer middleware compartilhado.

A coalescência de conexões HTTP/2 acrescenta outra particularidade. Alguns clientes podem usar uma conexão segura para mais de uma origem quando as verificações de certificado e nome permitem. A coalescência não repete sozinha um cabeçalho Authorization. Porém, ela significa que uma identidade de pool baseada em uma comparação informal de host pode não descrever a conexão que o cliente realmente escolheu. Mantenha as decisões de autorização de origem separadas da otimização de transporte e teste o comportamento da biblioteca que você usa.

A compressão de cabeçalhos também preocupa as pessoas pelo motivo errado. HPACK comprime cabeçalhos dentro de uma conexão HTTP/2, e QPACK faz algo relacionado no HTTP/3. Um cliente em conformidade não decodifica o valor Authorization de uma solicitação anterior em outra posterior. O perigo é a reutilização direta de estado no código do cliente, além de possíveis preocupações com metadados em modelos de ameaça incomuns. Marque cabeçalhos sensíveis como nunca indexados quando a biblioteca expuser esse controle, mas não trate isso como substituto para isolar solicitações.

O HTTP/3 muda o transporte de TCP para QUIC. Ele não muda a regra de propriedade. Um pool que deixa um armazenamento de cookies ou cache de credenciais transitar livremente entre ações continua errado sobre QUIC.

Certificados de cliente são diferentes. Um certificado de cliente TLS é escolhido durante o estabelecimento da conexão, por isso está de fato vinculado à conexão. Nunca multiplexe identidades de certificados de cliente diferentes pela mesma conexão ou entrada de pool. Coloque o registro do certificado na identidade do pool ou dê a cada certificado uma instância de cliente separada. O mesmo cuidado vale para um proxy que autentica uma conexão antes de encaminhar solicitações.

## Torne a identidade do pool explícita no código

A identidade do pool deve incluir cada valor que muda o que uma nova conexão pode significar com segurança. Para uma API somente com bearer, com cookies desativados e sem identidade de proxy, isso pode ser a origem mais o identificador do registro de credencial. Para um certificado de cliente, caminho autenticado por proxy ou perfil TLS especial, inclua esses identificadores também.

Não use o texto secreto como identificador do mapa. Isso cria cópias desnecessárias do segredo na memória e nos logs, complica a rotação e tenta alguém a imprimir o mapa durante a depuração. Use um ID opaco de registro de credencial, cujo ciclo de vida seja controlado pelo seu cofre ou registro de ações.

Este esboço em TypeScript mostra o formato. Ele usa o `Pool` do Undici, mas o limite se aplica a qualquer biblioteca de cliente. O `credentialId` é um identificador, não o valor bearer.

```ts
import { Pool } from "undici";

type Boundary = {
  origin: string;
  credentialId: string;
  proxyId?: string;
  clientCertificateId?: string;
};

const pools = new Map<string, Pool>();

function poolId(boundary: Boundary): string {
  return [
    boundary.origin,
    boundary.credentialId,
    boundary.proxyId ?? "direct",
    boundary.clientCertificateId ?? "none"
  ].join("\u001f");
}

function poolFor(boundary: Boundary): Pool {
  const id = poolId(boundary);
  let pool = pools.get(id);
  if (!pool) {
    pool = new Pool(boundary.origin);
    pools.set(id, pool);
  }
  return pool;
}

function headersFor(token: string, input: HeadersInit = {}): Headers {
  const headers = new Headers(input);
  headers.delete("authorization");
  headers.delete("cookie");
  headers.delete("proxy-authorization");
  headers.set("authorization", `Bearer ${token}`);
  return headers;
}
```

O esboço impede que um cabeçalho Authorization ou Cookie fornecido pelo chamador siga junto com a credencial injetada. Ele não implementa um armazenamento de cookies, redirecionamentos, despacho por proxy nem configuração de certificados. A omissão é intencional: cada um exige uma decisão consciente, em vez de um padrão acidental.

Se todas as credenciais falam com o mesmo endpoint público anônimo, pools separados podem ser desnecessários. Se o endpoint vê contas diferentes, use entradas de pool separadas até ter um motivo específico e evidências para compartilhar. Um pool custa pouco comparado a confundir contas.

A rotação precisa de sua própria regra. Quando um registro de credencial muda, deixe de atribuir novas ações ao pool antigo. Permita que solicitações já enviadas terminem com o registro original, se a semântica da ação permitir, e depois feche esse pool. Não redirecione uma nova tentativa em andamento para o registro substituto. Uma rotação muda a autoridade, não corrige o significado de um trabalho já iniciado.

Uma recomendação popular diz para desativar keep-alive em toda parte. Isso reduz o número de objetos de transporte compartilhados e parece seguro após um incidente. Também oculta se cookies, código de redirecionamento e caches de desafios têm o escopo correto, enquanto cria carga de handshake nova a cada solicitação. Mantenha o pool, explicite sua propriedade e teste os caminhos difíceis.

## Redirecionamentos podem enviar uma solicitação limpa ao lugar errado

O tratamento de redirecionamentos é um segundo cliente escondido dentro do primeiro. Uma biblioteca recebe uma resposta 301, 302, 303, 307 ou 308, constrói outra solicitação e decide quais cabeçalhos permanecem. Se essa decisão acontecer depois do código que delimita credenciais, ela pode levar um cabeçalho de conta ou cookie para um destino não pretendido.

Para ações com credenciais injetadas, comece com redirecionamentos desativados ou manuais. Inspecione a origem de destino antes de seguir um. Permita redirecionamentos somente quando a definição da ação os esperar, o destino estiver no conjunto aprovado de origens e o cliente recriar cabeçalhos a partir do contexto original da credencial, em vez de copiar um conjunto antigo de cabeçalhos.

O código de status importa. Um 303 pode mudar uma solicitação para GET no comportamento comum de navegadores, enquanto 307 e 308 preservam método e corpo. Um cliente que tenta novamente e trata todos do mesmo modo pode repetir uma gravação autenticada em outro endpoint. Isso prejudica a integridade da ação mesmo quando nenhum segredo cruza uma origem.

Remova os cabeçalhos Authorization e Cookie em todo redirecionamento entre origens. Em muitos clientes, isso já é o padrão, mas padrões não são resultado de teste. Verifique a versão e a configuração que você usa. Redirecionamentos na mesma origem ainda precisam de uma lista de caminhos permitidos quando a credencial concede mais autoridade do que o endpoint original deveria receber.

A autenticação de proxy precisa do mesmo tratamento. `Proxy-Authorization` pertence ao caminho de proxy selecionado, não ao serviço de origem. Nunca a coloque em um mapa genérico de cabeçalhos padrão. Se duas ações alcançam a mesma API por proxies autenticados diferentes, separe suas entradas de pool e torne a seleção de proxy visível no registro da ação.

## Um teste de isolamento com falha tem um padrão reconhecível

A maioria das equipes testa que alpha pode chamar a API e que beta pode chamar a API. Isso prova que as credenciais funcionam. Não prova que o mesmo cliente de longa duração pode mudar de alpha para beta sem levar bagagem.

Crie um pequeno servidor de teste com duas contas e faça-o informar o que recebeu. Ele deve devolver a conta derivada de Authorization, reproduzir o cabeçalho Cookie recebido, definir um cookie específico da conta e expor um endpoint de redirecionamento. O serviço deve rejeitar uma solicitação quando o rótulo de conta do cookie for diferente do rótulo de conta bearer. A rejeição torna o erro visível, em vez de deixar uma camada de afinidade escondê-lo.

Execute esta sequência com a configuração exata de cliente usada em produção:

1. Envie uma solicitação alpha por um pool novo. Confirme que a resposta define `route=alpha`.
2. Envie uma solicitação beta pela busca de pool usada em produção. Confirme que o servidor vê a autorização beta e um cabeçalho Cookie vazio, a menos que beta possua um armazenamento separado que contenha apenas cookies de beta.
3. Inicie solicitações alpha e beta ao mesmo tempo por HTTP/2. Faça cada endpoint pausar antes de responder para que seus fluxos se sobreponham e então confirme as identidades e cookies de forma independente.
4. Devolva um desafio 401 para cada conta e verifique que a nova tentativa usa o registro de credencial original.
5. Devolva um redirecionamento para outra origem e verifique que a solicitação seguinte não tem cabeçalho de autorização nem cookie.

As verificações devem registrar mais do que códigos de status. Registre o ID do registro de credencial selecionado, ID do pool, origem, versão do protocolo, destino do redirecionamento, presença de Cookie e conta da resposta. Não registre a credencial em si. Quando o teste falhar, esses campos identificam se o vazamento veio da busca de pool, de cabeçalhos do ambiente, de um armazenamento de cookies ou de middleware de novas tentativas.

Teste também a rotação de credenciais. Inicie uma solicitação que fique aguardando no servidor, substitua o registro de credencial e depois emita uma nova solicitação. A nova ação deve usar uma nova identidade de pool. Decida e documente se a ação em espera termina com sua autoridade original ou é cancelada. Ambas as opções podem ser defensáveis; mudar silenciosamente a autoridade durante a execução não é.

Execute o teste por qualquer configuração de proxy compatível. O estado do proxy e o estado da origem muitas vezes ficam em camadas diferentes da biblioteca, tornando este o ponto em que um teste unitário tranquilizador se transforma em um teste de integração útil.

## Ações de agentes precisam de uma superfície menor de autoridade

Um agente autônomo de programação deve pedir uma ação como "chame esta API aprovada usando o registro de credencial billing-read". Ele não deve receber um token e construir um cliente amplo e duradouro que possa manter estado de sessão após a tarefa mudar. O executor de ações pode vincular o destino aprovado, método, registro de credencial e limite do cliente antes mesmo de enviar bytes.

O Sallyport segue essa separação ao manter credenciais de API e SSH em seu cofre criptografado e executar a ação por conta própria, para que o agente receba o resultado, não a credencial. Essa contenção só ajuda se o caminho HTTP também tratar cada registro de credencial como seu próprio contexto de autoridade.

A aprovação não substitui o isolamento do cliente. Um operador pode aprovar uma ação beta legítima enquanto um armazenamento compartilhado de cookies descuidado a transforma em uma solicitação mista de alpha e beta. O registro de aprovação passa a documentar uma ação diferente daquela que o operador pretendia permitir. Coloque o limite abaixo da tela de aprovação, onde cabeçalhos, novas tentativas e seleção de transporte de fato acontecem.

Para um gateway de ações, registre o ID do registro de credencial e a identidade do pool no evento de auditoria, mas nunca o segredo. Se um chamador relatar uma discrepância de conta, será necessário estabelecer se o executor selecionou a credencial correta e anexou apenas o estado que esse registro possui. Uma trilha de atividades inviolável ajuda a investigar essa questão; ela não torna seguro um projeto de cliente ambíguo.

A primeira mudança prática é simples: encontre cada cliente HTTP compartilhado e liste o que ele guarda além de uma conexão aberta. Se a resposta incluir cookies, respostas a desafios, redirecionamentos, certificados de cliente ou identidade de proxy, dê um proprietário explícito a cada item antes que a próxima rotação de token ou execução simultânea de agente torne o erro caro.
