Assista ao vídeo da aula: Protegendo Agentes de IA com Recibos Criptográficos
(Vídeo da aula e miniatura a serem adicionados pela equipe de conteúdo da Microsoft após a mesclagem, seguindo o padrão da aula 14 / 15.)
Esta aula abordará:
Após concluir esta aula, você saberá como:
Imagine que você implantou um agente de IA para a Contoso Travel. O agente lê solicitações de 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 examina e faz a pergunta mais difícil: “Como 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 que ele precise confiar em alguém (você, seu provedor de nuvem, seu fornecedor de banco de dados). Para uso interno, essa confiança é geralmente aceitável. Para cargas reguladas (finanças, saúde, qualquer coisa sujeita à Lei de IA da UE), não é.
Recibos criptográficos resolvem isso ao tornar cada ação do agente independentemente verificável. O auditor não precisa confiar em você. 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[Canonicalizar JSON RFC 8785]
C --> D[Hash SHA-256]
D --> E[Assinar Ed25519]
E --> F[Recibo com assinatura]
F --> G[Auditor verifica offline]
G --> H{Assinatura válida?}
H -- sim --> I[Prova à prova de adulteração]
H -- não --> J[Recibo rejeitado]
Um recibo mínimo se parece com isto:
{
"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. Alterar qualquer campo invalida a assinatura.
Codificação canônica. Antes de assinar, o recibo é serializado usando o JSON Canonicalization Scheme (JCS, RFC 8785). Isso garante que duas implementações produzindo o mesmo recibo lógico produzam uma saída byte-idêntica. Sem canonicalização, 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. A adulteração fica visível no nível da cadeia mesmo se as assinaturas individuais forem contornadas.
Juntas, essas propriedades garantem três coisas:
Você não precisa de uma biblioteca especial para produzir um recibo. As primitivas criptográficas estão amplamente disponíveis e a lógica são poucas dezenas de linhas em Python.
Os exercícios práticos em code_samples/18-signed-receipts.ipynb percorrem todo o fluxo. 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 o payload 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, gerar 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)),
},
}
Esse é todo o pipeline de assinatura. Os exercícios no notebook explicam 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, invalida 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 (quebraria a assinatura do recibo 3), OUSe a chave privada estiver em um cofre de hardware e você publicar a chave pública com cada recibo, nenhum dos ataques é viável sem detecção.
O notebook mostra:
previous_receipt_hash de cada recibo corresponde ao hash real do recibo anterior.É assim que você produz uma trilha de auditoria que um auditor externo pode verificar sem confiar em você.
Esta é a seção mais importante da aula. 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 essa ação se verificada. O recibo registra o que foi alegado, não o que foi aplicado.Esse limite importa por duas razões:
Um erro comum é assumir que “temos recibos” significa “estamos governados.” Não é. Recibos são a base. Governança é o sistema que você constrói sobre ela.
O código Python nesta aula é intencionalmente mínimo para que você tenha clareza de cada linha e compreenda exatamente o que ocorre. Em produção, você tem duas opções:
Construir diretamente sobre as primitivas criptográficas. As 50 linhas que você viu acima são suficientes para muitos casos. PyNaCl (Ed25519) e o pacote jcs (JSON canônico) são bibliotecas mantidas e auditadas.
Usar uma biblioteca de recibos para 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 motores de políticas):
draft-farley-acta-signed-receipts) atualmente em processo de padronização.protect-mcp (npm) e @veritasacta/verify (npm) fornecem uma implementação Node de assinatura de recibos e verificação offline, destinados a envolver qualquer servidor MCP com uma trilha de auditoria evidente para adulteração.pip install nobulex) fornece o mesmo padrão Ed25519 + JCS para assinatura em Python com integrações LangChain e CrewAI, incluindo vetores de teste de validação cruzada publicados e um mapeamento de conformidade contribuído via OWASP PR #2210.A decisão entre criar seu próprio código ou usar uma biblioteca é similar à decisão entre escrever sua própria biblioteca JWT e usar uma testada: ambas razoáveis; a biblioteca economiza tempo e reduz a superfície de auditoria; o caminho do zero força você a entender cada primitiva. Esta aula ensina o caminho do zero para que você tenha a base para ambas as escolhas.
Teste seu entendimento antes de avançar 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 estava sobre a carga original. O que acontece durante a verificação?
3. Por que o recibo inclui tool_args_hash e result_hash em vez dos argumentos e resultados brutos?
4. O campo previous_receipt_hash vincula cada recibo ao seu predecessor. Se um atacante exclui silenciosamente um recibo no meio de uma 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 à política?
Abra code_samples/18-signed-receipts.ipynb e complete as quatro seções:
Desafio estendido 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 verificação de ida e volta. Você acabou de construir uma prova de inclusão em uma etapa: qualquer pessoa que possua 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 recibos de divulgação seletiva usam em escala (compromissos 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, aplicação de políticas ou infraestrutura de identidade. São a base para essas camadas. Ao implantar agentes em cargas de trabalho reguladas, fluxos de trabalho multi-organizações, ou qualquer cenário onde um auditor futuro não pode presumir confiança em você, 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 procedência honesto e um enganoso.
Quando estiver pronto para avançar desta lição para implantar agentes assinados com 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 office hours e tirar suas dúvidas sobre Agentes de IA.
Esta lição cobre assinatura de recibos únicos e sequências encadeadas por hash. Os mesmos primitvos se combinam em vários padrões avançados que você pode encontrar à medida que sua postura de governança amadurece:
authorization_*) e metade 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. Isso se combina additivamente ao formato de recibo ensinado nesta lição.result_hash. Cargas do mundo real geralmente são mais ricas que o resultado de uma única chamada de ferramenta: raciocínio pré-decisão (previsão do modelo, opções consideradas, evidência e sua completude, postura de risco, cadeia de responsabilidade, resultado do gate) podem conviver na carga, selados por um único recibo. Isso mantém o formato de recibo minimalista enquanto permite que esquemas de carga evoluam por domínio.signature.alg pode carregar ML-DSA-65 (padrão NIST de assinatura pós-quântica) quando precisar migrar. Planeje um período de transição com recibos assinados duplamente.Construindo Agentes de Uso de Computador (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.