Переглянути відео уроку: Захист AI-агентів за допомогою криптографічних квитанцій
(Відео уроку та мініатюра будуть додані командою контенту Microsoft після злиття, відповідно до патерну уроків 14 / 15.)
У цьому уроці буде розглянуто:
Після проходження цього уроку ви знатимете, як:
Уявіть, що ви розгорнули AI-агента для Contoso Travel. Агент читає запити клієнтів, викликає API квитків для пошуку варіантів і бронює місця від імені клієнта. У минулому кварталі агент обробив 50,000 бронювань.
Сьогодні приходить аудитор. Він ставить просте запитання: «Покажіть, що зробив ваш агент.»
Ви даєте журнали. Аудитор дивиться їх і ставить складніше запитання: «Як я можу знати, що ці логи не були відредаговані?»
Це й є проблема аудиторського сліду. Більшість сьогоднішніх розгортань агентів покладаються на:
Жоден із цих способів не може відповісти аудиторське питання без того, щоб аудитор не довіряв комусь (вам, вашому хмарному провайдеру або постачальнику бази даних). Для внутрішнього використання така довіра часто прийнятна. Для регламентованих навантажень (фінанси, охорона здоров’я, будь-що під юрисдикцією AI Act ЄС) — ні.
Криптографічні квитанції вирішують це, роблячи кожну дію агента незалежно перевірною. Аудитор не має довіряти вам. Йому потрібен лише ваш публічний ключ і сама квитанція.
Квитанція — це об’єкт JSON, який записує, що зробив агент, підписаний цифровим підписом.
flowchart LR
A[Агент виконує інструмент] --> B[Створення корисного навантаження квитанції]
B --> C[Канонізація JSON RFC 8785]
C --> D[Хеш SHA-256]
D --> E[Підпис Ed25519]
E --> F[Квитанція з підписом]
F --> G[Аудитор перевіряє офлайн]
G --> H{Підпис дійсний?}
H -- yes --> I[Доказ, що свідчить про спотворення]
H -- no --> J[Квитанція відхилена]
Мінімальна квитанція виглядає так:
{
"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..."
}
}
Три властивості виконують основну роботу:
Підпис. Квитанція підписується шлюзом агента за допомогою приватного ключа Ed25519. Будь-хто, хто має відповідний публічний ключ, може офлайн перевірити підпис. Зміна будь-якого поля робить підпис недійсним.
Канонічне кодування. Перед підписом квитанція серіалізується за схемою JSON Canonicalization Scheme (JCS, RFC 8785). Це гарантує, що дві реалізації, які виробляють один логічний вміст, генерують ідентичний у байтах результат. Без канонізації різні JSON-серіалізатори створювали б різні підписи для однакового вмісту.
Хеш-ланцюжок. Поле previous_receipt_hash пов’язує кожну квитанцію з попередньою. Видалення або зміна порядку однієї квитанції руйнує всі наступні. Підробка стає помітною на рівні ланцюжка, навіть якщо окремі підписи обходять.
Разом ці властивості забезпечують три гарантії:
Для створення квитанції вам не потрібна спеціальна бібліотека. Криптографічні примітиви широко доступні, а логіка — це кілька десятків рядків на Python.
Покрокові вправи у code_samples/18-signed-receipts.ipynb показують увесь процес. Коротко:
import json
import hashlib
import base64
from nacl import signing
from jcs import canonicalize # RFC 8785 канонічний 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()}"
# Згенерувати або завантажити ключ підпису (у виробництві зберігати в сховищі ключів)
signing_key = signing.SigningKey.generate()
verify_key = signing_key.verify_key
# Побудувати корисне навантаження квитанції (ще без підпису)
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,
}
# Канонізувати, хешувати, підписувати.
canonical_bytes = canonicalize(payload)
message_hash = hashlib.sha256(canonical_bytes).digest()
signature_bytes = signing_key.sign(message_hash).signature
# Додати структуру об'єкта підпису.
receipt = {
**payload,
"signature": {
"alg": "EdDSA",
"sig": b64url_nopad(signature_bytes),
"public_key": b64url_nopad(bytes(verify_key)),
},
}
Це увесь конвеєр підпису. Вправи в зошиті проходять кожен крок.
Перевірка — це обернена операція:
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:
# Підпис є структурованим об'єктом: {"alg", "sig", "public_key"}.
sig_obj = receipt.get("signature")
if not sig_obj or sig_obj.get("alg") != "EdDSA":
return False
# Відновіть навантаження, яке було фактично підписане (все, крім підпису).
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
Ця функція приймає квитанцію і повертає True, якщо підпис дійсний, і False інакше. Жодних мережевих викликів, жодної залежності від сервісу, жодної потрібної довіри до третьої сторони.
Щоби побачити в дії виявлення підробки, у зошиті демонструють:
tool_args_hash.Це практична демонстрація, що квитанції є підробно-виявними: будь-яка зміна, навіть найменша, порушує підпис.
Одна підписана квитанція захищає одну дію. Ланцюг квитанцій захищає послідовність дій.
flowchart LR
R0[Резюме 0<br/>генезис] --> R1[Резюме 1]
R1 --> R2[Резюме 2]
R2 --> R3[Резюме 3]
R1 -. previous_receipt_hash .-> R0
R2 -. previous_receipt_hash .-> R1
R3 -. previous_receipt_hash .-> R2
Кожна квитанція записує хеш попередньої. Щоб тихо видалити квитанцію 2, зловмисник має або:
previous_receipt_hash у квитанції 3 (порушить підпис квитанції 3), АБОЯкщо приватний ключ зберігається у апаратному сховищі, а публічний ключ публікується з кожною квитанцією, жодна з атак неможлива без виявлення.
У зошиті демонструють:
previous_receipt_hash кожної квитанції співпадає з фактичним хешем попередньої квитанції.Так ви отримуєте аудиторський слід, який зовнішній аудитор може перевірити без довіри до вас.
Це найважливіший розділ уроку. Квитанції потужні, але їх потужність обмежена.
Квитанції доводять три речі:
Квитанції НЕ доводять:
policy_id, була справді оцінена, або що вона б дозволила цю дію, якби була перевірена. Квитанція записує, що було заявлено, а не що було застосовано.Ця межа важлива з двох причин:
Поширена помилка — вважати, що «у нас є квитанції» означає «ми керуємося». Ні. Квитанції — це фундамент. Керування — це система, яку ви на нього насипаєте.
Код Python у цьому уроці навмисно мінімальний, щоб ви могли прочитати кожен рядок і точно розуміти, що відбувається. У виробництві у вас є два варіанти:
Будувати безпосередньо на криптографічних примітивах. 50 рядків, які ви бачили вище, достатньо для багатьох випадків. PyNaCl (Ed25519) і пакет jcs (канонічний JSON) — це добре підтримувані і перевірені бібліотеки.
Використовувати виробничу бібліотеку квитанцій. Кілька open-source проектів реалізують той самий патерн із додатковими функціями (ротація ключів, пакетна перевірка, розповсюдження JWK Set, інтеграція з політичними рушіями):
draft-farley-acta-signed-receipts), яка наразі йде процесом стандартизації.protect-mcp (npm) і @veritasacta/verify (npm) надають Node-реалізацію підпису квитанцій і офлайн перевірки, призначені для враппінгу будь-якого MCP-сервера під підробно-виявний аудиторський слід.Вибір між написанням свого та використанням бібліотеки подібний до вибору між написанням власної JWT-бібліотеки або використанням перевіреної: обидва варіанти розумні; бібліотека економить час і звужує аудиторську площу; підхід із нуля змушує розуміти кожен примітив. Цей урок навчає підходу із нуля, щоб дати вам фундамент для будь-якого вибору.
Перевірте свої знання перед переходом до практичного завдання.
1. Квитанція підписана приватним ключем Ed25519 агента. Аудитор має лише публічний ключ. Чи може аудитор перевірити квитанцію офлайн?
2. Зловмисник змінив поле policy_id квитанції, щоб стверджувати, що дія підпорядковується більш ліберальній політиці. Підпис було виконано за оригінальним пакетом. Що станеться під час перевірки?
3. Чому у квитанції є tool_args_hash та result_hash, а не сирі аргументи чи результат?
4. Поле previous_receipt_hash пов’язує кожну квитанцію з попередньою. Якщо зловмисник тихо видалить одну квитанцію посеред ланцюжка, що стане недійсним?
5. Квитанція коректно перевірилася. Чи доводить це, що дія агента була правильною, обґрунтованою і відповідала політиці?
Відкрийте code_samples/18-signed-receipts.ipynb і виконайте усі чотири розділи:
Додаткове завдання 1: розширте схему квитанції власним додатковим полем (наприклад, ідентифікатором запиту для трасування), оновіть логіку канонічного підпису, щоб його включити, і підтвердіть, що квитанція проходить перевірку круговим шляхом. Потім змініть поле після підпису і підтвердіть, що перевірка провалюється. Це змусить вас зрозуміти, як кожен байт канонічного кодування впливає на підпис. Задача підвищеної складності 2: Хешуйте дві ваші квитанції за допомогою SHA-256 разом (конкатенуючи їх канонічні байти в детермінованому порядку) і вбудуйте отриманий дайджест як нове поле у третю квитанцію перед її підписанням. Перевірте, що всі три квитанції все ще можуть пройти цикл перевірки. Ви тільки що побудували доказ включення в один крок: будь-хто, хто має третю квитанцію, може довести, що перші дві існували на момент її підписання, не розкриваючи їх вміст. Це патерн, який використовується у квитанціях селективного розкриття на великому масштабі (Merkle-зобов’язання, RFC 6962).
Криптографічні квитанції надають агентам ШІ аудитний слід, який є:
Вони не є заміною валідації вхідних даних, виконання політик або інфраструктури ідентичності. Вони — основа для цих шарів. Коли ви розгортаєте агентів у регульованих робочих навантаженнях, міжорганізаційних робочих процесах чи будь-якому середовищі, де не можна припускати, що майбутній аудитор вам довірятиме, квитанції – це спосіб зробити аудиторський слід чесним.
Найважливіше: квитанції доводять, хто що сказав і коли. Вони не доводять, що те, що було сказано, є правдивим або правильним. Тримайте це розмежування чітким. Це різниця між чесною системою походження та оманливою.
Коли ви будете готові перейти від цього уроку до розгортання агентів, підписаних квитанціями, у реальному середовищі:
https://your-org.example.com/.well-known/agent-keys.json.Приєднуйтесь до Microsoft Foundry Discord, щоб поспілкуватися з іншими учнями, відвідати години прийому та отримати відповіді на ваші питання про агентів ШІ.
Урок охоплює підписання одиночної квитанції і послідовності з хеш-ланцюгом. Ті ж примітиви формують ще кілька більш просунутих патернів, які ви можете зустріти із розвитком вашої системи управління:
authorization_*) і після виконання (result_*) з незалежними підписами, що корисно, коли рішення про авторизацію та фактичний результат виробляються різними учасниками або у різний час. Це доповнює формат квитанції з цього уроку.result_hash. Реальні вмісти часто багатші за результат одного виклику: логіка перед рішенням (прогноз моделі, опції, докази з їх повнотою, ризик, ланцюг відповідальності, результат охорони) можуть зберігатися у вмісті, закритому однією квитанцією. Це зберігає формат мінімальним, даючи змогу схематам вмісту розвиватися для кожної доменної області.signature.alg може містити ML-DSA-65 (стандарт постквантового підпису NIST), коли потрібно перейти на новий стандарт. Плануйте період переходу з подвійним підписом квитанцій.Побудова агентів для роботи з комп’ютером (CUA)
(Визначатиметься авторами навчальної програми)
Відмова від відповідальності: Цей документ було перекладено за допомогою сервісу штучного інтелекту для перекладу Co-op Translator. Хоча ми прагнемо до точності, будь ласка, майте на увазі, що автоматичні переклади можуть містити помилки або неточності. Оригінальний документ рідною мовою слід вважати авторитетним джерелом. Для критично важливої інформації рекомендується професійний людський переклад. Ми не несемо відповідальності за будь-які непорозуміння або неправильні тлумачення, що виникли внаслідок використання цього перекладу.