Veja o vídeo da lição: Segurança de Agentes de IA com Recibos Criptográficos
(Vídeo da lição e miniatura a serem adicionados pela equipa de conteúdos da Microsoft após a fusão, seguindo o padrão da lição 14/15.)
Esta lição irá abordar:
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 procurar 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 registos. O auditor olha para eles e faz a pergunta mais difícil: “Como posso saber que estes registos não foram editados?”
Este é o problema do registo de auditoria. A maioria das implementações de agentes hoje em dia baseia-se em:
Nenhum destes pode responder à pergunta do auditor sem exigir que este confie em alguém (em si, no seu fornecedor de cloud, no fornecedor da sua 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 Regulamento Europeu de IA), não é.
Os recibos criptográficos resolvem isto tornando cada ação do agente independentemente verificável. O auditor não precisa de confiar em si. Precisa apenas 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[O agente invoca uma ferramenta] --> B[Construir carga útil do recibo]
B --> C[Canonicalizar JSON RFC 8785]
C --> E[Assinar bytes canónicos com 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 parece-se 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 estão a fazer o trabalho:
A assinatura. O recibo é assinado pela gateway do agente usando uma chave privada Ed25519. Qualquer pessoa com a chave pública correspondente pode verificar a assinatura offline. Manipular qualquer campo invalida a assinatura.
Codificação canónica. Antes de assinar, o recibo é serializado usando o Esquema de Canonicalização JSON (JCS, RFC 8785). Isto assegura que duas implementações que produzem o mesmo recibo lógico produzem saída byte-idêntica. Sem a canonizaçã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 cada recibo que o segue. A manipulação torna-se visível ao nível da cadeia, mesmo que as assinaturas individuais sejam burladas.
Em conjunto, estas propriedades oferecem três garantias:
Não precisa de uma biblioteca especial para produzir um recibo. As primitivas criptográficas 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 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 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 e assinar diretamente os bytes JCS. PureEdDSA faz hashing internamente.
canonical_bytes = canonicalize(payload)
signature_bytes = signing_key.sign(canonical_bytes).signature
# Anexar um objeto de assinatura estruturado.
receipt = {
**payload,
"signature": {
"alg": "EdDSA",
"sig": b64url_nopad(signature_bytes),
"public_key": b64url_nopad(bytes(verify_key)),
},
}
Essa é toda a pipeline de assinatura. Os exercícios no caderno percorrem 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 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)
try:
verify_key = signing.VerifyKey(b64url_decode(sig_obj["public_key"]))
verify_key.verify(canonical_bytes, 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 confiança necessária em terceiros.
Para ver a deteção de manipulação em ação, o caderno percorre:
tool_args_hash.Esta é a demonstração prática de que os recibos evidenciam manipulaçã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 regista o hash do recibo anterior. Para remover silenciosamente o recibo 2, um atacante teria de:
previous_receipt_hash do recibo 3 (quebra a assinatura do recibo 3), OUSe a chave privada estiver num cofre de chaves de hardware e publicar a chave pública com cada recibo, nenhum ataque é viável sem deteção.
O caderno percorre:
previous_receipt_hash do recibo corresponde ao hash real do recibo anterior.É assim que produz um registo 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 de facto avaliada, ou que teria permitido esta ação se verificada. O recibo regista o que foi declarado, não o que foi cumprido.Este limite é importante por duas razões:
Um erro comum é assumir que “temos recibos” significa “estamos governados.” Isso não é verdade. Os recibos são uma base. A governação é o sistema que constrói em cima.
O ponto 3 acima merece a sua própria secção: um recibo de ação diz “esta chave assinou este conteúdo,” nunca “um humano autorizou isto.” Para ações de alto risco (reembolsos, eliminações, transferências bancárias), os frameworks de governação exigem cada vez mais exatamente essa declaração em falta, e ela é produzida com as mesmas primitivas que já construiu nesta lição.
O caderno seguinte code_samples/human-authorization-receipts.ipynb adiciona um segundo tipo de recibo, human.approval.v1, na mesma forma de envelope que os recibos da lição (uma carga útil tipada assinada por Ed25519 sobre os seus bytes canónicos JCS, com o objeto signature fora dos bytes assinados). Um aprovador nomeado assina a ação canónica completa e o seu digest antes da execução; o recibo de ação do agente carrega o mesmo digest da ação e um parent_approval_ref, o receipt_hash da aprovação, na mesma convenção do previous_receipt_hash na cadeia que construiu acima. Um verify_chain percorre ambos os artefactos sob registos de chaves fixos separados (chaves aprovadoras vs chaves do agente), pelo que o caminho de código é partilhado mas as autoridades nunca o são.
A propriedade que isto oferece, declarada cuidadosamente: o humano aprovou esta ação exata, e o agente executou exatamente essa ação aprovada. Os testes de recusa do caderno é que tornam a propriedade real e não meramente afirmada:
Cada falha recusa por uma razão distinta, para que um auditor lendo uma recusa possa dizer se a autoridade ficou desatualizada ou a ação executada mudou. A regra que o caderno ensina: uma aprovação assinada não é autoridade por si só. A autoridade só existe se ambos os recibos ainda ligam à mesma ação canónica na altura da execução. O recibo de aprovação humana é uma composição educativa definida por esta lição, não um tipo de recibo definido por draft-farley-acta-signed-receipts.
O código Python desta lição é intencionalmente minimalista para que possa ler cada linha e compreender 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 de uso. 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 por lotes, distribuição JWK Set, integração com motores de políticas):
draft-farley-acta-signed-receipts, revisão 02). O recibo educativo plano desta lição difere do envelope {payload, signature} do rascunho e não é apresentado como uma implementação conforme. O rascunho publica um conjunto de conformidade partilhado (agent-governance-testvectors) para implementações que visam o seu formato de fio.protect-mcp (npm) e @veritasacta/verify (npm) providenciam uma implementação baseada em Node para assinatura de recibos e verificação offline, destinado a envolver qualquer servidor MCP com um registo de auditoria à prova de manipulação, incluindo um fluxo de retenção para coassinatura em que uma ação pausada emite um recibo de aprovação ligado ao digest da ação (WebAuthn suportado no fluxo desktop), o mesmo padrão de recibo de aprovação do caderno de autorização humana acima.pip install nobulex) providencia o mesmo padrão de assinatura Ed25519 + JCS 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 algo 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-o 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 passar ao exercício prático.
1. Um recibo é assinado com a chave privada Ed25519 do agente. O auditor tem apenas 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 foi feita sobre a carga útil original. O que acontece durante a verificação?
3. Por que é que o recibo inclui um tool_args_hash e um result_hash em vez dos argumentos e resultados brutos?
4. O campo previous_receipt_hash liga cada recibo ao seu predecessor. Se um atacante eliminar silenciosamente um recibo do meio de uma cadeia, o que se torna inválido?
5. Um recibo verifica-se corretamente. 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 secções:
Desafio extra 1: estenda o esquema do recibo com um campo adicional à sua escolha (por exemplo, um ID de pedido para rastreamento), atualize a lógica canónica de assinatura para o incluir, e confirme que o recibo ainda passa pela verificação. Depois modifique o campo após a assinatura e confirme que a verificação falha. Isto obriga a entender como cada byte da codificação canónica contribui para a assinatura.
Desafio extra 2: faça 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. Acabou de construir uma prova de inclusão de um só passo: qualquer pessoa que possua o terceiro recibo pode provar que os dois primeiros existiam no momento da assinatura, sem precisar de revelar o seu conteúdo. Este é o padrão que os recibos com divulgação seletiva usam à escala (compromissos Merkle, RFC 6962).
Recibos criptográficos dão aos agentes AI uma trilha de auditoria que é:
Não são um substituto para validação de entrada, aplicação de políticas ou infraestruturas de identidade. São a base para essas camadas. Quando estiver a implementar agentes em cargas de trabalho reguladas, fluxos de trabalho multi-organização, ou qualquer cenário onde um auditor futuro não possa ser assumido como confiável, os recibos são o modo de tornar a trilha de auditoria honesta.
O ponto mais importante: os recibos provam quem disse o quê, quando. Não provam que o que foi dito é verdade ou certo. Mantenha essa distinção clara. É a diferença entre um sistema de proveniência honesto e um enganador.
Quando estiver pronto para avançar desta lição para implementar agentes com recibos assinados num ambiente real:
https://your-org.example.com/.well-known/agent-keys.json.Junte-se ao Microsoft Foundry Discord para conhecer outros aprendizes, participar em horas de expediente e obter respostas às suas perguntas sobre Agentes AI.
Esta lição cobre a assinatura de recibos únicos e sequências com hash encadeado. As mesmas primitivas compõem vários padrões mais avançados que poderá encontrar à medida que a sua postura de governação amadureça:
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 completa o formato de recibo ensinado nesta lição.result_hash. Cargas úteis do mundo real são frequentemente mais ricas que o resultado de uma única chamada de ferramenta: raciocínio pré-decisão (previsão do modelo, opções consideradas, evidências e sua completude, postura de risco, cadeia de responsabilidade, resultado de gate) podem residir dentro da carga útil, seladas por um único recibo. Isto mantém o formato do recibo mínimo permitindo que esquemas de carga evoluam domínio a domínio.signature.alg pode transportar ML-DSA-65 (o padrão NIST de assinatura pós-quântica) quando precisar migrar. Planeie um período de transição onde os recibos sejam assinados duplamente.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.