Obejrzyj wideo z lekcją: Zabezpieczanie agentów AI za pomocą kryptograficznych potwierdzeń
(Wideo z lekcją i miniaturka zostaną dodane przez zespół Microsoft ds. treści po scaleniu, zgodnie ze wzorem lekcji 14 / 15.)
Ta lekcja obejmie:
Po ukończeniu tej lekcji będziesz potrafił:
Wyobraź sobie, że wdrożyłeś agenta AI dla Contoso Travel. Agent czyta prośby klientów, wywołuje API lotów, aby wyszukać opcje, i rezerwuje miejsca w imieniu klienta. W ostatnim kwartale agent przetworzył 50 000 rezerwacji.
Dziś przychodzi audytor. Zadaje proste pytanie: „Pokaż mi, co zrobił twój agent.”
Przekazujesz pliki dziennika. Audytor je przegląda i zadaje trudniejsze pytanie: „Skąd mam wiedzieć, że te dzienniki nie zostały zmienione?”
To jest problem śladu audytu. Większość obecnych wdrożeń agentów opiera się na:
Żaden z tych sposobów nie odpowiada na pytanie audytora bez wymogu zaufania do kogoś (ciebie, twojego dostawcy chmury, producenta bazy danych). W zastosowaniach wewnętrznych takie zaufanie jest często akceptowalne. W regulowanych obciążeniach (finanse, opieka zdrowotna, wszystko podlegające EU AI Act) nie jest.
Kryptograficzne potwierdzenia rozwiązują to, czyniąc każdą akcję agenta niezależnie weryfikowalną. Audytor nie musi ufać tobie. Potrzebuje tylko twojego klucza publicznego i samego potwierdzenia.
Potwierdzenie to obiekt JSON rejestrujący, co agent zrobił, podpisany cyfrowo.
flowchart LR
A[Agent wywołuje narzędzie] --> B[Buduj ładunek potwierdzenia]
B --> C[Kanoniczuj JSON RFC 8785]
C --> D[Hash SHA-256]
D --> E[Podpis Ed25519]
E --> F[Potwierdzenie z podpisem]
F --> G[Audytor weryfikuje offline]
G --> H{Podpis prawidłowy?}
H -- tak --> I[Dowód odporny na manipulacje]
H -- nie --> J[Potwierdzenie odrzucone]
Minimalne potwierdzenie wygląda tak:
{
"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..."
}
}
Trzy właściwości wykonują ważną pracę:
Podpis. Potwierdzenie jest podpisane przez bramę agenta za pomocą prywatnego klucza Ed25519. Każdy posiadający odpowiadający klucz publiczny może offline zweryfikować podpis. Zmiana jakiegokolwiek pola unieważnia podpis.
Kodowanie kanoniczne. Przed podpisaniem potwierdzenie jest serializowane z użyciem JSON Canonicalization Scheme (JCS, RFC 8785). Zapewnia to, że dwa różne implementacje generujące ten sam logiczny dokument dadzą identyczną sekwencję bajtów. Bez kanonizacji różne serializatory JSON generowałyby różne podpisy dla tej samej zawartości.
Łańcuchowanie hashy. Pole previous_receipt_hash łączy każde potwierdzenie z poprzednim. Usunięcie lub zmiana kolejności potwierdzenia łamie każde kolejne potwierdzenie. Manipulacje stają się widoczne na poziomie całego łańcucha, nawet jeśli pojedyncze podpisy zostaną obejścié.
Razem te właściwości dają trzy gwarancje:
Nie potrzebujesz specjalnej biblioteki do wygenerowania potwierdzenia. Prymitywy kryptograficzne są powszechnie dostępne, a logika zajmuje kilkadziesiąt linijek Pythona.
Ćwiczenia praktyczne w code_samples/18-signed-receipts.ipynb prowadzą przez cały proces. Podsumowanie:
import json
import hashlib
import base64
from nacl import signing
from jcs import canonicalize # RFC 8785 kanoniczny 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()}"
# Wygeneruj lub załaduj klucz podpisu (w produkcji przechowuj w sejfie na klucze)
signing_key = signing.SigningKey.generate()
verify_key = signing_key.verify_key
# Zbuduj ładunek paragonu (jeszcze bez podpisu)
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,
}
# Kanonizuj, haszuj, podpisuj.
canonical_bytes = canonicalize(payload)
message_hash = hashlib.sha256(canonical_bytes).digest()
signature_bytes = signing_key.sign(message_hash).signature
# Dołącz strukturalny obiekt podpisu.
receipt = {
**payload,
"signature": {
"alg": "EdDSA",
"sig": b64url_nopad(signature_bytes),
"public_key": b64url_nopad(bytes(verify_key)),
},
}
To cały proces podpisywania. Ćwiczenia w notatniku przechodzą przez każdy etap.
Weryfikacja to operacja odwrotna:
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:
# Podpis jest ustrukturyzowanym obiektem: {"alg", "sig", "public_key"}.
sig_obj = receipt.get("signature")
if not sig_obj or sig_obj.get("alg") != "EdDSA":
return False
# Odtwórz ładunek, który faktycznie został podpisany (wszystko oprócz podpisu).
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
Ta funkcja przyjmuje potwierdzenie i zwraca True jeśli podpis jest ważny, w przeciwnym wypadku False. Bez wywołań sieci, bez zależności od zewnętrznych usług, bez konieczności zaufania żadnej stronie trzeciej.
Aby zobaczyć działanie wykrywania manipulacji, notatnik demonstruje:
tool_args_hash.To praktyczna demonstracja, że potwierdzenia są odporne na manipulacje: każda zmiana, nawet najmniejsza, niszczy ważność podpisu.
Pojedyncze podpisane potwierdzenie chroni jedną akcję. Łańcuch potwierdzeń chroni sekwencję.
flowchart LR
R0[Potwierdzenie 0<br/>geneza] --> R1[Potwierdzenie 1]
R1 --> R2[Potwierdzenie 2]
R2 --> R3[Potwierdzenie 3]
R1 -. previous_receipt_hash .-> R0
R2 -. previous_receipt_hash .-> R1
R3 -. previous_receipt_hash .-> R2
Każde potwierdzenie zapisuje hash potwierdzenia poprzedniego. Aby cicho usunąć potwierdzenie nr 2, atakujący musiałby:
previous_receipt_hash w potwierdzeniu nr 3 (co unieważnia podpis potwierdzenia nr 3), LUBJeśli klucz prywatny jest w sprzętowym sejfie kluczy, a ty publikujesz klucz publiczny z każdym potwierdzeniem, żadna z tych metod nie jest możliwa bez wykrycia.
Notatnik przeprowadza przez:
previous_receipt_hash z prawdziwym hashem poprzedniego potwierdzenia.Tak tworzysz ślad audytu, który zewnętrzny audytor może zweryfikować bez konieczności zaufania tobie.
To najważniejsza sekcja tej lekcji. Potwierdzenia są potężne, ale ich moc jest ograniczona.
Potwierdzenia udowadniają trzy rzeczy:
Potwierdzenia NIE udowadniają:
policy_id była faktycznie oceniana lub że pozwoliłaby na tę akcję po kontroli. Potwierdzenie rejestruje, co zostało zadeklarowane, nie to, co zostało wymuszone.Ta granica jest istotna z dwóch powodów:
Powszechny błąd to zakładanie, że „mamy potwierdzenia” oznacza „jesteśmy zarządzani.” Nie oznacza. Potwierdzenia to fundament. Zarządzanie to system, który budujesz na jego bazie.
Kod Python w tej lekcji jest celowo minimalny, abyś mógł przeczytać każdą linijkę i dokładnie zrozumieć przebieg. W produkcji masz dwie opcje:
Budować bezpośrednio na prymitywach kryptograficznych. 50 linijek powyżej wystarcza w wielu przypadkach. PyNaCl (Ed25519) i pakiet jcs (kanoniczny JSON) to dobrze utrzymywane i audytowane biblioteki.
Używać produkcyjnej biblioteki potwierdzeń. Kilka projektów open source implementuje ten sam wzorzec z dodatkowymi funkcjami (rotacja kluczy, weryfikacja wsadowa, dystrybucja zestawu JWK, integracja z silnikami polityk):
draft-farley-acta-signed-receipts) obecnie w procesie standaryzacji.protect-mcp (npm) i @veritasacta/verify (npm) oferują implementację Node do podpisywania i offline weryfikacji potwierdzeń, przeznaczoną do owinięcia dowolnego serwera MCP w tamper-evident ścieżkę audytu.Decyzja między własnym rozwiązaniem a biblioteką przypomina decyzję między własną biblioteką JWT a testowaną biblioteką: oba podejścia są rozsądne; biblioteka oszczędza czas i redukuje powierzchnię audytu; własne rozwiązanie wymusza zrozumienie każdego prymitywu. Ta lekcja uczy podejścia od podstaw, byś miał fundament pod oba wybory.
Sprawdź swoje rozumienie przed przejściem do ćwiczenia praktycznego.
1. Potwierdzenie jest podpisane prywatnym kluczem Ed25519 agenta. Audytor ma tylko klucz publiczny. Czy audytor może zweryfikować potwierdzenie offline?
2. Atakujący zmienia pole policy_id w potwierdzeniu, by twierdzić, że było nim rządzone bardziej liberalne reguły. Podpis był nad oryginalnym obiektem. Co się stanie podczas weryfikacji?
3. Dlaczego potwierdzenie zawiera tool_args_hash i result_hash zamiast surowych argumentów i wyników?
4. Pole previous_receipt_hash łączy potwierdzenia w łańcuch. Jeśli atakujący usunie cicho jedno potwierdzenie z środka łańcucha, co stanie się nieważne?
5. Potwierdzenie weryfikuje się poprawnie. Czy to dowodzi, że działanie agenta było poprawne, słuszne lub zgodne z polityką?
Otwórz code_samples/18-signed-receipts.ipynb i wykonaj wszystkie cztery sekcje:
Dodatkowe wyzwanie 1: rozszerz schemat potwierdzenia o dodatkowe, własne pole (np. identyfikator żądania dla śledzenia), zaktualizuj logikę kanonicznego podpisywania, by je uwzględnić, i potwierdź, że potwierdzenie przechodzi weryfikację. Następnie zmodyfikuj pole po podpisaniu i potwierdź niepowodzenie weryfikacji. To wymusza zrozumienie, jak każdy bajt kodowania kanonicznego wpływa na podpis. Wyzwanie dodatkowe 2: Podwójnie zahashuj SHA-256 dwa swoje pokwitowania razem (łącząc ich kanoniczne bajty w deterministycznej kolejności) i osadź powstały skrót jako nowe pole na trzecim pokwitowaniu przed jego podpisaniem. Zweryfikuj, że wszystkie trzy pokwitowania nadal poprawnie się odtwarzają. Właśnie stworzyłeś dowód włączenia jednokrokowego: każdy posiadający trzecie pokwitowanie może udowodnić, że pierwsze dwa istniały w momencie jego podpisania, bez potrzeby ujawniania ich zawartości. Jest to wzorzec używany przez pokwitowania selektywnego ujawniania na dużą skalę (zobowiązania Merkle’a, RFC 6962).
Kryptograficzne pokwitowania dają agentom AI ślad audytowy, który jest:
Nie zastępują one walidacji wejścia, egzekwowania polityk ani infrastruktury tożsamości. Stanowią fundament dla tych warstw. Gdy wdrażasz agentów do środowisk regulowanych, przepływów pracy obejmujących wiele organizacji lub w każdej sytuacji, gdzie przyszły audytor nie może zakładać, że ci ufa, pokwitowania są sposobem na uczciwy ślad audytowy.
Najważniejszy wniosek: pokwitowania udowadniają, kto co powiedział i kiedy. Nie udowadniają, że to, co powiedziano, było prawdziwe lub słuszne. Traktuj tę różnicę poważnie. To różnica między uczciwym systemem pochodzenia a mylącym.
Gdy będziesz gotów przejść do wdrożenia agentów podpisujących pokwitowania w środowisku produkcyjnym:
https://twoja-org.example.com/.well-known/agent-keys.json.Dołącz do Microsoft Foundry Discord, aby spotkać innych uczących się, uczestniczyć w godzinach konsultacji i uzyskać odpowiedzi na pytania o agentach AI.
Lekcja ta obejmuje podpisywanie pojedynczego pokwitowania i łańcuchy hashów. Te same prymitywy składają się na kilka bardziej zaawansowanych wzorców, które możesz napotkać, gdy twoja postawa zarządcza dojrzeje:
authorization_*) i powykonawczą (result_*) z niezależnymi podpisami, przydatne gdy decyzję o autoryzacji i obserwowany wynik podejmują różni aktorzy lub w różnym czasie. Komponuje się to dodatkowo na formacie pokwitowania nauczonym w tej lekcji.result_hash. Rzeczywiste ładunki często są bogatsze niż pojedynczy wynik wywołania narzędzia: rozumowanie przed decyzją (predykcja modelu, rozpatrywane opcje, dowody i ich kompletność, postawa ryzyka, łańcuch odpowiedzialności, wynik bramki) może mieszkać w ładunku, zapieczętowany pojedynczym pokwitowaniem. Utrzymuje to format pokwitowania minimalistyczny, umożliwiając ewolucję schematów ładunku w domenach.signature.alg może zawierać ML-DSA-65 (standard podpisu postkwantowego NIST) gdy potrzebujesz migracji. Zaplanuj okres przejściowy, gdy pokwitowania są podpisywane podwójnie.Building Computer Use Agents (CUA)
(Do ustalenia przez opiekunów programu nauczania)
Zastrzeżenie: Niniejszy dokument został przetłumaczony za pomocą usługi tłumaczenia AI Co-op Translator. Choć dążymy do dokładności, prosimy pamiętać, że automatyczne tłumaczenia mogą zawierać błędy lub niedokładności. Oryginalny dokument w jego języku źródłowym należy uznawać za autorytatywne źródło. W przypadku informacji krytycznych zalecane jest skorzystanie z profesjonalnego tłumaczenia wykonanego przez człowieka. Nie ponosimy odpowiedzialności za jakiekolwiek nieporozumienia lub błędne interpretacje wynikające z użycia tego tłumaczenia.