Se lektionens video: Sikring af AI-agenter med kryptografiske kvitteringer
(Lektionsvideo og miniaturebillede tilføjes af Microsofts indholdsteam efter sammensmeltning, der matcher lektion 14/15-mønsteret.)
Denne lektion vil dække:
Efter at have gennemført denne lektion vil du vide, hvordan du:
Forestil dig, at du har implementeret en AI-agent til Contoso Travel. Agenten læser kundebestillinger, kalder en fly-API for at finde muligheder og booker pladser på kundens vegne. I sidste kvartal behandlede agenten 50.000 bookinger.
I dag ankommer en revisor. De stiller et enkelt spørgsmål: “Vis mig, hvad din agent gjorde.”
Du giver dine logfiler. Revisoren ser på dem og stiller det sværere spørgsmål: “Hvordan ved jeg, at disse logs ikke er redigerede?”
Dette er revisionsspor-problemet. De fleste agent-implementeringer i dag er afhængige af:
Ingen af disse kan besvare revisors spørgsmål uden at kræve, at revisor stoler på nogen (dig, din cloud-udbyder, din databaseleverandør). Til internt brug er den tillid ofte acceptabel. For regulerede arbejdsmængder (finans, sundhedsvæsen, alt underlagt EU’s AI-lovgivning) er det ikke.
Kryptografiske kvitteringer løser dette ved at gøre hver agenthandling uafhængigt verificerbar. Revisor behøver ikke at stole på dig. De behøver kun din offentlige nøgle og selve kvitteringen.
En kvittering er et JSON-objekt, der registrerer, hvad en agent gjorde, signeret med en digital signatur.
flowchart LR
A[Agent påkalder et værktøj] --> B[Byg kvitteringsindhold]
B --> C[Kanonicér JSON RFC 8785]
C --> D[SHA-256 hash]
D --> E[Ed25519 signér]
E --> F[Kvittering med signatur]
F --> G[Revisor verificerer offline]
G --> H{Signatur gyldig?}
H -- ja --> I[Manipulationssikkert bevis]
H -- nej --> J[Kvittering afvist]
En minimal kvittering ser sådan ud:
{
"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..."
}
}
Tre egenskaber gør arbejdet:
Signaturen. Kvitteringen signeres af agentens gateway ved hjælp af en Ed25519-privatnøgle. Enhver med den tilhørende offentlige nøgle kan verificere signaturen offline. Manipulation af et hvilket som helst felt ugyldiggør signaturen.
Kanonisk kodning. Før signering serialiseres kvitteringen ved hjælp af JSON Canonicalization Scheme (JCS, RFC 8785). Dette sikrer, at to implementeringer, der producerer den samme logiske kvittering, genererer byte-identisk output. Uden kanonisering ville forskellige JSON-serialisatorer producere forskellige signaturer for det samme indhold.
Hashkædning. Feltet previous_receipt_hash forbinder hver kvittering til den forrige. Fjernelse eller omordning af en kvittering bryder alle kvitteringer, der kom efter den. Manipulation bliver synlig på kædeniveau, selv hvis individuelle signaturer omgås.
Sammen giver disse egenskaber tre garantier:
Du behøver ikke et specielt bibliotek for at producere en kvittering. De kryptografiske primitiva er bredt tilgængelige, og logikken er få dusin linjer Python.
De praktiske øvelser i code_samples/18-signed-receipts.ipynb gennemgår hele flowet. Resuméversionen:
import json
import hashlib
import base64
from nacl import signing
from jcs import canonicalize # RFC 8785 kanonisk 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()}"
# Generer eller indlæs en signeringsnøgle (i produktion, opbevar i en nøgleboks)
signing_key = signing.SigningKey.generate()
verify_key = signing_key.verify_key
# Byg kvitteringsindholdet (ingen underskrift endnu)
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,
}
# Kanonisér, hash, underskriv.
canonical_bytes = canonicalize(payload)
message_hash = hashlib.sha256(canonical_bytes).digest()
signature_bytes = signing_key.sign(message_hash).signature
# Vedhæft et struktureret signeringsobjekt.
receipt = {
**payload,
"signature": {
"alg": "EdDSA",
"sig": b64url_nopad(signature_bytes),
"public_key": b64url_nopad(bytes(verify_key)),
},
}
Det er hele signaturpipeline. Øvelserne i notebooken går igennem hvert trin.
Verificering er den inverse operation:
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:
# Signaturen er et struktureret objekt: {"alg", "sig", "public_key"}.
sig_obj = receipt.get("signature")
if not sig_obj or sig_obj.get("alg") != "EdDSA":
return False
# Genskab den nyttelast, der faktisk blev signeret (alt undtagen signaturen).
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
Denne funktion tager en kvittering og returnerer True, hvis signaturen er gyldig, False ellers. Ingen netværkskald, ingen tjenesteafhængighed, ingen tillid nødvendig til nogen tredjepart.
For at se manipulation opdages i praksis, gennemgår notebooken:
tool_args_hash.Dette er den praktiske demonstration af, at kvitteringer er manipulationssynlige: enhver ændring, uanset hvor lille, bryder signaturen.
En enkelt signeret kvittering beskytter en handling. En kæde af kvitteringer beskytter en sekvens.
flowchart LR
R0[Kvittering 0<br/>Genesis] --> R1[Kvittering 1]
R1 --> R2[Kvittering 2]
R2 --> R3[Kvittering 3]
R1 -. previous_receipt_hash .-> R0
R2 -. previous_receipt_hash .-> R1
R3 -. previous_receipt_hash .-> R2
Hver kvittering registrerer hashen af den forrige kvittering. For at fjerne kvittering 2 uden at blive opdaget, skal en angriber enten:
previous_receipt_hash i kvittering 3 (bryder signaturen på kvittering 3), ELLERHvis den private nøgle er i et hardware-nøglerum, og du offentliggør den offentlige nøgle med hver kvittering, er ingen af angrebene mulige uden opdagelse.
Notebooken gennemgår:
previous_receipt_hash matcher den faktiske hash af den foregående kvittering.Sådan producerer du et revisionsspor, som en ekstern revisor kan verificere uden at stole på dig.
Dette er det vigtigste afsnit i denne lektion. Kvitteringer er kraftfulde, men deres kraft er begrænset.
Kvitteringer beviser tre ting:
Kvitteringer beviser IKKE:
policy_id, faktisk blev evalueret, eller at den ville have tilladt denne handling, hvis kontrolleret. Kvitteringen registrerer, hvad der blev hævdet, ikke hvad der blev håndhævet.Denne grænse betyder noget af to grunde:
En almindelig fejltagelse er at antage, at “vi har kvitteringer” betyder “vi er underlagt styring.” Det gør det ikke. Kvitteringer er fundamentet. Styring er systemet, du bygger ovenpå.
Python-koden i denne lektion er bevidst minimal, så du kan læse hver linje og forstå præcis, hvad der foregår. I produktion har du to muligheder:
Byg direkte på de kryptografiske primitiva. De 50 linjer, du så ovenfor, er tilstrækkelige til mange anvendelser. PyNaCl (Ed25519) og pakken jcs (kanonisk JSON) er velvedligeholdte og auditerede biblioteker.
Brug et produktionsbibliotek til kvitteringer. Flere open source-projekter implementerer samme mønster med ekstra funktioner (nøgleudskiftning, batchverifikation, JWK Set-distribution, integration med politikmotorer):
draft-farley-acta-signed-receipts), som er under standardiseringsprocessen.protect-mcp (npm) og @veritasacta/verify (npm) tilbyder en Node-baseret implementering af signering af kvitteringer og offline verifikation, tiltænkt til at indpakke enhver MCP-server med et manipulationssynligt revisionsspor.Valget mellem at lave sin egen løsning eller bruge et bibliotek svarer til valget mellem at skrive sit eget JWT-bibliotek eller bruge et testet et: begge er rimelige; biblioteket sparer tid og mindsker audit-overfladen; scratch-tilgangen tvinger dig til at forstå hver primitiv. Denne lektion lærer scratch-vejen, så du har fundamentet for begge valg.
Test din forståelse før du går videre til praksisøvelsen.
1. En kvittering er signeret med agentens private Ed25519-nøgle. Revisor har kun den offentlige nøgle. Kan revisor verificere kvitteringen offline?
2. En angriber ændrer policy_id-feltet i en kvittering for at hævde, at den var styret af en mere lempelig politik. Signaturen var over den oprindelige pakke. Hvad sker der under verifikationen?
3. Hvorfor indeholder kvitteringen en tool_args_hash og result_hash i stedet for de rå argumenter og resultat?
4. Feltet previous_receipt_hash forbinder hver kvittering med sin forgænger. Hvis en angriber tavst sletter en kvittering midt i en kæde, hvad bliver så ugyldigt?
5. En kvittering verificeres rent. Beviser det, at agentens handling var korrekt, holdbar eller overholdt politikken?
Åbn code_samples/18-signed-receipts.ipynb og gennemfør alle fire sektioner:
Udvidelsesudfordring 1: udvid kvitteringsskemaet med et ekstra felt efter eget valg (for eksempel et anmodnings-ID til sporing), opdater den kanoniske signeringslogik til at inkludere det, og bekræft at kvitteringen stadig kan gennemgå verifikation. Ændr derefter feltet efter signering og bekræft, at verifikationen fejler. Dette tvinger dig til at forstå, hvordan hver byte af den kanoniske kodning bidrager til signaturen. Stretch-udfordring 2: SHA-256-hash to af dine kvitteringer sammen (sammenkæd deres kanoniske bytes i en deterministisk rækkefølge) og indlejre den resulterende digest som et nyt felt på en tredje kvittering, før du underskriver den. Bekræft, at alle tre kvitteringer stadig kan rundsendes. Du har netop opbygget et ét-trins inklusionsbevis: hvem som helst, der har den tredje kvittering, kan bevise, at de første to eksisterede på det tidspunkt, den blev underskrevet, uden at skulle afsløre deres indhold. Dette er det mønster, som selective-disclosure-kvitteringer bruger i stor skala (Merkle-committeringer, RFC 6962).
Kryptografiske kvitteringer giver AI-agenter en revisionssti, der er:
De er ikke en erstatning for inputvalidering, politikhåndhævelse eller identitetsinfrastruktur. De er fundamentet for disse lag. Når du implementerer agenter i regulerede arbejdsbelastninger, multi-organisation arbejdsflows eller ethvert miljø, hvor en fremtidig revisor ikke kan antages at have tillid til dig, er kvitteringer måden, hvorpå du gør revisionssporet ærligt.
Det vigtigste at tage med sig: kvitteringer beviser, hvem der sagde hvad, hvornår. De beviser ikke, at det, der blev sagt, var sandt eller korrekt. Hold denne sondring klart. Det er forskellen mellem et ærligt provenienssystem og et misvisende.
Når du er klar til at gå videre fra denne lektion til at udrulle kvitteringsunderskrevne agenter i et reelt miljø:
https://your-org.example.com/.well-known/agent-keys.json.Deltag i Microsoft Foundry Discord for at mødes med andre lærende, deltage i kontortider og få svar på dine AI Agent-spørgsmål.
Denne lektion dækker enkelt-kvitteringssignatur og hash-kædede sekvenser. De samme primitiva sammensættes til flere mere avancerede mønstre, du kan støde på, efterhånden som din governance-positur modnes:
authorization_*) og post-udførelse (result_*) halvdele med uafhængige signaturer, nyttigt når autorisationsbeslutningen og det observerede resultat produceres af forskellige aktører eller på forskellige tidspunkter. Dette kombineres additivt oven på kvitteringsformatet, der læres i denne lektion.result_hash. Virkelige nyttelaster er ofte rigere end et enkelt værktøjskaldsresultat: forud-beslutningsbegrundelse (modelprædiktion, overvejede muligheder, beviser og deres fuldstændighed, risikopostur, ansvarskæde, gate-udfald) kan alle ligge indeni nyttelasten, forsejlet af en enkelt kvittering. Dette holder kvitteringsformatet minimalt, mens nyttelastskemaer kan udvikle sig domæne-for-domæne.signature.alg kan bære ML-DSA-65 (NIST post-kvante signaturstandard), når du skal migrere. Planlæg en overgangsperiode, hvor kvitteringer er dobbeltsignerede.Building Computer Use Agents (CUA)
(Bestemmes af faggruppeansvarlige)
Ansvarsfraskrivelse: Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten Co-op Translator. Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.