ai-agents-for-beginners

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.)

Segurança de Agentes de IA com Recibos Criptográficos

Introdução

Esta lição irá abordar:

Objetivos de Aprendizagem

Depois de completar esta lição, saberá como:

O Problema: O Registo de Auditoria do Seu Agente

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.

O que é um Recibo Criptográfico?

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:

  1. 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.

  2. 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.

  3. 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:

Produzir um Recibo em Python

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.

Verificar um Recibo e Detetar Manipulação

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:

  1. Produzir um recibo válido e confirmar que verifica.
  2. Modificar um byte do campo tool_args_hash.
  3. Reexecutar a verificação e ver falhar.

Esta é a demonstração prática de que os recibos evidenciam manipulação: qualquer modificação, por menor que seja, quebra a assinatura.

Encadear Recibos para Agentes com Múltiplos Passos

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:

Se 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:

  1. Construir uma cadeia de três recibos.
  2. Verificar que cada campo previous_receipt_hash do recibo corresponde ao hash real do recibo anterior.
  3. Manipular um recibo no meio e ver a cadeia quebrar exatamente nesse ponto.

É assim que produz um registo de auditoria que um auditor externo pode verificar sem confiar em si.

O que os Recibos Provam (e o que Não Provam)

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:

  1. Atribuição: uma chave específica assinou uma carga útil específica.
  2. Integridade: a carga útil não mudou desde a assinatura.
  3. Ordenação: este recibo veio depois daquele na cadeia de hash.

Os recibos NÃO provam:

  1. Correcção: que a ação do agente foi a ação correta. Um recibo pode ser assinado para uma resposta errada tão facilmente quanto para uma resposta certa.
  2. Conformidade com a política: que a política referenciada em 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.
  3. Identidade além da chave: o recibo diz “esta chave assinou este conteúdo.” Não diz “este humano autorizou isto.” A ligação de uma chave a uma pessoa ou organização requer infraestrutura de identidade separada (um diretório, um registo de chaves públicas, etc.).
  4. Verdade dos inputs: se o agente recebe um prompt manipulado e age em função dele, o recibo regista a ação fielmente. Os recibos estão a jusante da validação de inputs, não são um substituto para ela.

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.

Provar que um Humano Aprovou a Ação Exata

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.

Referências de Produção

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:

  1. 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.

  2. 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):

    • A pipeline de assinatura usa as convenções JCS e de escopo de assinatura num Rascunho IETF independente (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.
    • O Microsoft Agent Governance Toolkit compõe recibos com decisões de políticas baseadas em Cedar; veja o Tutorial 33 nesse repositório para um exemplo completo.
    • Os pacotes 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.
    • O SDK Python nobulex (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.

Verificação de Conhecimento

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?

Resposta Sim. A verificação Ed25519 requer apenas a chave pública e os bytes assinados. Sem chamada de rede, sem dependência de serviço. Esta é a propriedade que torna os recibos úteis em ambientes isolados, multi-organizacionais, ou de baixa confiança.

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?

Resposta A verificação falha. A assinatura foi calculada sobre os bytes canónicos da carga útil original; modificar qualquer campo altera esses bytes, o que torna a assinatura inválida. O atacante precisaria da chave privada para produzir uma nova assinatura válida, que não possui.

3. Por que é que o recibo inclui um tool_args_hash e um result_hash em vez dos argumentos e resultados brutos?

Resposta Duas razões. Primeiro, o recibo pode precisar ser arquivado ou transmitido em ambientes onde a divulgação do conteúdo bruto (dados pessoais identificáveis, dados comerciais) representa um problema. A aplicação de hash mantém o recibo pequeno e o conteúdo privado; o auditor verifica que o hash corresponde a uma cópia armazenada separadamente do conteúdo real. Segundo, os hashes têm um tamanho fixo; um recibo com hashes tem um tamanho limitado independentemente do tamanho dos inputs e outputs.

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?

Resposta Todos os recibos que vieram após o eliminado. Os seus campos `previous_receipt_hash` já não correspondem à cadeia real (porque o recibo referenciado deixou de existir, ou a cadeia agora aponta para um predecessor diferente). Para ocultar a eliminação, o atacante teria de voltar a assinar todos os recibos posteriores, o que requer a chave privada.

5. Um recibo verifica-se corretamente. Isso prova que a ação do agente foi correta, sólida ou conforme a política?

Resposta Não. Um recibo válido prova três coisas: atribuição (esta chave assinou este conteúdo), integridade (o conteúdo não mudou) e ordenação (este recibo veio depois daquele recibo). NÃO prova que a ação foi correta, que a política nomeada em `policy_id` foi realmente avaliada, ou que o agente seguiu todas as regras. Os recibos tornam o comportamento do agente auditável, não necessariamente correto. Esta é a fronteira mais importante desta lição.

Exercício de Prática

Abra code_samples/18-signed-receipts.ipynb e complete todas as quatro secções:

  1. Seção 1: Assine o seu primeiro recibo e verifique-o.
  2. Seção 2: Manipule o recibo e observe a falha de verificação.
  3. Seção 3: Construa uma cadeia de três recibos e verifique a integridade da cadeia.
  4. Seção 4: Aplique o padrão a um agente construído com o Microsoft Agent Framework: envolva uma chamada de ferramenta na assinatura do recibo, depois verifique o recibo de forma independente.

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).

Conclusão

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.

Lista de Verificação para Produção

Quando estiver pronto para avançar desta lição para implementar agentes com recibos assinados num ambiente real:

Tem Mais Perguntas sobre Segurança de Agentes AI?

Junte-se ao Microsoft Foundry Discord para conhecer outros aprendizes, participar em horas de expediente e obter respostas às suas perguntas sobre Agentes AI.

Para Além Deste Conteúdo

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:

Recursos Adicionais

Lição Anterior

Criar Agentes AI Locais


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.