Veja o vídeo da lição: Assegurar Agentes de IA com Recibos Criptográficos
(O vídeo da lição e a miniatura serão adicionados pela equipa de conteúdos da Microsoft após a junção, seguindo o padrão da lição 14 / 15.)
Esta lição irá cobrir:
Depois de completar esta lição, saberá como:
Imagine que implementou um agente de IA para a Contoso Travel. O agente lê pedidos dos clientes, chama uma API de voos para consultar opções e reserva lugares em nome do cliente. No último trimestre, o agente processou 50.000 reservas.
Hoje chega um auditor. Ele faz uma pergunta simples: “Mostre-me o que o seu agente fez.”
Entrega os seus ficheiros de registo. O auditor olha para eles e faz a pergunta mais difícil: “Como sei que estes registos não foram editados?”
Este é o problema da trajetória de auditoria. A maioria das implementações de agentes hoje em dia depende de:
Nenhum destes pode responder à pergunta do auditor sem que este tenha que confiar em alguém (você, o seu fornecedor de cloud, o fornecedor da base de dados). Para uso interno, essa confiança é muitas vezes aceitável. Para cargas de trabalho reguladas (finanças, saúde, qualquer coisa sujeita ao Regime de IA da UE), não é.
Os recibos criptográficos resolvem isto tornando cada ação do agente verificável independentemente. O auditor não precisa de confiar em si. Só precisa da sua chave pública e do próprio recibo.
Um recibo é um objeto JSON que regista o que um agente fez, assinado com uma assinatura digital.
flowchart LR
A[Agente invoca uma ferramenta] --> B[Construir carga útil de recibo]
B --> C[Canonizar JSON RFC 8785]
C --> D[Hash SHA-256]
D --> E[Assinar Ed25519]
E --> F[Recibo com assinatura]
F --> G[Auditora verifica offline]
G --> H{Assinatura válida?}
H -- yes --> I[Prova à prova de manipulação]
H -- no --> J[Recibo rejeitado]
Um recibo minimalista parece assim:
{
"type": "agent.tool_call.v1",
"agent_id": "contoso-travel-bot",
"tool_name": "lookup_flights",
"tool_args_hash": "sha256:a3f9c1...",
"result_hash": "sha256:7b2e1d...",
"policy_id": "contoso-travel-policy-v3",
"timestamp": "2026-04-25T14:30:00Z",
"sequence": 47,
"previous_receipt_hash": "sha256:9d4e6a...",
"signature": {
"alg": "EdDSA",
"sig": "c5af83...",
"public_key": "8f3b2c..."
}
}
Três propriedades fazem o trabalho:
A assinatura. O recibo é assinado pelo gateway do agente, usando uma chave privada Ed25519. Quem tiver a chave pública correspondente pode verificar a assinatura offline. Qualquer adulteração de um campo invalida a assinatura.
Codificação canónica. Antes de assinar, o recibo é serializado usando o JSON Canonicalization Scheme (JCS, RFC 8785). Isto garante que duas implementações que produzem o mesmo recibo lógico geram saída byte-idêntica. Sem canoicalização, diferentes serializadores JSON produziriam assinaturas diferentes para o mesmo conteúdo.
Encadeamento por hash. O campo previous_receipt_hash liga cada recibo ao anterior. Remover ou reordenar um recibo quebra todos os recibos posteriores. A adulteração torna-se visível a nível da cadeia mesmo que as assinaturas individuais sejam ultrapassadas.
Juntas, estas propriedades fornecem três garantias:
Não precisa de uma biblioteca especial para produzir um recibo. As primitivas criptográficas estão amplamente disponíveis e a lógica é de poucas dezenas de linhas de Python.
Os exercícios práticos em code_samples/18-signed-receipts.ipynb percorrem o fluxo completo. A versão resumida:
import json
import hashlib
import base64
from nacl import signing
from jcs import canonicalize # RFC 8785 JSON canónico
def b64url_nopad(data: bytes) -> str:
return base64.urlsafe_b64encode(data).decode("ascii").rstrip("=")
def sha256_canonical(obj) -> str:
"""SHA-256 of a Python object's JCS-canonical JSON form."""
return f"sha256:{hashlib.sha256(canonicalize(obj)).hexdigest()}"
# Gerar ou carregar uma chave de assinatura (em produção, armazenar num cofre de chaves)
signing_key = signing.SigningKey.generate()
verify_key = signing_key.verify_key
# Construir a carga útil do recibo (ainda sem assinatura)
tool_args = {"origin": "SYD", "destination": "LAX"}
tool_result = [{"flight": "QF11", "price": 1850, "stops": 0}]
payload = {
"type": "agent.tool_call.v1",
"agent_id": "contoso-travel-bot",
"tool_name": "lookup_flights",
"tool_args_hash": sha256_canonical(tool_args),
"result_hash": sha256_canonical(tool_result),
"policy_id": "contoso-travel-policy-v3",
"timestamp": "2026-04-25T14:30:00Z",
"sequence": 0,
"previous_receipt_hash": None,
}
# Canonicalizar, hash, assinar.
canonical_bytes = canonicalize(payload)
message_hash = hashlib.sha256(canonical_bytes).digest()
signature_bytes = signing_key.sign(message_hash).signature
# Anexar um objeto de assinatura estruturado.
receipt = {
**payload,
"signature": {
"alg": "EdDSA",
"sig": b64url_nopad(signature_bytes),
"public_key": b64url_nopad(bytes(verify_key)),
},
}
Este é todo o pipeline de assinatura. Os exercícios no notebook explicam cada passo.
A verificação é a operação inversa:
import base64
import hashlib
from nacl import signing
from nacl.exceptions import BadSignatureError
from jcs import canonicalize
def b64url_decode(s: str) -> bytes:
padding = "=" * ((4 - len(s) % 4) % 4)
return base64.urlsafe_b64decode(s + padding)
def verify_receipt(receipt: dict) -> bool:
# A assinatura é um objeto estruturado: {"alg", "sig", "public_key"}.
sig_obj = receipt.get("signature")
if not sig_obj or sig_obj.get("alg") != "EdDSA":
return False
# Reconstrua o payload que foi realmente assinado (tudo exceto a assinatura).
payload = {k: v for k, v in receipt.items() if k != "signature"}
canonical_bytes = canonicalize(payload)
message_hash = hashlib.sha256(canonical_bytes).digest()
try:
verify_key = signing.VerifyKey(b64url_decode(sig_obj["public_key"]))
verify_key.verify(message_hash, b64url_decode(sig_obj["sig"]))
return True
except BadSignatureError:
return False
Esta função recebe um recibo e retorna True se a assinatura for válida, False caso contrário. Sem chamadas de rede, sem dependência de serviços, sem necessidade de confiar em terceiros.
Para ver a detecção de adulteração em ação, o notebook percorre:
tool_args_hash.Esta é a demonstração prática de que os recibos são à prova de adulteração: qualquer modificação, por mínima que seja, quebra a assinatura.
Um único recibo assinado protege uma ação. Uma cadeia de recibos protege uma sequência.
flowchart LR
R0[Recibo 0<br/>génese] --> R1[Recibo 1]
R1 --> R2[Recibo 2]
R2 --> R3[Recibo 3]
R1 -. previous_receipt_hash .-> R0
R2 -. previous_receipt_hash .-> R1
R3 -. previous_receipt_hash .-> R2
Cada recibo regista o hash do recibo anterior. Para remover silenciosamente o recibo 2, um atacante teria que:
previous_receipt_hash do recibo 3 (quebra a assinatura do recibo 3), OUSe a chave privada estiver num hardware key vault e publicar a chave pública com cada recibo, nenhum destes ataques é viável sem deteção.
O notebook percorre:
previous_receipt_hash de cada recibo corresponde ao hash real do recibo anterior.É assim que produz uma trajetória de auditoria que um auditor externo pode verificar sem confiar em si.
Esta é a secção mais importante desta lição. Os recibos são poderosos mas o seu poder tem limites.
Os recibos provam três coisas:
Os recibos NÃO provam:
policy_id foi realmente avaliada, ou que teria permitido esta ação se verificada. O recibo regista o que foi alegado, não o que foi aplicado.Este limite é importante por duas razões:
Um erro comum é assumir que “temos recibos” significa “temos governação.” Não significa. Os recibos são uma base. A governação é o sistema que constrói sobre ela.
O código Python nesta lição é intencionalmente minimalista para que possa ler cada linha e entender exatamente o que está a acontecer. Em produção, tem duas opções:
Construir diretamente sobre as primitivas criptográficas. As 50 linhas que viu acima são suficientes para muitos casos. PyNaCl (Ed25519) e o pacote jcs (JSON canónico) são bibliotecas bem mantidas e auditadas.
Usar uma biblioteca de recibos para produção. Vários projetos open-source implementam o mesmo padrão com funcionalidades adicionais (rotação de chaves, verificação em lote, distribuição de conjuntos JWK, integração com motores de políticas):
draft-farley-acta-signed-receipts) atualmente em processo de normalização.protect-mcp (npm) e @veritasacta/verify (npm) fornecem uma implementação baseada em Node de assinatura de recibos e verificação offline, destinados a envolver qualquer servidor MCP com uma trajetória de auditoria à prova de adulteração.pip install nobulex) fornece o mesmo padrão de assinatura Ed25519 + JCS em Python com integrações LangChain e CrewAI, incluindo vetores de teste cruzados publicados e um mapeamento de conformidade contribuído via OWASP PR #2210.A decisão entre construir o seu próprio e usar uma biblioteca espelha a decisão entre escrever a sua própria biblioteca JWT e usar uma testada: ambos são razoáveis; a biblioteca poupa tempo e reduz a superfície de auditoria; a abordagem do zero obriga a entender cada primitiva. Esta lição ensina o caminho do zero para que tenha a base para qualquer escolha.
Teste a sua compreensão antes de avançar para o exercício prático.
1. Um recibo é assinado com a chave privada Ed25519 do agente. O auditor só tem a chave pública. Pode o auditor verificar o recibo offline?
2. Um atacante modifica o campo policy_id de um recibo para alegar que foi governado por uma política mais permissiva. A assinatura era sobre a carga útil original. O que acontece durante a verificação?
3. Porque é que o recibo inclui um tool_args_hash e result_hash em vez dos argumentos e resultado brutos?
4. O campo previous_receipt_hash liga cada recibo ao seu predecessor. Se um atacante eliminar silenciosamente um recibo no meio da cadeia, o que se torna inválido?
5. Um recibo verifica-se corretamente. Isso prova que a ação do agente foi correta, válida ou conforme a política?
Abra code_samples/18-signed-receipts.ipynb e complete as quatro secções:
Desafio estendido 2: calcule o hash SHA-256 de dois dos seus recibos juntos (concatene os seus bytes canónicos numa ordem determinística) e incorpore o resumo resultante como um novo campo num terceiro recibo antes de o assinar. Verifique que os três recibos ainda passam pela verificação de ida e volta. Acaba de construir uma prova de inclusão de um passo: qualquer pessoa que possua o terceiro recibo pode provar que os dois primeiros existiam no momento em que foi assinado, sem precisar revelar os seus conteúdos. Este é o padrão que os recibos de divulgação seletiva usam à escala (compromissos Merkle, RFC 6962).
Os recibos criptográficos fornecem aos agentes de IA um registo de auditoria que é:
Não são um substituto para a validação de entradas, aplicação de políticas ou infraestruturas de identidade. São a base para essas camadas. Quando estiver a implantar agentes em cargas de trabalho regulamentadas, fluxos de trabalho multi-organização ou qualquer ambiente onde um auditor futuro não possa assumir confiança em si, os recibos são como torna honesto o registo de auditoria.
A lição mais importante: os recibos provam quem disse o quê e quando. Não provam que o que foi dito é verdadeiro ou correto. Mantenha essa distinção bem clara. É a diferença entre um sistema de proveniência honesto e um enganador.
Quando estiver pronto para passar desta lição para implantar agentes assinados com recibos num ambiente real:
https://your-org.example.com/.well-known/agent-keys.json.Junte-se ao Microsoft Foundry Discord para reunir-se com outros aprendizes, participar em horas de atendimento e obter respostas para as suas dúvidas sobre Agentes de IA.
Esta lição cobre a assinatura única de recibos e sequências encadeadas por hash. As mesmas primitivas compõem vários padrões mais avançados que pode encontrar à medida que a sua postura de governação amadurece:
authorization_*) e pós-execução (result_*) com assinaturas independentes, útil quando a decisão de autorização e o resultado observado são produzidos por atores diferentes ou em momentos diferentes. Isto acrescenta-se ao formato de recibo ensinado nesta lição.result_hash. Cargas úteis do mundo real são muitas vezes mais ricas do que o resultado de uma única chamada a ferramenta: raciocínio pré-decisão (previsão do modelo, opções consideradas, evidência e a sua completude, postura de risco, cadeia de responsabilidade, resultado do gate) podem residir na carga útil, selados por um único recibo. Isto mantém o formato de recibo minimalista ao permitir que esquemas de carga útil evoluam domain-by-domain.signature.alg pode conter ML-DSA-65 (o padrão de assinatura pós-quântica NIST) quando precisar migrar. Planear um período de transição onde recibos sejam assinados duplamente.Construir Agentes de Uso de Computador (CUA)
(A ser determinada pelos responsáveis pelo currículo)
Aviso Legal: Este documento foi traduzido utilizando o serviço de tradução automática Co-op Translator. Embora nos esforcemos pela precisão, esteja ciente de que traduções automáticas podem conter erros ou imprecisões. O documento original na sua língua nativa deve ser considerado a fonte autorizada. Para informações críticas, recomenda-se tradução profissional humana. Não nos responsabilizamos por quaisquer mal-entendidos ou interpretações incorretas resultantes da utilização desta tradução.