ai-agents-for-beginners

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

Sikring af AI-agenter med kryptografiske kvitteringer

Introduktion

Denne lektion vil dække:

Læringsmål

Efter at have gennemført denne lektion vil du vide, hvordan du:

Problemet: Dit agents revisionsspor

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.

Hvad er en kryptografisk kvittering?

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:

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

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

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

Produktion af en kvittering i Python

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 af en kvittering og opdage manipulation

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:

  1. Produktion af en gyldig kvittering og bekræftelse af at den verificeres.
  2. Ændring af et enkelt byte i feltet tool_args_hash.
  3. Gentagelse af verifikation og observering af fejlslag.

Dette er den praktiske demonstration af, at kvitteringer er manipulationssynlige: enhver ændring, uanset hvor lille, bryder signaturen.

Kædning af kvitteringer for flerstegs-agenter

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:

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

  1. Opbygning af en kæde på tre kvitteringer.
  2. Verifikation af, at hver kvitterings previous_receipt_hash matcher den faktiske hash af den foregående kvittering.
  3. Manipulation af en kvittering midt i kæden og observation af at kæden bryder præcis der.

Sådan producerer du et revisionsspor, som en ekstern revisor kan verificere uden at stole på dig.

Hvad kvitteringer beviser (og hvad de ikke gør)

Dette er det vigtigste afsnit i denne lektion. Kvitteringer er kraftfulde, men deres kraft er begrænset.

Kvitteringer beviser tre ting:

  1. Tilskrivning: en specifik nøgle har signeret en specifik pakke.
  2. Integritet: pakken har ikke ændret sig siden signering.
  3. Rækkefølge: denne kvittering kom efter den kvittering i hashkæden.

Kvitteringer beviser IKKE:

  1. Korrekthed: at agentens handling var den rigtige handling. En kvittering kan signeres for et forkert svar lige så let som for et rigtigt svar.
  2. Politikoverholdelse: at den politik, som refereres i 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.
  3. Identitet ud over nøglen: kvitteringen siger “denne nøgle har signeret dette indhold.” Den siger ikke “denne person har godkendt dette.” At forbinde en nøgle til en person eller organisation kræver separat identitetsinfrastruktur (et katalog, et register over offentlige nøgler osv.).
  4. Sandhed i inputs: hvis agenten modtager en manipuleret prompt og handler på den, registrerer kvitteringen handlingen troværdigt. Kvitteringer er downstream for inputvalidering, ikke en erstatning derfor.

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

Produktionsreferencer

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:

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

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

    • Kvitteringsformatet brugt i denne lektion følger et IETF Internet-Draft (draft-farley-acta-signed-receipts), som er under standardiseringsprocessen.
    • Microsoft Agent Governance Toolkit sammensætter kvitteringer med Cedar-baserede politiske beslutninger; se Tutorial 33 i den repository for et eksempel fra ende til anden.
    • Pakkerne 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.

Videnscheck

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?

Svar Ja. Ed25519-verifikation kræver kun den offentlige nøgle og de signerede bytes. Ingen netværkskald, ingen tjenesteafhængighed. Dette er egenskaben, der gør kvitteringer nyttige i luftgap, multi-organisationer eller lavtillids auditsituationer.

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?

Svar Verifikationen fejler. Signaturen blev beregnet over de kanoniske bytes af den oprindelige pakke; ændring af et hvilket som helst felt ændrer de kanoniske bytes, som ændrer SHA-256 hashen, hvilket gør signaturen ugyldig. Angriberen ville skulle have den private nøgle for at producere en frisk gyldig signatur, hvilket de ikke har.

3. Hvorfor indeholder kvitteringen en tool_args_hash og result_hash i stedet for de rå argumenter og resultat?

Svar To grunde. For det første kan kvitteringen være nødt til at arkiveres eller overføres i miljøer, hvor lækage af rå indhold (persondata, forretningsdata) er et problem. Hashing holder kvitteringen lille og indholdet privat; revisors verifikation sikrer, at hashen matcher en separat opbevaret kopi af det faktiske indhold. For det andet har hashes en fast størrelse; en kvittering med hashes er størrelsesmæssigt begrænset uanset hvor store inputs og outputs var.

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?

Svar Alle de kvitteringer, der kom efter den slettede. Deres `previous_receipt_hash`-felter stemmer ikke længere overens med den faktiske kæde (fordi den kvittering, de refererede til, ikke længere findes, eller kæden nu peger på en anden forgænger). For at skjule sletningen skulle angriberen gensigne alle efterfølgende kvitteringer, hvilket kræver den private nøgle.

5. En kvittering verificeres rent. Beviser det, at agentens handling var korrekt, holdbar eller overholdt politikken?

Svar Nej. En gyldig kvittering beviser tre ting: tilskrivning (denne nøgle har signeret dette indhold), integritet (indholdet er uændret), og rækkefølge (denne kvittering kom efter den kvittering). Den beviser IKKE, at handlingen var korrekt, at politikken navngivet i `policy_id` rent faktisk blev evalueret, eller at agenten fulgte alle regler. Kvitteringer gør agentadfærd auditerbar, ikke nødvendigvis korrekt. Dette er den vigtigste grænse i lektionen.

Praksisøvelse

Åbn code_samples/18-signed-receipts.ipynb og gennemfør alle fire sektioner:

  1. Sektion 1: Signér din første kvittering og verificér den.
  2. Sektion 2: Manipuler kvitteringen og se verifikationen fejle.
  3. Sektion 3: Byg en kæde af tre kvitteringer og verificér kædens integritet.
  4. Sektion 4: Anvend mønstret på en agent bygget med Microsoft Agent Framework: indpak et værktøjskald i kvitteringssignering, og verificér derefter kvitteringen uafhængigt.

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

Konklusion

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.

Produktionscheckliste

Når du er klar til at gå videre fra denne lektion til at udrulle kvitteringsunderskrevne agenter i et reelt miljø:

Flere spørgsmål om sikring af AI-agenter?

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.

Udover denne lektion

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:

Yderligere ressourcer

Forrige lektion

Building Computer Use Agents (CUA)

Næste lektion

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