Pogledajte video lekciju: Zaštita AI agenata kriptografskim potvrdom
(Video lekcija i sličica bit će dodani od strane Microsoftovog tima za sadržaj nakon spajanja, u skladu s obrascem lekcije 14 / 15.)
Ova će lekcija obuhvatiti:
Nakon završetka ove lekcije, znat ćete kako:
Zamislite da ste rasporedili AI agenta za Contoso Travel. Agent čita zahtjeve korisnika, poziva API za letove da bi pronašao opcije i rezervira sjedala u ime korisnika. Prošlog kvartala agent je obradio 50,000 rezervacija.
Danas dolazi revizor. Postavlja jednostavno pitanje: “Pokažite mi što je vaš agent radio.”
Predajete mu datoteke zapisnika. Revizor ih promatra i postavlja teže pitanje: “Kako znam da ti zapisnici nisu uređivani?”
Ovo je problem zapisnika aktivnosti. Većina rasporeda agenata danas se oslanja na:
Nijedan od ovih ne može odgovoriti na revizorovo pitanje bez zahtjeva da revizor nekome vjeruje (vama, vašem cloud davatelju, prodavaču baze podataka). Za internu upotrebu, ta je povjerenja često prihvatljiva. Za regulirane slučajeve korištenja (financije, zdravstvo, sve pod EU AI zakonom), nije.
Kriptografske potvrde suzbijaju ovaj problem tako što pružaju potpisanu izjavu koja se može neovisno verificirati. Revizor i dalje treba izvanbantni razlog za povjerenje u potpisni ključ i komponentu koja je proizvela izjavu. Ugrađeni ključ dokazuje samo dosljednost, ne identitet izdavatelja ili mjesto provođenja.
Potvrda je JSON objekt koji bilježi što je agent učinio, potpisan digitalnim potpisom.
flowchart LR
A[Agent invokes a tool] --> B[Build receipt payload]
B --> C[Canonicalize JSON RFC 8785]
C --> E[Ed25519 sign canonical bytes]
E --> F[Receipt with signature]
F --> G[Auditor verifies offline]
G --> H{Signature valid?}
H -- yes --> I[Signed statement is intact]
H -- no --> J[Receipt rejected]
Minimalna potvrda izgleda ovako:
{
"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..."
}
}
Tri svojstva obavljaju rad:
Potpis. Potvrdu potpisuje pristupna točka agenta koristeći privatni Ed25519 ključ. Svatko s odgovarajućim javnim ključem može offline provjeriti potpis. Dokazna strana mora zaključati taj ključ ili ga dohvatiti iz pouzdanog registra izvan potvrde. Manipulacija bilo kojim potpisanim poljem poništava potpis.
Kanonsko kodiranje. Prije potpisivanja, potvrda se serijalizira koristeći JSON Canonicalization Scheme (JCS, RFC 8785). To osigurava da dvije implementacije koje proizvode istu logičku potvrdu proizvode identičan bajtni ishod. Bez kanonizacije, različiti JSON serijalizatori bi stvarali različite potpise za isti sadržaj.
Heš lanac. Polje previous_receipt_hash povezuje svaku potvrdu s prethodnom. Uklanjanje ili preraspored potvrde prekida predstavljenu sekvencu. Otkrivanje skraćivanja ili prepisivanja kontroliranog od potpisnika također zahtijeva zaključani trenutni vrh lanca, neovisno svjedočenu kontrolnu točku ili autentificirani zaključak zatvorene populacije.
Zajedno ova svojstva pružaju tri jamstva:
Ne trebate posebnu knjižnicu za proizvodnju potvrde. Kriptografski primitivci su široko dostupni, a logika je tek nekoliko desetaka linija Pythona.
Praktični zadatci u code_samples/18-signed-receipts.ipynb vode kroz cjelokupan tijek. Izvorno kontrolirani code_samples/18-signed-receipts.execution-receipt.json bilježi točan hash bilježnice, Python okruženje, broj ćelija i provjere iz najnovjeg čistog izvođenja bez mijenjanja spremljenih rezultata lekcije. Sažeti prikaz:
import json
import hashlib
import base64
from nacl import signing
from jcs import canonicalize # RFC 8785 canonical 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()}"
# Generate or load a signing key (in production, store in a key vault)
signing_key = signing.SigningKey.generate()
verify_key = signing_key.verify_key
TRUSTED_PUBLIC_KEYS = {b64url_nopad(bytes(verify_key))} # pinned out of band by the verifier
# Build the receipt payload (no signature yet)
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,
}
# Canonicalize and sign the JCS bytes directly. PureEdDSA hashes internally.
canonical_bytes = canonicalize(payload)
signature_bytes = signing_key.sign(canonical_bytes).signature
# Attach a structured signature object.
receipt = {
**payload,
"signature": {
"alg": "EdDSA",
"sig": b64url_nopad(signature_bytes),
"public_key": b64url_nopad(bytes(verify_key)),
},
}
To je cjelokupni proces potpisivanja. Zadaci u bilježnici vode kroz svaki korak.
Verifikacija je suprotna operacija:
import base64
import hashlib
from nacl import signing
from nacl.exceptions import CryptoError
from jcs import canonicalize
def b64url_decode(s: str) -> bytes:
padding = "=" * ((4 - len(s) % 4) % 4)
return base64.b64decode(s + padding, altchars=b"-_", validate=True)
def verify_receipt(receipt: dict, trusted_public_keys: set[str]) -> bool:
# The signature is a structured object: {"alg", "sig", "public_key"}.
if not isinstance(receipt, dict):
return False
sig_obj = receipt.get("signature")
if not isinstance(sig_obj, dict) or sig_obj.get("alg") != "EdDSA":
return False
try:
# An inline key proves only self-consistency. Trust is configured out of band.
public_key = sig_obj.get("public_key")
signature = sig_obj.get("sig")
if not isinstance(public_key, str) or not isinstance(signature, str):
return False
if public_key not in trusted_public_keys:
return False
payload = {k: v for k, v in receipt.items() if k != "signature"}
canonical_bytes = canonicalize(payload)
verify_key = signing.VerifyKey(b64url_decode(public_key))
verify_key.verify(canonical_bytes, b64url_decode(signature))
return True
except (CryptoError, TypeError, ValueError, KeyError, OverflowError):
return False
Ova funkcija prima potvrdu plus zaključani skup ključeva korisnika i vraća
True samo ako je potpis valjan pod prihvaćenim ključem. Verifikacija ne zahtijeva
mrežni poziv, ali povjerenje u ključ ostaje vanjsku konfiguracijsku odluku.
Za vidjeti otkrivanje manipulacije u praksi, bilježnica vodi kroz:
tool_args_hash.Ovo je praktični dokaz da su potvrde otporne na manipulacije: bilo kakva izmjena, ma koliko mala, prekida potpis.
Jedna potpisana potvrda štiti jednu akciju. Lanac potvrda štiti niz.
flowchart LR
R0[Receipt 0<br/>genesis] --> R1[Receipt 1]
R1 --> R2[Receipt 2]
R2 --> R3[Receipt 3]
R1 -. previous_receipt_hash .-> R0
R2 -. previous_receipt_hash .-> R1
R3 -. previous_receipt_hash .-> R2
Svaka potvrda bilježi heš potvrde prije nje. Da bi neovlašteni uklonio potvrdu 2 tiho, morao bi ili:
previous_receipt_hash potvrde 3 (prekida potpis potvrde 3), ILIAko je privatni ključ zaštićen i provjeritelj zaključava neovisno svjedočeni vrh lanca, ove su napade moguće otkriti. Objavljivanje ključa unutar svake potvrde nije dovoljno: potpisnik ili napadač može prikazati drugi ključ, prepisati sufiks ili skratiti lanac osim ako korisnik nema vanjski anker povjerenja i potpunosti.
Bilježnica vodi kroz:
previous_receipt_hash svake potvrde odgovara stvarnom hash-u prethodne potvrde.Ovo je način na koji se proizvodi trag revizije koji vanjski revizor može verificirati pod svojim vlastitim učvršćenim ključevima izdavatelja i kontrolnim točkama lanca, bez povjerenja u vaš promjenjivi zapisnik.
Ovo je najvažniji dio ove lekcije. Potvrde su moćne, ali njihova moć je ograničena.
Potvrde dokazuju tri stvari:
Potvrde NE dokazuju:
policy_id zapravo evaluirana, ili da bi dozvolila ovu akciju ako bi bila provjerena. Potvrda bilježi što je tvrdnjeno, ne što je primijenjeno.Ova granica je važna iz dva razloga:
Česta pogreška je pretpostaviti da “imamo potvrde” znači “mi smo u upravljanju.” Nije tako. Potvrde su temelj. Upravljanje je sustav koji gradite iznad njih.
Stavka 3 iznad zaslužuje vlastiti odjeljak: potvrda akcije kaže “ovaj ključ je potpisao ovaj sadržaj,” nikad “čovjek je to autorizirao.” Za akcije visokog rizika (povrati, brisanja, prijenosi novca), zaseban potpis odobritelja može povezati točnu akciju. Povezivanje tog ključa s čovjekom i provjera ljudske prisutnosti ostaju zasebne odgovornosti.
Sljedeća bilježnica code_samples/human-authorization-receipts.ipynb dodaje drugu vrstu potvrde, human.approval.v1, u istom obliku omotnice kao i potvrde lekcije (tipizirani sadržaj potpisan Ed25519 nad njegovim kanonskim JCS bajtovima, s objektom signature izvan potpisanih bajtova). Ključ odobritelja potpisuje potpunu kanonsku akciju i njezin digest; ključ agenta potpisuje izjavu koja nosi isti digest akcije i parent_approval_ref, receipt_hash odobrenja, istu konvenciju kao u previous_receipt_hash u lancu koji ste gore izgradili. Jedan verify_chain obrađuje oba artefakta pod zasebnim registrima ključnih sidara (ključevi odobritelja nasuprot ključevima agenata), tako da je put koda zajednički, ali autoriteti nikada nisu.
Svojstvo koje to donosi, pažljivo izrečeno: učvršćeni ključ odobritelja potpisao je ovu točnu akciju, a ključ agenta je kasnije potpisao izjavu koja nosi isti digest akcije. To je korisni dokaz, ali nije sam po sebi dokaz da je akcija izvršena. Za kontrolu izvršenja, točno odobrenje mora biti također rezervirano i iskorišteno jednom na stvarnoj granici izvršitelja; nepoznati ishod pružatelja mora ostati nepoznat umjesto da se slijepo ponavlja. Fiksacije odbijanja u bilježnici testiraju sloj lanca potvrda:
Svaki neuspjeh odbija s različitim razlogom, tako da revizor koji čita odbijanje može utvrditi je li ovlast zastarjela ili se radnja promijenila. Provjera je samo za čitanje i ponovljiva: ne troši odobrenje niti poziva pružatelja usluge. Poseban izvršitelj zahtijeva trajnu, atomsku zaštitu ponovnog izvršenja i eksplicitne ishode uspjeha, neuspjeha i neodređenosti. Potvrda ljudskog odobrenja je edukativna kompozicija definirana ovom lekcijom, a ne vrsta potvrde definirana draft-farley-acta-signed-receipts.
Python kod u ovoj lekciji je namjerno minimalan da biste mogli pročitati svaki redak i točno razumjeti što se događa. U produkciji imate dvije opcije:
Izgradite izravno na kriptografskim primitivima. 50 linija koje ste vidjeli gore dovoljno je za mnoge slučajeve uporabe. PyNaCl (Ed25519) i jcs paket (kanonski JSON) su dobro održavane i revidirane biblioteke.
Koristite proizvodnu biblioteku za potvrde. Nekoliko open-source projekata implementira isti uzorak s dodatnim značajkama (rotacija ključeva, grupna verifikacija, distribucija JWK seta, integracija s policy engine-ima):
draft-farley-acta-signed-receipts, revizija 02). Ova lekcija s ravnom obrazovnom potvrdom razlikuje se od {payload, signature} omotača iz nacrta i nije predstavljena kao sukladna implementacija. Nacrt objavljuje zajednički skup sukladnosti (agent-governance-testvectors) za implementacije usmjerene na njegov žični format.protect-mcp (npm) i @veritasacta/verify (npm) pružaju Node-baziranu implementaciju potpisivanja potvrda i offline verifikacije, namijenjenu za omotavanje bilo kojeg MCP poslužitelja s auditnim tragom koji je otporan na manipulacije, uključujući i tijek zadržane su-koautorizacije u kojem pauzirana akcija emitira potvrdu odobrenja vezanu uz sažetak akcije (WebAuthn podržan u desktop tijeku), isti uzorak potvrde odobrenja kao u gornjem bilježniku za ljudsku autorizaciju.pip install nobulex) pruža isti Ed25519 + JCS obrazac potpisivanja u Pythonu s integracijama LangChain i CrewAI, uključujući objavljene vektore za međuprovalnu provjeru i mapiranje usklađenosti doprineseno putem OWASP PR #2210.Odluka između izrade vlastitog i korištenja biblioteke odražava odluku između pisanja vlastite JWT biblioteke i korištenja testirane: oba su razumljiva; biblioteka štedi vrijeme i smanjuje površinu revizije; pristup od nule tjera vas da razumijete svaku primitivu. Ova lekcija podučava put od nule kako biste imali temelj za bilo koji odabir.
Testirajte svoje razumijevanje prije prelaska na praktičnu vježbu.
1. Potvrda je potpisana privatnim Ed25519 ključem agenta. Revizor ima samo javni ključ. Može li revizor potvrdu verificirati offline?
2. Napadač mijenja polje policy_id potvrde da tvrdi da je bila pod upravom permisivnije politike. Potpis je bio nad izvornim teretom. Što se događa tijekom verifikacije?
3. Zašto potvrda sadrži tool_args_hash i result_hash, a ne sirove argumente i rezultat?
4. Polje previous_receipt_hash povezuje svaku potvrdu s prethodnikom. Ako napadač tiho izbriše jednu potvrdu iz sredine lanca, što postaje nevaljano?
5. Potvrda se uspješno verificira. Dokazuje li to da je agentova akcija bila ispravna, valjana ili usklađena s politikom?
Otvorite code_samples/18-signed-receipts.ipynb i dovršite svih četiri dijela:
Izazov za dodatno usavršavanje 1: proširite shemu potvrde dodatnim poljem po vlastitom izboru (na primjer, ID zahtjeva za praćenje), ažurirajte kanonsku logiku potpisivanja da ga uključi i potvrdite da potvrda i dalje prolazi kroz verifikaciju. Zatim izmijenite polje nakon potpisivanja i potvrdite da verifikacija ne uspijeva. Ovo vas prisiljava da razumijete kako svaki bajt kanonskog kodiranja doprinosi potpisu.
Izazov za dodatno usavršavanje 2: SHA-256 hashirajte dvije svoje potvrde zajedno (spojite njihove kanonske bajtove u determinističkom redu) i ugradite dobiveni sažetak kao novo polje na trećoj potvrdi prije njenog potpisivanja. Verificirajte da sve tri potvrde i dalje prolaze verifikaciju. Upravo ste izgradili dokaz uključivanja u jednom koraku: svatko tko posjeduje treću potvrdu može dokazati da su prve dvije postojale u vrijeme njenog potpisivanja, bez potrebe da otkriva njihov sadržaj. Ovo je obrazac koji potvrde selektivnog otkrivanja koriste u velikoj mjeri (Merkle obveze, RFC 6962).
Kriptografske potvrde daju AI agentima trag revizije koji je:
Nisu zamjena za provjeru unosa, provedbu politike, dozvolu izvršitelja,
dokaz ishoda, kontrole potpunosti ili infrastrukturu identiteta. Oni su temelj
za te slojeve. U reguliranim ili višestruko-organizacijskim tijekovima rada, potvrde čine
pojedinačne izjave izdavatelja neovisno provjerljivima pod povjerenjem revizora.
Najvažnija poruka: potvrde dokazuju da je određeni ključ potpisao određeni
sadržaj. Priložena vremenska oznaka je potpisnikova tvrdnja osim ako nije zasebno vremenski potvrđena.
Potvrda ne dokazuje da je njen sadržaj bio istinit, da je akcija izvršena ili da je
skup zapisa potpun. Držite te razlike čvrsto.
Kad ste spremni preći s ove lekcije na implementaciju agenata s potpisanim potvrdam u stvarnom okruženju:
Pridružite se Microsoft Foundry Discord kako biste se upoznali s drugim učenicima, sudjelovali u uredskim satima i dobili odgovore na pitanja o AI agentima.
Ova lekcija pokriva potpisivanje pojedinačnih potvrda i sekvence lančanih hashova. Isti primitivni elementi sastavljaju se u nekoliko naprednijih obrazaca na koje možete naići kako se vaš upravljački stav razvija:
authorization_*) i poslije-izvršne (result_*) polovice s neovisnim potpisima, korisno kada odluku o autorizaciji i promatrani rezultat proizvode različiti akteri ili u različitim vremenima. Ovo se aditivno nadograđuje na format potvrde podučavan u ovoj lekciji.result_hash. Stvarni tereti često su bogatiji od rezultata jednog poziva alatu: razlozi prije odluke (predviđanje modela, razmotrene opcije, dokaz i njegova potpunost, rizik, lanac odgovornosti, rezultat kapije) mogu svi živjeti unutar tereta, zatvoreni jedinstvenom potvrdom. Ovo održava format potvrde minimalnim dok dopušta šemu tereta da se razvija po domenama.alg sama nije migracija: profil mora definirati ML-DSA kodiranje ključa, kanonske potpisane bajtove, verifikaciju, politiku oviseće strane, otpornost na degradaciju i semantiku prijelaza. Planirajte i testirajte dvostruku potpisnu tranziciju prema zajedničkim vektorima prije ikakvih tvrdnji o kompatibilnosti.Napomena: Ovaj dokument je preveden korištenjem AI prevoditeljskog servisa Co-op Translator. Iako težimo točnosti, imajte na umu da automatski prijevodi mogu sadržavati greške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati autoritativnim izvorom. Za važne informacije preporuča se profesionalni ljudski prijevod. Nismo odgovorni za bilo kakva nesporazumevanja ili pogrešne interpretacije koje proizlaze iz korištenja ovog prijevoda.