8 min de leitura

Como as assinaturas de requisições HTTP limitam chamadas de APIs por agentes de IA

Assinaturas de requisições HTTP vinculam chamadas de API operadas por IA ao método, destino, corpo e horário. Aprenda sobre campos seguros, proteção contra replay e regras para diferenças de relógio.

Como as assinaturas de requisições HTTP limitam chamadas de APIs por agentes de IA

Um agente de IA com um token bearer tem, na prática, a mesma liberdade de qualquer outro processo que consiga ler esse token. Ele pode chamar agora um endpoint permitido, tentar de novo mais tarde, enviar o token para outro host se o código permitir e fazer uma requisição cujo corpo tenha pouca relação com a instrução do usuário que iniciou a execução. O servidor vê a posse da credencial e pouco mais.

As assinaturas de requisições HTTP melhoram essa situação ao vincular a autorização a uma mensagem específica. Um bom projeto pode tornar uma assinatura válida apenas para POST https://api.example.test/v1/releases/42, apenas com o corpo exato preparado pelo agente, apenas por um período curto e apenas sob uma identidade de assinatura. Esse é um controle útil. Ele não substitui autorização, aprovação nem isolamento de credenciais.

As equipes erram nisso de duas formas opostas. Algumas mantêm tokens bearer porque assinar parece complicado e depois descobrem que um agente pode usar um token com escopo amplo em qualquer lugar para onde consiga fazer uma requisição de saída. Outras assinam todos os cabeçalhos emitidos pela biblioteca HTTP e constroem um verificador tão frágil que proxies comuns, diferenças de relógio ou uma atualização da biblioteca derrubam a produção. O projeto certo vincula os campos que alteram o significado ou o destino de uma ação e torna a validade e a verificação simples o bastante para operar.

Assinaturas restringem uma mensagem, enquanto tokens bearer apenas provam a posse

Um token bearer responde a uma pergunta: esta requisição apresentou um segredo aceito no momento? Normalmente, ele não vincula o segredo ao método, destino, corpo ou horário da requisição. Tokens de acesso OAuth podem carregar escopos, audiências e expiração, o que ajuda, mas o detentor ainda pode usar cada escopo permitido até o vencimento.

Uma assinatura de requisição responde a uma pergunta criptográfica mais específica: o detentor desta chave de assinatura autorizou este conjunto definido de componentes da requisição? O servidor reconstrói a entrada assinada, verifica a assinatura com a chave pública registrada ou com um segredo compartilhado, confere os limites de tempo e então aplica a autorização normal. Essa ordem importa. A verificação da assinatura estabelece a integridade da mensagem e a identidade do assinante. A autorização decide se essa identidade pode executar a ação.

Considere um serviço de implantação com um token bearer limitado à criação de releases. Primeiro, um agente cria uma release de staging inofensiva. Depois, uma prompt injection orienta o mesmo agente a criar uma release de produção. O token permite as duas requisições se o escopo abranger os dois ambientes. Trocar o token por uma requisição assinada não corrige esse erro de autorização. Se a identidade de assinatura puder criar releases de produção, ela poderá assinar uma requisição de produção.

As assinaturas oferecem ao serviço proteções que um cabeçalho bearer comum não oferece:

  • Uma requisição assinada capturada expira rapidamente e não pode ser repetida para sempre.
  • Uma assinatura copiada normalmente não pode ser transferida de POST /v1/staging/releases para POST /v1/production/releases.
  • Um corpo JSON alterado falha na verificação quando o resumo do corpo é assinado.
  • O serviço pode identificar uma credencial de assinatura sem aceitá-la como um valor de cabeçalho reutilizável.
  • Um verificador pode registrar exatamente quais campos o assinante aprovou.

Não descreva isso como uma substituição de tokens de acesso em toda arquitetura. Muitos sistemas usam os dois. Um token de acesso pode identificar o usuário delegado ou a carga de trabalho, enquanto uma assinatura de requisição vincula a mensagem individual a uma credencial de assinatura específica do agente. Em outros sistemas, uma requisição assinada autentica diretamente o chamador, e o servidor determina as permissões a partir da identidade de assinatura.

A diferença tem uma consequência prática para agentes de IA: uma assinatura transforma a ação em um objeto concreto que pode ser inspecionado, aprovado e registrado. Um token bearer aparece principalmente como uma capacidade disponível no ambiente. Essa diferença torna a revisão possível, mas somente se o limite da ferramenta impedir que o agente extraia o segredo de assinatura.

Use a RFC 9421 em vez de inventar uma string canônica

A RFC 9421, HTTP Message Signatures, define uma forma estruturada de declarar os componentes cobertos e enviar uma assinatura. Ela evita o formato privado comum em que um lado junta campos com caracteres de nova linha, outro normaliza uma URL de maneira diferente e os dois culpam a criptografia quando a verificação falha.

A RFC separa duas ideias. Signature-Input declara um rótulo, os componentes cobertos e parâmetros como created, expires, nonce, alg e keyid. Signature carrega o valor criptográfico resultante sob o mesmo rótulo. O verificador constrói a base da assinatura a partir dos componentes e parâmetros declarados e então a verifica.

Uma requisição compacta pode ser assim:

POST /v1/releases/42?environment=staging HTTP/1.1
Host: api.example.test
Content-Type: application/json
Content-Digest: sha-256=:rPMyV6WTE4Duf0JApE9tXvDYy9EzrgFQbq3e2XTCwbs=:
Signature-Input: sig1=("@method" "@authority" "@path" "@query" "content-digest" "date");created=1735689600;expires=1735689660;keyid="agent-release-17";alg="ed25519"
Signature: sig1=:BASE64_SIGNATURE_BYTES:
Date: Wed, 01 Jan 2025 00:00:00 GMT

{"version":"2025.01.01","notes":"staging validation"}

O valor do resumo acima ilustra o formato transmitido, não é o resumo do JSON de exemplo. Um cliente de produção calcula o resumo a partir dos bytes exatos que enviará. Se ele serializar o JSON uma segunda vez depois da assinatura, terá criado uma fonte de falhas.

A RFC 9421 é propositalmente flexível. Isso é útil para intermediários e diferentes versões de HTTP, mas significa que o contrato da sua API precisa definir um perfil exato. Declare o algoritmo permitido, os componentes obrigatórios, a duração máxima da assinatura, o formato de keyid, o algoritmo de resumo aceito e se um nonce é necessário. Se o contrato disser apenas que as requisições devem ser assinadas, cada autor de cliente fará suposições diferentes.

Ed25519 é uma boa opção padrão quando o serviço pode registrar chaves públicas. O servidor armazena uma chave pública de verificação, e a perda desse registro público não expõe um segredo de assinatura. Assinaturas HMAC podem funcionar quando um componente confiável e a API compartilham um segredo, mas esse segredo precisa existir nos dois lados. Em fluxos com agentes, isso costuma aumentar o número de lugares onde um segredo reutilizável pode vazar.

Evite um esquema proprietário, a menos que uma restrição de protocolo o exija. Formatos proprietários tendem a assinar a URL bruta em um cliente, o caminho decodificado em outro e uma representação diferente do host no verificador. A RFC 9421 oferece identificadores de componentes definidos e campos estruturados. Use-os e teste o perfil exato que você publica.

Vincule o método, o destino e a consulta que definem a ação

Um agente deve assinar todos os campos da requisição que alterem para onde ela vai ou qual operação do servidor será chamada. Para a maioria das chamadas de API, o conjunto mínimo útil é @method, @authority, @path e @query. Você pode usar @target-uri quando quiser um componente que cubra toda a URI de destino, mas não assine os dois formatos sem uma razão que os implementadores consigam explicar.

@method impede que alguém reutilize uma assinatura destinada a GET como DELETE. Isso parece óbvio, mas esquemas montados manualmente frequentemente o omitem porque um engenheiro presume que o caminho determina a operação. APIs REST costumam reutilizar um caminho com métodos diferentes. O método muda a ação.

@authority vincula o host e a autoridade da porta. Isso impede que uma assinatura emitida para uma origem de API seja validada contra outra origem que aceite a mesma credencial. Isso importa em organizações com hosts de preview, staging e produção. Uma identidade de assinatura destinada a staging não deve adquirir autoridade sobre produção porque um agente ou redirecionamento alterou o host.

@path e @query exigem o mesmo cuidado. Muitas APIs colocam parâmetros importantes na string de consulta:

POST /v1/invoices/817/refund?amount=2500&currency=USD

Se a assinatura cobrir apenas o caminho, um invasor capaz de alterar a requisição em trânsito poderá mudar o valor ou a moeda. Se o servidor obtém dry_run, environment, force, page_size, include_deleted ou um seletor de tenant da consulta, esses valores fazem parte da ação. Assine @query.

A RFC 9421 também define @query-param, que pode cobrir um parâmetro nomeado específico. Ele é útil em protocolos nos quais alguns parâmetros da consulta ficam explicitamente fora da decisão de segurança, como dados de rastreamento. Para uma API interna usada por agentes, assinar a consulta inteira costuma causar menos surpresas. Cada parâmetro passa a fazer parte da requisição aprovada, e os revisores não precisam se lembrar de uma lista de exceções.

Não trate a normalização de URLs de forma casual. O verificador precisa usar a semântica dos componentes especificada pela RFC 9421 e pela biblioteca escolhida. Não decodifique escapes percentuais e os recodifique manualmente. Não ordene parâmetros de consulta repetidos, a menos que a definição do componente selecionado faça isso. O destino de uma requisição são bytes transmitidos pela rede antes de se tornar um objeto conveniente da aplicação.

Estabeleça uma regra firme para redirecionamentos: não carregue automaticamente uma requisição assinada através de um redirecionamento para outra autoridade. Uma assinatura que cubra a autoridade original deve falhar no novo host, e isso está correto. Deixe o cliente receber o redirecionamento, aplicar uma lista de permissões explícita, construir uma nova requisição e assinar essa nova requisição. Para requisições que alteram dados, muitas equipes deveriam rejeitar redirecionamentos completamente.

Um corpo assinado precisa de um resumo, não de otimismo sobre JSON

Assine content-digest quando o corpo influenciar o resultado. Isso inclui quase todas as requisições JSON que alteram dados, uploads multipart, envios de formulários e operações em lote. Assinar content-type também pode fazer sentido quando o servidor interpreta os mesmos bytes de forma diferente conforme o tipo de mídia.

A IETF define Content-Digest na RFC 9530. Ele carrega um resumo do conteúdo da mensagem HTTP usando a sintaxe de Structured Fields. A assinatura cobre o cabeçalho de resumo, em vez de cobrir diretamente um corpo enorme, enquanto o destinatário calcula o resumo do corpo e o compara antes de aceitar a assinatura. Assim, a assinatura usa uma representação de tamanho fixo do conteúdo exato.

A sequência segura de envio é simples e precisa permanecer nesta ordem:

  1. Monte o objeto final da requisição, incluindo parâmetros de consulta e cabeçalhos que afetem a interpretação.
  2. Serialize o corpo uma única vez em bytes e mantenha esses bytes para a transmissão.
  3. Calcule Content-Digest sobre esses bytes.
  4. Crie Signature-Input sobre os componentes escolhidos e assine sua base de assinatura.
  5. Envie os mesmos bytes e cabeçalhos, sem alterações.

A falha comum é mais banal que um ataque criptográfico. A aplicação serializa um objeto para calcular o resumo, assina o resultado e depois um auxiliar HTTP serializa o objeto novamente. A ordem dos membros do objeto JSON, os escapes, os espaços, a formatação numérica ou um campo de horário mudam. O verificador informa corretamente que o resumo não corresponde. Os desenvolvedores então removem a proteção do corpo para conseguir fazer a release. Esse é o conserto errado.

Passe um buffer de bytes, stream ou corpo de requisição imutável ao transporte. Se o streaming impossibilitar calcular um resumo completo antes da transmissão, use um protocolo criado para esse caso e teste-o cuidadosamente. Não omita silenciosamente o resumo do corpo de uma operação de alto impacto porque o streaming era inconveniente.

Os cabeçalhos exigem uma regra mais restrita. Assine um cabeçalho quando o destinatário ou intermediário puder usá-lo para alterar o significado de segurança da requisição. content-type é um candidato. Um cabeçalho de tenant também é, se o servidor o usar para escolher uma conta. Um cabeçalho de idempotência é candidato quando retries e efeitos duplicados importam. Um cabeçalho de diagnóstico normalmente não é.

Assinar user-agent, accept, IDs de rastreamento, cabeçalhos de conexão e todos os cabeçalhos emitidos pela biblioteca cria clientes frágeis. Proxies podem adicionar, combinar ou reescrever cabeçalhos comuns. O HTTP permite transformações legítimas. Você quer que a assinatura rejeite mudanças semânticas, não que transforme uma variação inofensiva do transporte em uma indisponibilidade.

Janelas de validade devem tolerar diferenças, mas rejeitar tarefas em fila

Revogue um agente em execução
O diário de Sessões do Sallyport acompanha as execuções dos agentes e permite revogar uma sessão imediatamente.

Limites de tempo fazem as assinaturas capturadas durarem pouco. Eles também causam incidentes desnecessários quando as equipes fingem que toda estação de trabalho, container e VM tem horário perfeito. A resposta é uma regra de aceitação limitada e uma boa sincronização de relógio, não uma tolerância de duas horas.

Use created e expires em Signature-Input. Uma duração de sessenta segundos funciona bem para chamadas interativas quando o agente assina imediatamente antes do envio. Alguns minutos podem ser adequados para uma rede instável ou um fluxo que tenta novamente depois de uma falha temporária. A API deve documentar uma duração máxima e aplicá-la. Não permita que os clientes escolham uma expiração arbitrária só porque consideram a expiração inconveniente.

O verificador deve avaliar três casos separadamente:

  • Rejeitar uma requisição cujo horário created esteja muito no futuro, além de uma pequena tolerância configurada.
  • Rejeitar uma requisição cujo horário expires já tenha passado.
  • Rejeitar uma requisição cuja duração, expires - created, exceda o máximo da API, mesmo que ela ainda não tenha expirado.

Um servidor pode aceitar um relógio do cliente ligeiramente atrasado ou adiantado sem aceitar trabalho antigo. Por exemplo, um serviço pode permitir uma pequena diferença no futuro e um intervalo curto de expiração. Os valores exatos dependem de onde os clientes executam, mas o princípio não muda: tolerância de horário não é uma janela de replay deixada aberta durante toda a tarde.

Não use o cabeçalho HTTP Date como único mecanismo de validade. Ele pode ser útil como componente coberto para compatibilidade e diagnóstico, mas created e expires vivem diretamente nos parâmetros da assinatura e são menos ambíguos. Se você assinar os dois, declare quais valores o servidor usa para aplicar a regra quando houver divergência. Um verificador que aceita qualquer um deles dá uma vantagem desnecessária aos invasores.

Tarefas de agentes colocadas em fila expõem um erro de projeto oculto. Suponha que um agente crie requisições assinadas às 09:00, uma aprovação humana espere até 09:20 e um worker envie a requisição antiga depois da aprovação. O serviço deve rejeitá-la. A tarefa precisa de um novo evento de assinatura após a aprovação, porque a ação aprovada deve ter um horário atual e um destino atual significativos.

Para operações que podem ser repetidas, armazene um identificador de idempotência em um cabeçalho ou campo do corpo assinado e cubra-o com a assinatura. O cliente pode criar uma assinatura nova a cada retry, enquanto o servidor reconhece a operação lógica e evita efeitos duplicados. Reutilizar uma requisição assinada expirada não é uma estratégia de retry.

Nonces interrompem replay apenas quando o servidor se lembra deles

Uma expiração curta limita o replay, mas não impede que um invasor repita várias vezes uma requisição capturada durante essa janela. A importância disso depende do endpoint. Repetir uma leitura inofensiva tem pouco efeito. Repetir uma transferência de dinheiro, exclusão de conta ou alteração de infraestrutura pode ser grave.

Um nonce trata esse risco quando o servidor considera cada nonce de uso único para uma identidade de assinatura. O cliente gera um valor imprevisível, coloca-o nos parâmetros da assinatura ou em um cabeçalho coberto, e o servidor registra o uso bem-sucedido até a assinatura expirar. Uma segunda requisição com o mesmo assinante e nonce falha, mesmo que a assinatura ainda seja válida.

Essa é a parte que as equipes ignoram quando dizem que usam nonces. Um nonce que o servidor não guarda é apenas uma string aleatória extra. Ele não prova unicidade. Um cache ou banco compartilhado precisa de uma operação atômica de criação para que replays concorrentes não passem ambos pela verificação.

Use um armazenamento de nonces quando o dano potencial do replay justificar o custo operacional. Você precisa escolher o período de retenção, os limites de tamanho, o comportamento em caso de falha e o particionamento. Mantenha um nonce aceito até o último momento em que o servidor poderia aceitar a requisição. Identifique os registros pelo conjunto identidade do assinante e nonce, não apenas pelo nonce, porque identidades diferentes podem gerar o mesmo valor sem impacto de segurança.

Não torne um nonce obrigatório para toda chamada de baixo risco e alto volume apenas porque isso parece mais seguro. Essa escolha pode transformar uma falha no armazenamento de nonces em uma indisponibilidade para leituras inofensivas. Um perfil prático pode exigir nonce para alterações irreversíveis e depender de expiração curta e controles de idempotência em outros casos. Escreva essa regra endpoint por endpoint.

As verificações de nonce também não substituem a idempotência. O nonce diz que esta mensagem assinada deve ser aceita uma vez. Um identificador de idempotência diz que várias tentativas de retry, cada uma assinada separadamente, representam uma única operação comercial pretendida. Eles resolvem falhas diferentes.

A verificação deve falhar de forma segura antes que o código da aplicação veja a requisição

Registre cada chamada do agente
O diário de Atividade registra cada chamada junto ao registro da sessão da execução do agente.

O gateway da API ou o ponto de entrada da aplicação deve verificar a requisição antes que um handler de rota interprete parâmetros de ação, inicie um job ou consulte serviços downstream. Um handler que lê um corpo e executa trabalho antes da verificação já abriu mão da propriedade de segurança que a assinatura deveria oferecer.

Um verificador precisa de uma sequência previsível:

  1. Analise Signature-Input e Signature como Structured Fields, rejeitando sintaxe malformada e ambiguidades causadas por duplicatas.
  2. Selecione um rótulo de assinatura permitido e rejeite algoritmos desconhecidos, componentes obrigatórios ausentes ou combinações proibidas de componentes.
  3. Resolva keyid para uma identidade de assinatura ativa e obtenha seu material de verificação.
  4. Reconstrua a base da assinatura conforme a RFC 9421, usando a requisição recebida, não uma URL da aplicação reconstruída.
  5. Verifique a assinatura criptográfica, o resumo do corpo, os limites de tempo, o estado do nonce quando necessário e, depois, a autorização.

Mantenha separados nos logs os erros criptográficos e os erros de autorização. As respostas públicas podem continuar deliberadamente simples. Um 401 ou 403 com um código de erro estável é suficiente para os chamadores. Internamente, registre se o serviço rejeitou a requisição por keyid desconhecido, assinatura expirada, resumo inválido, autoridade incompatível, nonce reutilizado ou permissão insuficiente.

Não registre os bytes da assinatura como se fossem material inofensivo de depuração. Assinaturas de chave pública não são segredos como as credenciais HMAC, mas logs completos de requisições costumam conter cabeçalhos de autorização, dados pessoais e corpos. Registre um identificador da requisição, a identidade do assinante, os nomes dos componentes cobertos, um valor de resumo se a política de retenção permitir e a decisão. Limite a captura bruta a um procedimento deliberado de incidente.

Vetores de teste importam mais que prosa. A RFC 9421 inclui exemplos, mas o perfil da sua API precisa dos próprios fixtures. Mantenha requisições que devem ser verificadas, além de mutações que devem falhar: método alterado, consulta alterada, byte do corpo alterado, assinatura expirada, valor created no futuro, autoridade errada, keyid alterado e nonce repetido. Execute esses testes em todas as implementações de cliente compatíveis.

Um verificador de assinatura deve rejeitar ambiguidades, mesmo quando um parser permissivo pudesse adivinhar a intenção do remetente. Cabeçalhos duplicados, serialização inconsistente de componentes e algoritmos não suportados são erros de protocolo. Um agente não precisa que o servidor seja prestativo. Precisa que ele seja exato.

Mantenha o material de assinatura fora do contexto do agente

Dar a um agente de programação de IA uma chave privada de assinatura ou um segredo HMAC anula grande parte do propósito da assinatura. A chave pode aparecer na saída de uma ferramenta, no histórico do shell, em arquivos temporários, relatórios de erro ou em um prompt que instrua o agente a imprimir o ambiente. Mesmo um agente bem-comportado tem uma superfície indireta grande demais para uma credencial reutilizável.

Em vez disso, exponha um limite de ação restrito. O agente fornece o método, o destino permitido, os cabeçalhos e o corpo a um componente local ou remoto confiável. Esse componente valida o destino permitido, obtém aprovação quando o fluxo exigir, monta a lista de componentes cobertos, assina imediatamente antes do envio e devolve a resposta. O agente não recebe um segredo em texto simples nem um marcador falso que possa encaminhar por acidente.

Esse limite também torna concreta a revisão da autoridade. Uma identidade de assinatura pode ser limitada a um serviço, ambiente, família de rotas e classe de ação. Se o agente precisa apenas abrir uma release de staging, não dê a ele uma credencial capaz de assinar ajustes de cobrança ou chamadas de exclusão em produção. A autorização do servidor continua responsável por aplicar esses limites depois da verificação da assinatura.

O Sallyport segue essa separação para ações HTTP: o aplicativo mantém as credenciais da API em seu cofre criptografado e executa a chamada HTTP, de modo que um agente compatível com MCP recebe o resultado, não a credencial em texto simples.

Não confunda um gateway de ações com um padrão de assinatura de requisições. A RFC 9421 diz a duas partes HTTP como autenticar componentes selecionados de uma mensagem. Um gateway de ações decide onde fica o material de assinatura, quando uma pessoa vê uma aprovação e qual registro de auditoria existe em torno de uma execução do agente. Você pode usar um sem o outro, mas a combinação é útil quando ferramentas autônomas atuam sobre APIs importantes.

Se você depende de um assinante local, trate sua interface local como um limite de autorização. Vincule as requisições ao processo chamador quando possível, rejeite destinos arbitrários e garanta que um processo de agente não possa usar silenciosamente a sessão aprovada de outro processo. Um serviço local que assina qualquer URL enviada por qualquer programa local apenas moveu uma capacidade bearer para trás de um socket.

Aprovação e assinaturas respondem a perguntas diferentes

Coloque as ações atrás de um único limite
O Sallyport executa chamadas de APIs HTTP e comandos SSH enquanto as credenciais permanecem no cofre criptografado.

Uma aprovação humana registra que alguém permitiu determinada classe de ação do agente. Uma assinatura de requisição registra que uma identidade de assinatura autorizou uma mensagem HTTP definida. Nenhum dos dois registros prova o outro, a menos que o projeto faça essa vinculação explicitamente.

Para chamadas de alto impacto, faça a tela de aprovação mostrar os campos que a assinatura cobrirá: método, autoridade, caminho, parâmetros relevantes da consulta, resumo do corpo ou um resumo legível do corpo, identidade de assinatura e expiração. Se um usuário aprova POST /v1/releases/42?environment=staging, o assinante não pode depois substituir staging por produção através de um parâmetro de consulta sem assinatura.

É aqui que resumir apenas um corpo opaco pode prejudicar a revisão humana. O resumo criptográfico prova a identidade dos bytes, mas quase não diz nada a uma pessoa. Mantenha os dois artefatos: uma representação canônica da requisição para revisão e um resumo para integridade. A tela de revisão deve derivar do mesmo objeto imutável exato que o assinante enviará, não de um plano renderizado separadamente.

A aprovação de uma sessão pode ser adequada para uma execução curta de agente com muitas chamadas de baixo impacto. A aprovação por chamada é melhor para exclusões, publicação externa, alterações financeiras ou qualquer operação que um invasor possa esconder entre o tráfego rotineiro. Não peça aprovação para cada leitura apenas para alegar controle humano. As pessoas clicarão automaticamente, e você terá criado fadiga de aprovação sem uma decisão significativa.

O registro de auditoria deve capturar a identidade do assinante, os componentes cobertos, a duração da assinatura, a decisão de autorização, a referência da aprovação quando houver, o status da resposta e um identificador da requisição. Um histórico de auditoria legível ajuda o operador a responder a uma pergunta simples depois de um incidente: o que o agente enviou, sob autoridade de quem e o servidor aceitou?

As falhas que vale a pena ensaiar são principalmente falhas comuns de engenharia

Os bugs de implementação mais perigosos não estão em curvas elípticas quebradas. Eles são campos sem assinatura, canonicalização incompatível, trabalho antigo e segredos colocados onde os agentes conseguem lê-los.

Uma falha aparece quando uma equipe assina @method, @path e date, mas deixa @query de fora. A API de releases aceita ?environment=staging no uso normal. Mais tarde, uma alteração de manutenção acrescenta ?environment=production ao mesmo endpoint. Um bug de proxy ou um componente local hostil muda o parâmetro depois da assinatura. A assinatura é validada, o handler vê produção e o registro de auditoria afirma, de forma enganosa, que o agente enviou uma requisição assinada válida. A assinatura fez exatamente o que a lista de componentes pediu. A lista estava incompleta.

Outra falha aparece quando os desenvolvedores aceitam assinaturas de dez minutos para reduzir chamados relacionados a relógios. Um agente assina uma requisição de exclusão, grava todos os cabeçalhos em um log de depuração e um desenvolvedor copia o log para um issue. Qualquer pessoa com acesso a esse issue pode repetir a requisição durante a maior parte do dia de trabalho. Uma expiração curta não apaga o vazamento original, mas limita muito sua utilidade. Exigir um nonce para exclusões remove a janela de replay restante depois da primeira aceitação.

Uma terceira falha ocorre com credenciais HMAC compartilhadas. Vários agentes usam o mesmo segredo porque provisionar identidades individuais parecia trabalhoso. Quando uma auditoria encontra uma chamada destrutiva, a equipe consegue identificar a integração compartilhada, mas não a execução do agente, a aprovação do usuário nem o processo que a originou. Dê identidades de assinatura distintas a limites de autoridade distintos. A atribuição faz parte da resposta a incidentes, não é um luxo de relatórios.

Comece com um endpoint que altera dados e escreva o perfil antes de escolher uma biblioteca. Especifique sua autoridade permitida, componentes obrigatórios, regra do resumo do corpo, duração da assinatura, tolerância de horário, regra de replay, algoritmo de assinatura e mapeamento de autorização. Depois crie testes negativos que alterem cada campo coberto. Se um teste conseguir alterar um campo relevante da requisição e ainda assim passar pela verificação, não publique o perfil.

Uma requisição assinada deve ser fácil de rejeitar pelo motivo certo. Esse padrão conduz o projeto a uma autoridade restrita, mensagens de curta duração, corpos imutáveis e logs que mostram o que aconteceu depois que o agente agiu.

FAQ

Qual é a diferença entre um token bearer e uma assinatura de requisição HTTP?

Um token bearer autoriza qualquer pessoa que o possua. Uma assinatura de requisição prova que o detentor de uma chave de assinatura aprovou uma estrutura específica de requisição, como determinado método, destino, horário e resumo criptográfico do corpo. Ela reduz o replay e a substituição da requisição, mas não decide se o agente deveria ter permissão para agir.

Assinaturas de requisições HTTP tornam agentes autônomos seguros?

Não. Uma assinatura vincula a requisição a uma chave de assinatura, mas um agente que pode usar essa chave ainda consegue assinar uma requisição destrutiva dentro da autoridade que recebeu. Você continua precisando de credenciais limitadas, autorização no servidor, aprovação para ações sensíveis e um registro de auditoria.

Quais campos HTTP um agente deve assinar?

Comece com @method, @target-uri ou @authority junto com @path e @query, content-digest para requisições com corpo e um valor de validade, como date ou @created. Adicione cabeçalhos apenas quando o servidor tomar uma decisão de segurança com base neles. Não assine cabeçalhos irrelevantes só porque um SDK os envia.

Uma assinatura de requisição deve incluir o corpo da requisição HTTP?

Assine content-digest sempre que o corpo alterar a ação, o que inclui a maioria das requisições POST, PUT, PATCH e DELETE com JSON. Sem um resumo criptográfico, um intermediário ou um erro entre a assinatura e o envio pode substituir o corpo enquanto mantém a assinatura válida. Método e caminho assinados não protegem um payload que ficou sem assinatura.

Como as APIs devem lidar com diferenças de relógio em requisições assinadas?

Use um período curto de validade e permita uma tolerância pequena e explícita para erros normais de relógio. Rejeite requisições antigas, rejeite requisições muito adiantadas e mantenha os relógios corretos com sincronização de horário, em vez de ampliar indefinidamente a janela de aceitação. Se os agentes colocam tarefas em fila por longos períodos, assine imediatamente antes do envio.

Requisições de API assinadas precisam de um nonce?

Um nonce pode impedir replay dentro da janela de validade se o servidor armazenar cada nonce aceito por credencial ou identidade de assinatura. Esse armazenamento tem um custo e precisa de expiração, por isso muitas APIs usam assinaturas de curta duração para chamadas de baixo risco e acrescentam nonces apenas para transferências, exclusões ou outras ações nas quais um único replay seja inaceitável. O nonce precisa ser assinado, ou um invasor poderá substituí-lo.

A RFC 9421 é o padrão certo para assinar requisições de API?

A RFC 9421 define HTTP Message Signatures e fornece nomes padronizados de componentes, como @method, @authority, @path e @query. É uma boa escolha de formato de transmissão quando cliente e servidor controlam a integração. Ela não define seu modelo de autorização, armazenamento de replay nem distribuição de chaves de assinatura.

Um agente de IA deve guardar a chave de assinatura da API?

Em geral, não. Um segredo bruto copiado para o contexto do agente transforma cada prompt injection, vazamento de log e erro de ferramenta em um incidente de credencial. Dê ao agente uma interface de ações restrita, deixe um componente confiável separado guardar a credencial e registre a requisição exata que esse componente envia.

Por que assinaturas de requisição válidas ainda são rejeitadas?

Primeiro diferencie assinaturas malformadas de assinaturas válidas que não têm permissão, usando códigos de status diferentes e códigos internos de motivo. Mantenha os erros públicos enxutos para não permitir sondagens da lógica de verificação, mas registre no servidor a lista de componentes que falhou, o identificador do assinante, os horários e a entrada canônica. Nunca registre o segredo de assinatura.

Assinaturas de requisições HTTP podem substituir o TLS?

Não. TLS protege a conexão enquanto ela existe. Uma assinatura de mensagem viaja com a requisição e permite que o destinatário verifique campos selecionados. A maioria das chamadas de API precisa de TLS de qualquer forma, porque assinaturas não ocultam o conteúdo das requisições, o conteúdo das respostas nem os tokens bearer enviados junto delas.

Sallyport

O Sallyport executa chamadas de API e comandos SSH pelo seu agente de IA. As chaves ficam em um cofre local no seu Mac; você aprova cada execução e toda ação vai para um registro selado.

© 2026 Sallyport · Código aberto sob Apache-2.0 · Oleg Sotnikov