Bekijk de lesvideo: AI-agenten beveiligen met cryptografische kwitanties
(Lesvideo en thumbnail worden na samenvoeging toegevoegd door het Microsoft-contentteam, passend bij het patroon van les 14 / 15.)
Deze les behandelt:
Na het voltooien van deze les weet je hoe je:
Stel je voor dat je een AI-agent hebt ingezet voor Contoso Travel. De agent leest klantverzoeken, roept een vluchten-API aan om opties op te zoeken en boekt stoelen namens de klant. In het afgelopen kwartaal verwerkte de agent 50.000 boekingen.
Vandaag komt een auditor binnen. Hij stelt een eenvoudige vraag: “Laat me zien wat je agent heeft gedaan.”
Je overhandigt je logbestanden. De auditor bekijkt ze en stelt een moeilijkere vraag: “Hoe weet ik dat deze logs niet zijn aangepast?”
Dit is het audit-trailprobleem. De meeste agent-implementaties gebruiken tegenwoordig:
Geen van deze kan de vraag van de auditor beantwoorden zonder dat de auditor iemand moet vertrouwen (jou, je cloudprovider, je databaseleverancier). Voor intern gebruik is dat vaak acceptabel. Voor gereguleerde workloads (financiën, gezondheidszorg, alles onder de EU AI-wet) is dat niet zo.
Cryptografische kwitanties lossen dit op door elke agentactie onafhankelijk verifieerbaar te maken. De auditor hoeft jou niet te vertrouwen. Alleen jouw publieke sleutel en de kwitantie zelf zijn nodig.
Een kwitantie is een JSON-object dat vastlegt wat een agent deed, ondertekend met een digitale handtekening.
flowchart LR
A[Agent roept een tool aan] --> B[Ontvangstpayload opbouwen]
B --> C[JSON canonicaliseren RFC 8785]
C --> D[SHA-256 hash]
D --> E[Ed25519 tekenen]
E --> F[Ontvangst met handtekening]
F --> G[Auditor verifieert offline]
G --> H{Handtekening geldig?}
H -- yes --> I[Bewijs van manipulatiebestendigheid]
H -- no --> J[Ontvangst geweigerd]
Een minimale kwitantie ziet er als volgt uit:
{
"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..."
}
}
Drie eigenschappen doen het werk:
De handtekening. De kwitantie is ondertekend door de gateway van de agent met een Ed25519-private sleutel. Iedereen met de corresponderende publieke sleutel kan de handtekening offline verifiëren. Manipulatie van een veld maakt de handtekening ongeldig.
Canonieke codering. Voor het ondertekenen wordt de kwitantie geserialiseerd volgens het JSON Canonicalization Scheme (JCS, RFC 8785). Dit zorgt ervoor dat twee implementaties die dezelfde logische kwitantie produceren, exact identieke bytes genereren. Zonder canonieke codering zouden verschillende JSON-serializers verschillende handtekeningen genereren voor dezelfde inhoud.
Hash-keten. Het veld previous_receipt_hash koppelt elke kwitantie aan de voorgaande. Het verwijderen of herschikken van een kwitantie breekt elke daaropvolgende kwitantie. Manipulatie wordt zichtbaar op ketenniveau, ook als individuele handtekeningen worden omzeild.
Samen bieden deze eigenschappen drie garanties:
Je hebt geen speciale bibliotheek nodig om een kwitantie te produceren. De cryptografische primitieven zijn breed beschikbaar en de logica is een paar tientallen regels Python.
De praktische oefeningen in code_samples/18-signed-receipts.ipynb lopen de volledige flow door. De samenvatting:
import json
import hashlib
import base64
from nacl import signing
from jcs import canonicalize # RFC 8785 canonieke JSON
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()}"
# Genereer of laad een ondertekeningssleutel (in productie, sla op in een sleutelkluis)
signing_key = signing.SigningKey.generate()
verify_key = signing_key.verify_key
# Bouw de ontvangstgegevens (nog geen handtekening)
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,
}
# Canonicaliseer, hash, onderteken.
canonical_bytes = canonicalize(payload)
message_hash = hashlib.sha256(canonical_bytes).digest()
signature_bytes = signing_key.sign(message_hash).signature
# Voeg een gestructureerd handtekeningobject toe.
receipt = {
**payload,
"signature": {
"alg": "EdDSA",
"sig": b64url_nopad(signature_bytes),
"public_key": b64url_nopad(bytes(verify_key)),
},
}
Dit is de volledige ondertekeningspijplijn. De oefeningen in het notebook lopen elke stap door.
Verificatie is de omgekeerde bewerking:
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:
# De handtekening is een gestructureerd object: {"alg", "sig", "public_key"}.
sig_obj = receipt.get("signature")
if not sig_obj or sig_obj.get("alg") != "EdDSA":
return False
# Reconstrueer de payload die daadwerkelijk is ondertekend (alles behalve de handtekening).
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
Deze functie neemt een kwitantie en geeft True terug als de handtekening geldig is, anders False. Geen netwerkverzoek, geen dienstafhankelijkheid, geen vertrouwen in derden nodig.
Om manipulatie-detectie te zien, laat het notebook zien:
tool_args_hash.Dit is de praktische demonstratie dat kwitanties tamper-evident zijn: elke wijziging, hoe klein ook, breekt de handtekening.
Een enkele ondertekende kwitantie beschermt één actie. Een keten van kwitanties beschermt een reeks acties.
flowchart LR
R0[Ontvangst 0<br/>genesis] --> R1[Ontvangst 1]
R1 --> R2[Ontvangst 2]
R2 --> R3[Ontvangst 3]
R1 -. previous_receipt_hash .-> R0
R2 -. previous_receipt_hash .-> R1
R3 -. previous_receipt_hash .-> R2
Elke kwitantie legt de hash vast van de voorgaande kwitantie. Om kwitantie 2 stilletjes te verwijderen, zou een aanvaller moeten:
previous_receipt_hash van kwitantie 3 aanpassen (breekt de handtekening van kwitantie 3), OFAls de private sleutel in een hardware key vault zit en je publiceert de publieke sleutel bij elke kwitantie, is geen van deze aanvallen mogelijk zonder detectie.
Het notebook laat zien:
previous_receipt_hash van elke kwitantie overeenkomt met de daadwerkelijke hash van de vorige kwitantie.Zo produceer je een audit-trail die een externe auditor kan verifiëren zonder jou te vertrouwen.
Dit is het belangrijkste deel van deze les. Kwitanties zijn krachtig, maar hun kracht is begrensd.
Kwitanties bewijzen drie dingen:
Kwitanties bewijzen NIET:
policy_id daadwerkelijk werd geëvalueerd, of dat het deze actie zou hebben toegestaan als dat gecontroleerd was. De kwitantie registreert wat beweerd is, niet wat afgedwongen is.Deze grenzen zijn belangrijk om twee redenen:
Een veelgemaakte fout is aannemen dat “we hebben kwitanties” betekent “we worden bestuurd.” Dat is niet zo. Kwitanties zijn een fundament. Bestuur is het systeem dat je erop bouwt.
De Python-code in deze les is bewust minimaal zodat je elke regel kunt lezen en precies begrijpt wat er gebeurt. In productie heb je twee opties:
Direct bouwen op de cryptografische primitieve. De 50 regels die je hierboven zag zijn voldoende voor veel use cases. PyNaCl (Ed25519) en het jcs-pakket (canonieke JSON) zijn goed onderhouden en gecontroleerde bibliotheken.
Gebruik een productiebibliotheek voor kwitanties. Verschillende open-source projecten implementeren hetzelfde patroon met extra functies (sleutelrotatie, batch-verificatie, JWK Set-distributie, integratie met policies):
draft-farley-acta-signed-receipts) die momenteel in het standaardiseringsproces zit.protect-mcp (npm) en @veritasacta/verify (npm) bieden een Node-gebaseerde implementatie van kwitantieondertekening en offline verificatie, bedoeld om elke MCP-server te omhullen met een tamper-evidente audit-trail.pip install nobulex) biedt hetzelfde Ed25519 + JCS ondertekeningspatroon in Python met LangChain- en CrewAI-integraties, inclusief gepubliceerde cross-validatie testvectoren en een compliance mapping via OWASP PR #2210.De keuze tussen zelf bouwen en een bibliotheek gebruiken is vergelijkbaar met de keuze tussen zelf je JWT-bibliotheek schrijven of een geteste gebruiken: beide zijn redelijk; de bibliotheek bespaart tijd en vermindert het auditoppervlak; de from-scratch aanpak dwingt je elke primitive te begrijpen. Deze les leert de from-scratch aanpak zodat je de basis hebt voor beide keuzes.
Test je begrip voordat je doorgaat naar de praktijkopdracht.
1. Een kwitantie wordt ondertekend met de private Ed25519-sleutel van de agent. De auditor heeft alleen de publieke sleutel. Kan de auditor de kwitantie offline verifiëren?
2. Een aanvaller wijzigt het veld policy_id van een kwitantie om te beweren dat het door een meer permissief beleid werd beheerst. De handtekening was over de originele payload. Wat gebeurt er tijdens verificatie?
3. Waarom bevat de kwitantie een tool_args_hash en result_hash in plaats van de ruwe argumenten en het resultaat?
4. Het veld previous_receipt_hash koppelt elke kwitantie aan zijn voorganger. Als een aanvaller stilletjes een kwitantie uit het midden van een keten verwijdert, wat wordt dan ongeldig?
5. Een kwitantie wordt schoon geverifieerd. Bewijst dat dat de actie van de agent correct, deugdelijk of voldoet aan het beleid was?
Open code_samples/18-signed-receipts.ipynb en voltooi alle vier de secties:
Stretch-uitdaging 2: SHA-256-hash twee van je ontvangstbewijzen samen (concateneer hun canonieke bytes in een deterministische volgorde) en voeg de resulterende digest toe als een nieuw veld aan een derde ontvangstbewijs voordat je dit ondertekent. Verifieer dat alle drie de ontvangstbewijzen nog steeds correct worden geverifieerd. Je hebt net een eenstaps inclusie-bewijs gebouwd: iedereen die het derde ontvangstbewijs bezit kan bewijzen dat de eerste twee bestonden op het moment dat het werd ondertekend, zonder hun inhoud te hoeven onthullen. Dit is het patroon dat selective-disclosure ontvangstbewijzen op schaal gebruiken (Merkle commitments, RFC 6962).
Cryptografische ontvangstbewijzen geven AI-agenten een audit trail die:
Ze zijn geen vervanging voor invoervalidatie, beleidsuitvoering of identiteitinfrastructuur. Ze vormen een fundament voor die lagen. Wanneer je agenten inzet in gereguleerde workloads, multi-organisatie workflows, of elke omgeving waar je niet mag veronderstellen dat een toekomstige auditor jou vertrouwt, zijn ontvangstbewijzen hoe je de audit trail eerlijk maakt.
De belangrijkste les: ontvangstbewijzen bewijzen wie wat zei en wanneer. Ze bewijzen niet dat wat gezegd werd waar of correct was. Houd dat onderscheid strak. Het is het verschil tussen een eerlijk provenance-systeem en een misleidend systeem.
Wanneer je klaar bent om van deze les over te gaan naar het inzetten van ontvangstbewijs-ondertekende agenten in een echte omgeving:
https://your-org.example.com/.well-known/agent-keys.json.Word lid van de Microsoft Foundry Discord om andere deelnemers te ontmoeten, naar spreekuren te gaan en je AI Agents-vragen beantwoord te krijgen.
Deze les behandelt enkelvoudige ontvangstbewijzen ondertekenen en hash-gekoppelde reeksen. Dezelfde primitieve functies vormen samen diverse geavanceerdere patronen die je kunt tegenkomen naarmate je governance-houding volwassen wordt:
authorization_*) en een post-uitvoeringshelft (result_*) met onafhankelijke handtekeningen, handig wanneer de autorisatiebeslissing en het geobserveerde resultaat door verschillende actoren of op verschillende tijden worden geproduceerd. Dit kan aanvullend bovenop het ontvangstbewijsformaat in deze les worden toegepast.result_hash plaatst. Payloads in de praktijk zijn vaak rijker dan een enkel tool-oproepresultaat: redenatie voorafgaand aan de beslissing (modelvoorspelling, overwogen opties, bewijs en volledigheid, risicohouding, verantwoordingsketen, poortresultaat) kunnen allemaal in de payload leven, verzegeld door één ontvangstbewijs. Dit houdt het formaat minimaal terwijl payload-schema’s per domein kunnen evolueren.signature.alg veld kan ML-DSA-65 bevatten (de NIST post-quantum handtekeningstandaard) als je moet migreren. Plan een overgangsperiode waarin ontvangstbewijzen dubbel worden ondertekend.Computer Use Agents (CUA) bouwen
(Wordt bepaald door curriculumbeheerders)
Disclaimer: Dit document is vertaald met behulp van de AI vertaaldienst Co-op Translator. Hoewel we streven naar nauwkeurigheid, dient u er rekening mee te houden dat geautomatiseerde vertalingen fouten of onnauwkeurigheden kunnen bevatten. Het originele document in de oorspronkelijke taal moet worden beschouwd als de gezaghebbende bron. Voor kritieke informatie wordt professionele menselijke vertaling aanbevolen. Wij zijn niet aansprakelijk voor eventuele misverstanden of verkeerde interpretaties die voortvloeien uit het gebruik van deze vertaling.