Xem video bài học: Bảo mật Đại lý AI với Biên nhận Mật mã
(Video bài học và hình thu nhỏ sẽ được nhóm nội dung Microsoft thêm vào sau khi hợp nhất, theo mẫu bài học 14 / 15.)
Bài học này sẽ đề cập đến:
Sau khi hoàn thành bài học này, bạn sẽ biết cách:
Hãy tưởng tượng bạn đã triển khai một đại lý AI cho Contoso Travel. Đại lý đọc yêu cầu của khách hàng, gọi API vé bay để tìm lựa chọn, và đặt chỗ thay mặt khách hàng. Quý trước, đại lý đã xử lý 50.000 đặt chỗ.
Hôm nay có một kiểm toán viên đến. Họ đưa ra câu hỏi đơn giản: “Cho tôi xem đại lý của bạn đã làm gì.”
Bạn giao nhật ký cho họ xem. Người kiểm toán nhìn vào và đặt câu hỏi khó hơn: “Làm sao tôi biết những nhật ký này không bị chỉnh sửa?”
Đây là vấn đề dấu vết kiểm toán. Hầu hết các triển khai đại lý hiện nay dựa vào:
Không cái nào trong số này có thể trả lời câu hỏi của người kiểm toán mà không yêu cầu họ phải tin tưởng ai đó (bạn, nhà cung cấp đám mây, nhà cung cấp cơ sở dữ liệu). Đối với sử dụng nội bộ, sự tin tưởng đó thường chấp nhận được. Đối với khối lượng công việc được quản lý (tài chính, y tế, bất kỳ gì chịu sự điều chỉnh của Đạo luật AI EU), thì không thể.
Biên nhận mật mã giải quyết vấn đề này bằng cách làm cho mỗi hành động của đại lý có thể xác thực độc lập. Người kiểm toán không cần tin bạn. Họ chỉ cần khóa công khai của bạn và bản thân biên nhận.
Biên nhận là một đối tượng JSON ghi lại những gì đại lý đã làm, được ký bằng chữ ký số.
flowchart LR
A[Đại lý gọi công cụ] --> B[Xây dựng tải hóa đơn]
B --> C[Chuẩn hóa JSON RFC 8785]
C --> D[Băm SHA-256]
D --> E[Ký Ed25519]
E --> F[Hóa đơn kèm chữ ký]
F --> G[Kiểm toán viên xác minh ngoại tuyến]
G --> H{Chữ ký hợp lệ?}
H -- yes --> I[Bằng chứng chống giả mạo]
H -- no --> J[Hóa đơn bị từ chối]
Biên nhận tối giản trông như thế này:
{
"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..."
}
}
Ba thuộc tính sau đây thực hiện công việc:
Chữ ký. Biên nhận được ký bởi cổng đại lý sử dụng khóa riêng Ed25519. Bất kỳ ai có khóa công khai tương ứng đều có thể xác thực chữ ký ngoại tuyến. Sửa đổi bất kỳ trường nào làm chữ ký không hợp lệ.
Mã hóa chuẩn hóa. Trước khi ký, biên nhận được tuần tự hóa dùng JSON Canonicalization Scheme (JCS, RFC 8785). Điều này đảm bảo hai cài đặt khác nhau mà tạo ra cùng biên nhận bên logic sẽ cho ra kết quả byte-identical. Nếu không chuẩn hóa, các trình tuần tự hóa JSON khác nhau sẽ tạo ra chữ ký khác nhau cho cùng một nội dung.
Liên kết chuỗi băm. Trường previous_receipt_hash liên kết mỗi biên nhận với biên nhận trước nó. Bỏ hoặc sắp xếp lại một biên nhận làm gãy mọi biên nhận đến sau nó. Việc giả mạo trở nên hiển nhiên ở cấp chuỗi ngay cả khi chữ ký cá nhân bị bỏ qua.
Ba thuộc tính này cung cấp ba đảm bảo:
Bạn không cần thư viện đặc biệt để tạo biên nhận. Các nguyên thủy mật mã có sẵn rộng rãi và logic chỉ vài chục dòng Python.
Các bài tập thực hành trong code_samples/18-signed-receipts.ipynb hướng dẫn toàn bộ quy trình. Phiên bản tóm tắt:
import json
import hashlib
import base64
from nacl import signing
from jcs import canonicalize # JSON chuẩn RFC 8785
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()}"
# Tạo hoặc tải khóa ký (trong sản xuất, lưu trong khoá bảo mật)
signing_key = signing.SigningKey.generate()
verify_key = signing_key.verify_key
# Xây dựng dữ liệu biên nhận (chưa có chữ ký)
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,
}
# Chuẩn hóa, băm, ký.
canonical_bytes = canonicalize(payload)
message_hash = hashlib.sha256(canonical_bytes).digest()
signature_bytes = signing_key.sign(message_hash).signature
# Đính kèm đối tượng chữ ký có cấu trúc.
receipt = {
**payload,
"signature": {
"alg": "EdDSA",
"sig": b64url_nopad(signature_bytes),
"public_key": b64url_nopad(bytes(verify_key)),
},
}
Đó là toàn bộ quy trình ký. Các bài tập trong notebook sẽ hướng dẫn từng bước cụ thể.
Việc xác thực là thao tác nghịch đảo:
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:
# Chữ ký là một đối tượng có cấu trúc: {"alg", "sig", "public_key"}.
sig_obj = receipt.get("signature")
if not sig_obj or sig_obj.get("alg") != "EdDSA":
return False
# Tái tạo payload thực sự đã được ký (mọi thứ ngoại trừ chữ ký).
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
Hàm này nhận một biên nhận và trả về True nếu chữ ký hợp lệ, False nếu không. Không gọi mạng, không phụ thuộc dịch vụ, không cần tin bên thứ ba.
Để thấy tính năng phát hiện giả mạo hoạt động, notebook hướng dẫn:
tool_args_hash.Đây là minh chứng thực tế rằng biên nhận có tính không thể giả mạo: bất kỳ thay đổi nào dù nhỏ cũng làm gãy chữ ký.
Một biên nhận đã ký bảo vệ một hành động. Một chuỗi biên nhận bảo vệ một chuỗi hành động.
flowchart LR
R0[Biên nhận 0<br/>khởi tạo] --> R1[Biên nhận 1]
R1 --> R2[Biên nhận 2]
R2 --> R3[Biên nhận 3]
R1 -. previous_receipt_hash .-> R0
R2 -. previous_receipt_hash .-> R1
R3 -. previous_receipt_hash .-> R2
Mỗi biên nhận ghi lại băm biên nhận trước đó. Để bí mật loại bỏ biên nhận 2, kẻ tấn công phải:
previous_receipt_hash của biên nhận 3 (làm gãy chữ ký biên nhận 3), HOẶCNếu khóa riêng được lưu trong kho khóa phần cứng và bạn công bố khóa công khai cùng từng biên nhận, không có cách nào tấn công mà không bị phát hiện.
Notebook hướng dẫn:
previous_receipt_hash của mỗi biên nhận khớp với băm thực tế của biên nhận trước đó.Đây là cách bạn tạo dấu vết kiểm toán mà kiểm toán viên bên ngoài có thể xác thực mà không cần tin bạn.
Đây là phần quan trọng nhất của bài học. Biên nhận rất mạnh mẽ nhưng sức mạnh của chúng có giới hạn.
Biên nhận chứng minh ba điều:
Biên nhận KHÔNG chứng minh:
policy_id đã được đánh giá thực sự hay không, hoặc liệu chính sách đó có cho phép hành động này nếu được kiểm tra. Biên nhận ghi lại điều được tuyên bố, không phải điều được thực thi.Ranh giới này quan trọng vì hai lý do:
Một lỗi phổ biến là cho rằng “chúng ta có biên nhận” nghĩa là “chúng ta có quản trị.” Không phải vậy. Biên nhận là nền tảng. Quản trị là hệ thống bạn xây dựng trên nền tảng đó.
Mục 3 ở trên xứng đáng có một phần riêng: biên nhận hành động nói “khóa này đã ký nội dung này,” không bao giờ nói “một người đã phê duyệt.” Với các hành động rủi ro cao (hoàn tiền, xóa, chuyển khoản), các khuôn khổ quản trị ngày càng yêu cầu chính xác câu thiếu này, và điều đó có thể thực hiện bằng cùng các nguyên thủy bạn đã xây ở bài học này.
Notebook tiếp theo code_samples/human-authorization-receipts.ipynb thêm loại biên nhận thứ hai, human.approval.v1, cùng hình thức đóng gói như biên nhận bài học (payload có kiểu ký Ed25519 trên SHA-256 chuẩn hóa của nó, với đối tượng signature nằm ngoài byte ký). Người phê duyệt có tên ký toàn bộ hành động chuẩn hóa và digest của nó trước khi thực thi; biên nhận hành động đại lý chứa digest hành động đó và parent_approval_ref, là receipt_hash của sự phê duyệt, theo cùng quy ước như previous_receipt_hash trong chuỗi bạn tạo ở trên. Một lệnh verify_chain kiểm tra cả hai hiện vật dưới các đăng ký khóa riêng biệt được ghim (khóa của người phê duyệt và khóa của đại lý), nên mã dùng chung nhưng quyền kiểm soát chưa bao giờ dùng chung.
Thuộc tính đạt được, nói cẩn thận: con người đã phê duyệt chính xác hành động này, và đại lý thực thi chính xác hành động được phê duyệt đó. Các bộ kiểm tra từ chối trong notebook thực sự biến thuộc tính này thành hiện thực thay vì chỉ khẳng định:
Mỗi lỗi từ chối với lý do riêng biệt, nên kiểm toán viên đọc từ chối có thể biết liệu quyền hạn đã lỗi thời hay hành động thực thi đã thay đổi. Quy tắc được dạy trong notebook: một phê duyệt đã ký không phải quyền hạn tự thân. Quyền hạn chỉ tồn tại nếu cả hai biên nhận vẫn gắn với cùng hành động chuẩn hóa khi thực thi. Đường dẫn đồng ký trong cùng bản Internet-Draft mà bài học này theo (draft-farley-acta-signed-receipts) là hình thức chuẩn hóa tiêu chuẩn của mẫu này.
Mã Python trong bài học này có chủ đích tối giản để bạn có thể đọc từng dòng và hiểu chính xác những gì xảy ra. Trong sản xuất, bạn có hai lựa chọn:
Xây dựng trực tiếp trên các nguyên thủy mật mã. 50 dòng bạn thấy ở trên đủ cho nhiều trường hợp sử dụng. PyNaCl (Ed25519) và gói jcs (JSON chuẩn hóa) là thư viện được duy trì tốt và đã được kiểm tra.
Dùng thư viện biên nhận sản xuất. Một số dự án mã nguồn mở thực thi mẫu tương tự với tính năng bổ sung (xoay khóa, xác nhận hàng loạt, phân phối JWK Set, tích hợp với bộ máy chính sách):
draft-farley-acta-signed-receipts, sửa đổi 02) hiện đang trong quá trình chuẩn hoá, với bộ test tuân thủ dùng chung (agent-governance-testvectors) để các cài đặt độc lập xác minh chéo cho đầu ra chuẩn hóa byte-identical.protect-mcp (npm) và @veritasacta/verify (npm) cung cấp triển khai Node cho ký biên nhận và xác thực ngoại tuyến, dùng để bọc bất kỳ máy chủ MCP nào với dấu vết kiểm toán chống giả mạo, bao gồm luồng giữ cho đồng ký trong đó hành động bị tạm dừng phát ra biên nhận phê duyệt liên kết với digest hành động (hỗ trợ WebAuthn cho luồng trên máy tính để bàn), cùng mẫu biên nhận phê duyệt như notebook phê duyệt con người ở trên.pip install nobulex) cung cấp mẫu ký Ed25519 + JCS tương tự với tích hợp LangChain và CrewAI, bao gồm các vectors test xác nhận chéo đã phát hành và bản đồ tuân thủ đóng góp qua OWASP PR #2210.Quyết định giữa tự phát triển và dùng thư viện tương tự như quyết định giữa việc viết thư viện JWT riêng và dùng thư viện đã được kiểm thử: cả hai đều hợp lý; thư viện tiết kiệm thời gian và giảm bề mặt kiểm toán; cách tự viết buộc bạn hiểu rõ từng nguyên thủy. Bài học này dạy cách từ đầu để bạn có nền tảng cho cả hai lựa chọn.
Kiểm tra hiểu biết trước khi chuyển sang bài tập thực hành.
1. Một biên nhận được ký bằng khóa riêng Ed25519 của đại lý. Kiểm toán viên chỉ có khóa công khai. Kiểm toán viên có thể xác thực biên nhận ngoại tuyến không?
2. Kẻ tấn công sửa trường policy_id của biên nhận để tuyên bố nó được điều chỉnh bởi chính sách rộng hơn. Chữ ký ở trên payload gốc. Điều gì xảy ra trong quá trình xác thực?
3. Tại sao biên lai lại bao gồm tool_args_hash và result_hash thay vì các tham số thô và kết quả?
4. Trường previous_receipt_hash liên kết mỗi biên lai với biên lai tiền nhiệm của nó. Nếu kẻ tấn công lặng lẽ xóa một biên lai ở giữa chuỗi, điều gì sẽ trở nên không hợp lệ?
5. Một biên lai xác thực chính xác. Điều đó chứng minh hành động của tác nhân là đúng, hợp lý hoặc tuân thủ chính sách chứ?
Mở code_samples/18-signed-receipts.ipynb và hoàn thành cả bốn phần:
Thách thức mở rộng 1: mở rộng schema biên lai với một trường bổ sung do bạn tự chọn (ví dụ, ID yêu cầu để truy vết), cập nhật logic ký chính tắc để bao gồm nó, và xác nhận rằng biên lai vẫn có thể xác thực hai chiều. Sau đó sửa đổi trường sau khi ký và xác nhận xác thực thất bại. Điều này buộc bạn phải hiểu cách từng byte của mã hóa chính tắc đóng góp vào chữ ký.
Thách thức mở rộng 2: Băm SHA-256 hai biên lai của bạn cùng nhau (nối các byte chính tắc của chúng theo thứ tự xác định) và nhúng kết quả băm làm trường mới trên biên lai thứ ba trước khi ký nó. Xác minh rằng cả ba biên lai vẫn có thể xác thực hai chiều. Bạn vừa xây dựng bằng chứng bao gồm một bước: bất kỳ ai giữ biên lai thứ ba có thể chứng minh hai biên lai đầu tiên tồn tại tại thời điểm nó được ký, mà không cần tiết lộ nội dung của chúng. Đây là mẫu mà biên lai tiết lộ chọn lọc sử dụng ở quy mô lớn (cam kết Merkle, RFC 6962).
Biên lai mã hóa cung cấp cho các tác nhân AI một dấu vết kiểm toán:
Chúng không thay thế cho việc xác thực đầu vào, thực thi chính sách hoặc hạ tầng danh tính. Chúng là nền tảng cho những lớp đó. Khi bạn triển khai tác nhân vào các khối công việc quy định, luồng công việc đa tổ chức, hoặc bất kỳ môi trường nào mà một kiểm toán viên tương lai không thể tin cậy bạn, biên lai là cách bạn làm cho dấu vết kiểm toán trung thực.
Điều cần nhớ quan trọng nhất: biên lai chứng minh ai nói gì, khi nào. Chúng không chứng minh điều được nói là đúng hoặc chính xác. Hãy giữ phân biệt đó thật chặt. Đó là khác biệt giữa hệ thống nguồn gốc trung thực và hệ thống gây hiểu nhầm.
Khi bạn sẵn sàng tốt nghiệp từ bài học này để triển khai tác nhân ký biên lai trong môi trường thực:
https://your-org.example.com/.well-known/agent-keys.json.Tham gia Microsoft Foundry Discord để gặp gỡ người học khác, tham dự giờ làm việc, và được trả lời câu hỏi về AI Agents.
Bài học này bao gồm ký một biên lai đơn và chuỗi liên kết theo hàm băm. Các nguyên mẫu giống nhau kết hợp thành nhiều mẫu nâng cao hơn bạn có thể gặp khi tư thế quản trị của bạn phát triển:
authorization_*) và sau khi thực thi (result_*) với chữ ký độc lập, hữu ích khi quyết định ủy quyền và kết quả quan sát do các thực thể khác nhau hoặc thời điểm khác nhau tạo ra. Điều này bổ sung dựa trên định dạng biên lai trong bài học này.result_hash. Payload thực tế thường phong phú hơn một kết quả gọi công cụ đơn lẻ: lý luận trước quyết định (dự đoán mô hình, lựa chọn xem xét, bằng chứng và độ đầy đủ của nó, tư thế rủi ro, chuỗi trách nhiệm, kết quả kiểm soát) có thể tồn tại bên trong payload, được niêm phong bởi một biên lai duy nhất. Điều này giữ định dạng biên lai tối giản trong khi cho phép schema payload tiến hóa theo từng miền.signature.alg có thể mang ML-DSA-65 (chuẩn chữ ký hậu lượng tử của NIST) khi bạn cần nâng cấp. Lập kế hoạch giai đoạn chuyển tiếp nơi biên lai được ký kép.Tuyên bố miễn trừ trách nhiệm: Tài liệu này đã được dịch bằng dịch vụ dịch thuật AI Co-op Translator. Mặc dù chúng tôi cố gắng đảm bảo độ chính xác, xin lưu ý rằng bản dịch tự động có thể chứa lỗi hoặc sai sót. Tài liệu gốc bằng ngôn ngữ gốc nên được coi là nguồn tin chính thức. Đối với thông tin quan trọng, nên sử dụng dịch vụ dịch thuật chuyên nghiệp bởi con người. Chúng tôi không chịu trách nhiệm về bất kỳ hiểu lầm hoặc giải thích sai nào phát sinh từ việc sử dụng bản dịch này.