Peržiūrėkite pamokos vaizdo įrašą: AI agentų apsauga naudojant kriptografinius kvitus
(Pamokos vaizdo įrašą ir miniatiūrą po sujungimo pridės Microsoft turinio komanda, atitinkančią 14/15 pamokos modelį.)
Šioje pamokoje aptarsime:
Baigę šią pamoką, jūs mokėsite:
Įsivaizduokite, kad įdiegėte AI agentą Contoso Travel. Agentas skaito klientų užklausas, kviečia skrydžių API ieškoti variantų ir užsako vietas kliento vardu. Praėjusį ketvirtį agentas apdorodavo 50 000 užsakymų.
Šiandien atvyksta auditorius. Jis užduoda paprastą klausimą: „Parodykite, ką atliko jūsų agentas.“
Pateikiate savo žurnalų failus. Auditorius juos peržiūri ir dalija sudėtingesnį klausimą: „Kaip žinau, kad šie žurnalai nebuvo redaguoti?“
Tai yra audito tako problema. Dauguma agentų diegimų šiandien remiasi:
Nė viena iš šių parinkčių negali atsakyti auditoriaus klausimui be būtinybės pasitikėti kažkuo (jumis, jūsų debesų tiekėju, duomenų bazės tiekėju). Vidiniam naudojimui toks pasitikėjimas dažnai yra priimtinas, bet reglamentuojamoms darbų apkrovoms (finansai, sveikatos priežiūra, ES AI aktui pavaldžios sritys) taip nėra.
Kriptografiniai kvitai sprendžia šią problemą, leidžiant kiekvieną agento veiksmą nepriklausomai patikrinti. Auditorius neturi jums pasitikėti. Jam reikia tik jūsų viešojo rakto ir paties kvito.
Kvitas yra JSON objektas, kuris įrašo, ką agentas atliko, ir yra pasirašytas skaitmeniniu parašu.
flowchart LR
A[Agentas iškviečia įrankį] --> B[Sudaryti kvito duomenis]
B --> C[Kanonizuoti JSON RFC 8785]
C --> E[Pasirašyti kanoninius baitus su Ed25519]
E --> F[Kvitas su parašu]
F --> G[Auditorius tikrina neprisijungęs]
G --> H{Parašas galioja?}
H -- yes --> I[Aptikti klastojimą įrodymas]
H -- no --> J[Kvitas atmestas]
Minimalus kvitas atrodo taip:
{
"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..."
}
}
Trys savybės atlieka darbą:
Parašas. Kvitas pasirašomas agento vartų Ed25519 privačiu raktu. Bet kas turintis atitinkamą viešąjį raktą gali patikrinti parašą neprisijungus. Bet koks lauko pakeitimas paneigia parašą.
Kanoninė koduotė. Prieš pasirašant kvitas serializuojamas naudojant JSON Kanonizacijos Schemos (JCS, RFC 8785) standartą. Tai užtikrina, kad dvi įgyvendinimo versijos, generuojančios tą patį logišką kvitą, duoda baitų identišką išvestį. Be kanonizavimo skirtingi JSON serializatoriai sukurtų skirtingus parašus tame pačiame turinyje.
Maišų grandinimas. previous_receipt_hash laukas sujungia kiekvieną kvitą su ankstesniu. Pašalinus arba pakeitus kvitą grandinė sulūžta viduryje. Klastojimas tampa matomas grandinės lygiu net jei pavieniai parašai būtų apeiti.
Šios savybės kartu suteikia tris garantijas:
Kvito kūrimui nereikia specialios bibliotekos. Kriptografiniai primityvai plačiai prieinami, o logika - kelių dešimčių eilučių Python kalba.
Praktiniai pratimai faile code_samples/18-signed-receipts.ipynb parodo visą procesą. Apibendrinimas:
import json
import hashlib
import base64
from nacl import signing
from jcs import canonicalize # RFC 8785 kanoninis 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()}"
# Sugeneruoti arba įkelti parašo raktą (produkcijoje laikyti raktų saugykloje)
signing_key = signing.SigningKey.generate()
verify_key = signing_key.verify_key
# Sudaryti kvito duomenų paketą (dar be parašo)
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,
}
# Kanonizuoti ir pasirašyti JCS baitus tiesiogiai. Viduje naudojami PureEdDSA maišos.
canonical_bytes = canonicalize(payload)
signature_bytes = signing_key.sign(canonical_bytes).signature
# Pridėti struktūruotą parašo objektą.
receipt = {
**payload,
"signature": {
"alg": "EdDSA",
"sig": b64url_nopad(signature_bytes),
"public_key": b64url_nopad(bytes(verify_key)),
},
}
Tai visa pasirašymo grandinė. Praktiniuose užsiėmimuose atskirai išsamiai pereinama per kiekvieną žingsnį.
Patikra - tai atvirkštinė operacija:
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:
# Parašas yra struktūruotas objektas: {"alg", "sig", "public_key"}.
sig_obj = receipt.get("signature")
if not sig_obj or sig_obj.get("alg") != "EdDSA":
return False
# Atstatykite faktinį pasirašytą turinį (viską, išskyrus parašą).
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
Ši funkcija ima kvitą ir grąžina True, jei parašas yra teisingas, False - jei ne. Nereikia jokių tinklo užklausų, paslaugų priklausomybės ar pasitikėjimo tretiesiems asmenims.
Norėdami pamatyti klastojimo aptikimo veikimą, praktiniame faile demonstruojama:
tool_args_hash lauke.Tai praktinis įrodymas, kad kvitai yra atsparūs klastojimui: bet koks pakeitimas, net ir menkiausias, sulaužo parašą.
Vienas pasirašytas kvitas saugo vieną veiksmą. Kvito grandinė saugo veiksmų seką.
flowchart LR
R0[Kvitas 0<br/>genesis] --> R1[Kvitas 1]
R1 --> R2[Kvitas 2]
R2 --> R3[Kvitas 3]
R1 -. previous_receipt_hash .-> R0
R2 -. previous_receipt_hash .-> R1
R3 -. previous_receipt_hash .-> R2
Kiekvienas kvitas įrašo ankstesnio kvito maišą. Norint tyliai pašalinti antrąjį kvitą, užpuolikas turėtų arba:
previous_receipt_hash lauką (sulaužytų trečio kvito parašą), ARBAJei privatus raktas saugomas aparatinėje rakto saugykloje, o viešasis raktas skelbiamas su kiekvienu kvitu, nė vienas iš šių atakų nėra įmanomas be aptikimo.
Praktiniame faile demonstruojama:
previous_receipt_hash sutampa su ankstesnio kvito maišu.Taip sukuriamas audito takas, kurį išorinis auditorius gali patikrinti neprisirišdamas prie jūsų.
Tai svarbiausia šios pamokos dalis. Kviti yra galingi, tačiau jų galia yra ribota.
Kvitai įrodo tris dalykus:
Kvitai NEĮRODO:
policy_id nurodyta politika buvo tikrai įvertinta arba kad ji būtų leidusi šį veiksmą, jei būtų tikrinta. Kvitas įrašo, kas buvo teigiama, o ne kas buvo įgyvendinta.Ši riba svarbi dėl dviejų priežasčių:
Dažna klaida manyti, kad „turime kvitus“ reiškia „turime valdymą“. Ne. Kviti yra pagrindas. Valdymas – tai sistema, kurią statote viršuje.
Punktas 3 verta atskiros dalies: veiksmo kvitas sako „šis raktas pasirašė šį turinį“, bet niekada „žmogus autorizavo šį veiksmą“. Aukštos rizikos veiksmams (grąžinimams, ištrynimams, laidų pervedimams) valdymo sistemos vis dažniau reikalauja būtent tokio trūkstamo teiginio, ir tai galima sukurti naudojant tuos pačius primityvus, kuriuos jau pritaikėte šioje pamokoje.
Tolimesnis praktinis failas code_samples/human-authorization-receipts.ipynb prideda antrą kvito tipą, human.approval.v1, tokiu pačiu voku kaip ir pamokos kvitai (tipuotas apkrovos objektas, pasirašytas Ed25519 kanoninių JCS baitų formatu, su signature objektu už pasirašytų baitų ribų). Pavadintas patvirtintojas pasirašo visą kanoninį veiksmą ir jo santrauką prieš vykdymą; agento veiksmų kvitas neša tą patį veiksmų santrauką ir parent_approval_ref, tai yra patvirtinimo receipt_hash, tas pats principas kaip previous_receipt_hash grandinėje, kurią sukūrėte aukščiau. Viena funkcija verify_chain patikrina abu šiuos artefaktus pagal atskirus fiksuotų raktų registrus (patvirtintojų raktus prieš agentų raktus), todėl kodo kelias yra bendras, bet įgaliojimų niekada neliečia.
Ši savybė, išdėstyta atidžiai: žmogus patvirtino būtent šį veiksmą, o agentas iš tiesų vykdė tik tą patvirtintą veiksmą. Praktinio failo atsisakymų patikros mechanizmai paverčia šią savybę realia, o ne tik deklaratyvia:
Kiekvienas neatitikimas grąžina skirtingą klaidos priežastį, tad auditorius gali atskirti, ar įgaliojimas paseno, ar vykdytas veiksmas pasikeitė. Pamokoje mokoma taisyklė: pasirašytas patvirtinimas nėra pats įgaliojimas. Įgaliojimas egzistuoja tik jei abu kvitai vis dar susieja tą patį kanoninį veiksmą vykdymo metu. Žmogaus patvirtinimo kvitas yra šios pamokos edukacinė kompozicija, o ne draft-farley-acta-signed-receipts apibrėžtas kvito tipas.
Python kodas šioje pamokoje yra sąmoningai minimalus, kad galėtumėte perskaityti kiekvieną eilutę ir tiksliai suprasti, kas vyksta. Gamyboje turite dvi galimybes:
Kurti tiesiogiai ant kriptografinių primityvų. 50 eilučių, kurias matėte aukščiau, pakanka daugeliui naudojimo atvejų. PyNaCl (Ed25519) ir jcs paketas (kanoninis JSON) yra gerai prižiūrimos ir audituotos bibliotekos.
Naudoti gamybai skirtą kvitų biblioteką. Keli atviri projektai įgyvendina tą patį modelį su papildomomis funkcijomis (raktų rotacija, paketinis tikrinimas, JWK rinkinio platinimas, integracija su politikos varikliais):
draft-farley-acta-signed-receipts, 02 versija). Šios pamokos paprastas edukacinis kvitas skiriasi nuo brėžinio {payload, signature} voko ir nėra pateikiamas kaip atitikties įgyvendinimas. Brėžinyje skelbiama bendra atitikties testų serija (agent-governance-testvectors) implementacijoms, siekiančioms jo formatą.protect-mcp (npm) ir @veritasacta/verify (npm) paketai teikia Node įrankius kvitų pasirašymui ir neprisijungus tikrinimui, skirti bet kurį MCP serverį apgaubti su aptikimo audito taku, įskaitant paklaidinimą laukiant patvirtinimo, kai pristabdyta veiksmo funkcija išleidžia patvirtinimo kvitą, susietą su veiksmo santrauka (darbalaukio sraute palaikoma WebAuthn), tas pats patvirtinimo kvito modelis kaip žmogaus autorizavimo faile aukščiau.pip install nobulex) teikia tą patį Ed25519 + JCS pasirašymo modelį Python kalboje su LangChain ir CrewAI integracijomis, įskaitant paskelbtus kryžminės validacijos testų vektorius ir atitikties žemėlapį, prisidėtą per OWASP PR #2210.Sprendimas tarp savo kodavimo ir bibliotekos naudojimo yra panašus į sprendimą tarp savarankiško JWT bibliotekos rašymo ir patikrintos naudojimo: abu variantai yra pagrįsti; biblioteka taupo laiką ir mažina audito plotą; savarankiškas kelias priverčia suprasti kiekvieną primityvą. Ši pamoka moko savarankiško kelio, kad turėtumėte pagrindą abiems pasirinkimams.
Patikrinkite savo supratimą prieš pereidami prie praktikos užduoties.
1. Kvitas yra pasirašytas agento privačiu Ed25519 raktu. Auditorius turi tik viešąjį raktą. Ar auditorius gali patikrinti kvitą neprisijungęs?
2. Užpuolikas pakeičia kvito policy_id lauką, teigdamas, kad jį valdė liberalesnė politika. Parašas buvo darytas ant originalios apkrovos. Kas nutinka patikrinimo metu?
3. Kodėl kvite yra tool_args_hash ir result_hash, o ne žaliaviniai argumentai ir rezultatas?
4. Laukas previous_receipt_hash sujungia kiekvieną kvitą su jo pirmtaku. Jei užpuolikas tyliai ištrina vieną kvitą grandinės viduryje, kas tampa negaliojančiu?
5. Kvitas patikrinamas be klaidų. Ar tai įrodo, kad agento veiksmas buvo teisingas, pagrįstas ar atitinkantis politiką?
Atidarykite code_samples/18-signed-receipts.ipynb ir užbaikite visas keturias dalis:
Išplėstinis iššūkis 1: pridėkite papildomą savo pasirinktą lauką kvito schemoje (pavyzdžiui, užklausos ID stebėjimui), atnaujinkite kanoninę pasirašymo logiką įtraukdami jį ir patvirtinkite, kad kvitas vis dar gali būti patikrintas. Tada pakeiskite lauką po pasirašymo ir patvirtinkite, kad patikra nepavyksta. Tai privers jus suprasti, kaip kiekvienas kanoninės koduotės baitas prisideda prie parašo.
Išplėstinis iššūkis 2: SHA-256 maišykite du savo kvitus kartu (sujunkite jų kanoninius baitus deterministine tvarka) ir įterpkite gautą maišą kaip naują lauką trečiajame kvite prieš jį pasirašant. Patikrinkite, kad visi trys kvitai vis dar gali būti tikrinami. Jūs ką tik sukūrėte vieno žingsnio įtraukimo įrodymą: kas laikys trečią kvitą, gali įrodyti, kad pirmieji du egzistavo pasirašymo metu, neatskleisdamas jų turinio. Tai šablonas, kurį naudoja pasirinktinio atskleidimo kvitai mastu (Merkle įsipareigojimai, RFC 6962).
Kriptografiniai kvitai suteikia DI agentams auditavimo pėdsaką, kuris yra:
Jie nėra įėjimų patvirtinimo, politikos vykdymo ar tapatybės infrastruktūros pakaitalas. Jie yra šių sluoksnių fundamentas. Kai įdiegi agentus reguliuojamuose darbiniuose procesuose, daugorganizių darbo eigoje ar bet kur, kur ateities auditoriui negalima tikėti, kvitai yra kaip padaryti audito pėdsaką sąžiningu.
Svarbiausia išvada: kvitai įrodo, kas ką sakė ir kada. Jie neįrodo, kad pasakyta buvo tiesa ar teisinga. Laikykite šį skirtumą aiškiai. Tai skirtumas tarp sąžiningos kilmės sistemos ir klaidinančios.
Kai būsite pasirengę pereiti nuo pamokos prie kvitu pasirašytų agentų diegimo realiame aplinkoje:
https://your-org.example.com/.well-known/agent-keys.json.Prisijunkite prie Microsoft Foundry Discord, susitikite su kitais mokiniais, dalyvaukite konsultacijose ir gaukite atsakymus į savo klausimus apie AI agentus.
Ši pamoka apima vieno kvito pasirašymą ir maišinių grandinių sekas. Tos pačios prietaisų kombinacijos sudaro kelis pažangesnius šablonus, kuriuos galite sutikti tobulėjant jūsų valdymo klausimams:
authorization_*) ir posvykdymo (result_*) dalis su nepriklausomais parašais, naudinga, kai autorizacijos sprendimą ir stebėtą rezultatą kuria skirtingi veikėjai arba skirtingu laiku. Tai gali būti papildoma virš šios pamokos mokomo formato.result_hash. Realūs pateikimai dažnai yra turtingesni nei vieno įrankio kvietimo rezultatas: priešsprendiminiai motyvai (modelio prognozė, svarstyti variantai, įrodymai ir jų pilnumas, rizikų pozicija, atskaitomybės grandinė, prieigos sprendimas) gali būti pateikimo viduje, užantspauduoti vienu kvitu. Tai palaiko minimalų kvito formatą, leidžiantį pateikimo schemoms evoliucionuoti pagal domeną.signature.alg gali turėti ML-DSA-65 (NIST po-kvantinės parašo standarto) reikšmę, kai reikia pereiti. Planuokite pereinamuosius laikotarpius su dvigubu kvitų pasirašymu.Atsakomybės apribojimas: Šis dokumentas buvo išverstas naudojant dirbtinio intelekto vertimo paslaugą Co-op Translator. Nors siekiame tikslumo, prašome atkreipti dėmesį, kad automatiniai vertimai gali turėti klaidų ar netikslumų. Originalus dokumentas jo gimtąja kalba laikomas autoritetingu šaltiniu. Svarbiai informacijai rekomenduojama naudoti profesionalų žmogiškąjį vertimą. Mes neatsakome už jokius nesusipratimus ar neteisingą interpretaciją, kilusią naudojantis šiuo vertimu.