Assista ao vídeo da lição: Protegendo Agentes de IA com Recibos Criptográficos
(Vídeo da lição e miniatura serão adicionados pela equipe de conteúdo da Microsoft após a mesclagem, seguindo o padrão da lição 14 / 15.)
Esta lição irá cobrir:
Após concluir esta lição, você saberá como:
Imagine que você implantou um agente de IA para a Contoso Travel. O agente lê solicitações dos clientes, chama uma API de voos para buscar opções e reserva assentos em nome do cliente. No último trimestre, o agente processou 50.000 reservas.
Hoje um auditor chega. Ele faz uma pergunta simples: “Mostre-me o que seu agente fez.”
Você entrega seus arquivos de log. O auditor os analisa e faz a pergunta mais difícil: “Como eu sei que esses logs não foram editados?”
Este é o problema da trilha de auditoria. A maioria das implantações de agentes hoje depende de:
Nenhum deles pode responder à pergunta do auditor sem exigir que o auditor confie em alguém (você, seu provedor de nuvem, seu fornecedor de banco de dados). Para uso interno, essa confiança costuma ser aceitável. Para cargas reguladas (finanças, saúde, qualquer coisa sujeita à Lei de IA da UE), não é.
Recibos criptográficos resolvem isso tornando cada ação do agente verificável de maneira independente. O auditor não precisa confiar em você. Ele precisa apenas da sua chave pública e do próprio recibo.
Um recibo é um objeto JSON que registra o que um agente fez, assinado com uma assinatura digital.
flowchart LR
A[Agente invoca uma ferramenta] --> B[Construir carga útil do recibo]
B --> C[Canonizar JSON RFC 8785]
C --> D[Hash SHA-256]
D --> E[Assinatura Ed25519]
E --> F[Recibo com assinatura]
F --> G[Auditor verifica offline]
G --> H{Assinatura válida?}
H -- yes --> I[Prova à prova de adulteração]
H -- no --> J[Recibo rejeitado]
Um recibo mínimo é 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. Qualquer um com a chave pública correspondente pode verificar a assinatura offline. Qualquer alteração em algum campo invalida a assinatura.
Codificação canônica. Antes da assinatura, o recibo é serializado usando o JSON Canonicalization Scheme (JCS, RFC 8785). Isso garante que duas implementações produzindo o mesmo recibo lógico gerem saída byte-idêntica. Sem canonicidade, diferentes serializadores JSON produziriam assinaturas diferentes para o mesmo conteúdo.
Encadeamento por hash. O campo previous_receipt_hash vincula cada recibo ao anterior. Remover ou reordenar um recibo quebra todos os recibos que vieram depois. Adulterações tornam-se visíveis no nível da cadeia mesmo se assinaturas individuais forem burladas.
Juntas, essas propriedades garantem três coisas:
Você não precisa de uma biblioteca especial para produzir um recibo. Os primitivos criptográficos são amplamente disponíveis e a lógica são algumas dezenas de linhas de Python.
Os exercícios práticos em code_samples/18-signed-receipts.ipynb mostram o fluxo completo. A versão resumida:
import json
import hashlib
import base64
from nacl import signing
from jcs import canonicalize # JSON canônico RFC 8785
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 em um 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 percorrem cada etapa.
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 a carga útil que foi realmente assinada (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 chamada de rede, sem dependência de serviço, sem necessidade de confiar em terceiros.
Para ver a detecção de adulteração em ação, o notebook mostra:
tool_args_hash.Esta é a demonstração prática de que recibos evidenciam adulteração: qualquer modificação, por menor 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 registra o hash do recibo anterior. Para remover silenciosamente o recibo 2, um atacante precisaria:
previous_receipt_hash do recibo 3 (quebrando a assinatura do recibo 3), OUSe a chave privada estiver em um cofre de chaves de hardware e você publicar a chave pública com cada recibo, nenhum desses ataques é possível sem detecção.
O notebook mostra:
previous_receipt_hash de cada recibo corresponde ao hash real do recibo anterior.Assim você produz uma trilha de auditoria que um auditor externo pode verificar sem precisar confiar em você.
Esta é a seção mais importante da lição. Recibos são poderosos, mas seu poder é limitado.
Recibos provam três coisas:
Recibos NÃO provam:
policy_id foi realmente avaliada, ou que teria permitido esta ação se checada. O recibo registra o que foi afirmado, não o que foi aplicado.Este limite é importante por duas razões:
Um erro comum é supor que “temos recibos” significa “estamos governados.” Não significa. Recibos são a base. Governança é o sistema que você constrói sobre eles.
O código Python desta lição é propositalmente minimalista para você poder ler cada linha e entender exatamente o que está acontecendo. Em produção, você tem duas opções:
Construir diretamente sobre os primitivos criptográficos. As 50 linhas que você viu acima são suficientes para muitos casos de uso. PyNaCl (Ed25519) e o pacote jcs (JSON canônico) são bibliotecas bem mantidas e auditadas.
Usar uma biblioteca de recibos de produção. Vários projetos open-source implementam o mesmo padrão com recursos adicionais (rotação de chaves, verificação em lote, distribuição de JWK Set, integração com mecanismos de política):
draft-farley-acta-signed-receipts) atualmente em processo de padronização.protect-mcp (npm) e @veritasacta/verify (npm) oferecem implementação Node para assinatura e verificação offline de recibos, destinados a envolver qualquer servidor MCP com uma trilha de auditoria evidente para adulteração.A decisão entre criar seu próprio código e usar uma biblioteca é como decidir entre escrever sua própria biblioteca JWT ou usar uma testada: ambos são razoáveis; a biblioteca economiza tempo e reduz superfície de auditoria; o caminho do zero força você a entender cada primitivo. Esta lição ensina o caminho do zero para lhe dar a base para qualquer escolha.
Teste seu entendimento antes de passar para o exercício prático.
1. Um recibo é assinado com a chave privada Ed25519 do agente. O auditor tem apenas a chave pública. O auditor pode verificar o recibo offline?
2. Um atacante modifica o campo policy_id de um recibo para alegar que ele foi regido por uma política mais permissiva. A assinatura foi sobre a carga original. O que acontece na verificação?
3. Por que o recibo inclui um tool_args_hash e um result_hash em vez dos argumentos e resultados crus?
4. O campo previous_receipt_hash vincula cada recibo ao seu predecessor. Se um atacante excluir silenciosamente um recibo do meio da cadeia, o que se torna inválido?
5. Um recibo verifica limpo. Isso prova que a ação do agente foi correta, sólida ou conforme a política?
Abra code_samples/18-signed-receipts.ipynb e complete todas as quatro seções:
Desafio extra 1: estenda o esquema do recibo com um campo adicional de sua escolha (por exemplo, um ID de solicitação para rastreamento), atualize a lógica canônica de assinatura para incluí-lo e confirme que o recibo ainda passa pela verificação sem erros. Depois modifique o campo após a assinatura e confirme que a verificação falha. Isso força você a entender como cada byte da codificação canônica contribui para a assinatura. Desafio avançado 2: Faça o hash SHA-256 de dois dos seus recibos juntos (concatene seus bytes canônicos em uma ordem determinística) e incorpore o resumo resultante como um novo campo em um terceiro recibo antes de assiná-lo. Verifique se os três recibos ainda passam pela ida e volta. Você acabou de construir uma prova de inclusão em um único passo: qualquer pessoa que possuir o terceiro recibo pode provar que os dois primeiros existiam no momento em que ele foi assinado, sem precisar revelar seus conteúdos. Este é o padrão que os recibos de divulgação seletiva usam em escala (compromissos de Merkle, RFC 6962).
Recibos criptográficos fornecem aos agentes de IA uma trilha de auditoria que é:
Eles não substituem a validação de entrada, a aplicação de políticas ou a infraestrutura de identidade. São uma base para essas camadas. Quando você está implantando agentes em cargas de trabalho regulamentadas, fluxos de trabalho multi-organizacionais, ou qualquer ambiente onde um auditor futuro não possa ser assumido como confiável, os recibos são como você torna a trilha de auditoria honesta.
A lição mais importante: os recibos provam quem disse o quê, quando. Eles não provam que o que foi dito é verdadeiro ou correto. Mantenha essa distinção clara. É a diferença entre um sistema de proveniência honesta e um enganoso.
Quando estiver pronto para avançar desta lição e implantar agentes com assinaturas de recibos em um ambiente real:
https://your-org.example.com/.well-known/agent-keys.json.Junte-se ao Microsoft Foundry Discord para encontrar outros aprendizes, participar de horários de atendimento e tirar suas dúvidas sobre Agentes de IA.
Esta lição cobre assinatura de recibos simples e sequências encadeadas por hash. Os mesmos primitivos se combinam em vários padrões mais avançados que você pode encontrar conforme sua postura de governança 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 produções de atores diferentes ou em tempos diferentes. Isto se soma ao formato de recibo ensinado nesta lição.result_hash. Cargas úteis do mundo real geralmente são mais complexas do que o resultado de uma única chamada de ferramenta: raciocínio pré-decisão (predição do modelo, opções consideradas, evidências e sua completude, postura de risco, cadeia de responsabilidade, resultado de bloqueios) podem viver dentro da carga útil, selados por um único recibo. Isso mantém o formato do recibo minimalista enquanto permite que esquemas de carga útil evoluam domínio a domínio.signature.alg pode carregar ML-DSA-65 (o padrão de assinatura pós-quântica do NIST) quando você precisar migrar. Planeje um período de transição em que recibos sejam assinados duplamente.Construindo Agentes de Uso Computacional (CUA)
(A ser determinada pelos mantenedores do currículo)
Aviso Legal: Este documento foi traduzido usando o serviço de tradução por IA Co-op Translator. Embora nos esforcemos pela precisão, por favor, esteja ciente de que traduções automatizadas podem conter erros ou imprecisões. O documento original em seu idioma nativo 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 decorrentes do uso desta tradução.