Bỏ qua nội dung chính
HƯỚNG DẪN

Cách bảo vệ các hành động nhạy cảm mà không chặn các agent hợp lệ

Một mô hình thực tế để giữ an toàn cho thay đổi tài khoản và các thao tác có tác động lớn, đồng thời vẫn cho phép các client tự động hợp lệ làm việc hữu ích trong app của bạn.

Toshendra Sharma

Bởi Toshendra SharmaNhà sáng lập và CEO, AgentCredia · Updated November 13, 2026

Nâng cao · 10 phút đọc

Vì sao không chỉ chặn agent khỏi các hành động nhạy cảm?

Một số hành động trong ứng dụng của bạn quan trọng hơn những hành động khác. Đổi mật khẩu, thêm thành viên nhóm, xuất dữ liệu, xóa dự án, phê duyệt đơn hàng: nếu bất kỳ việc nào trong số này xảy ra mà không có đúng thẩm quyền, thiệt hại là có thật.

Đồng thời, ngày càng nhiều công việc hợp lệ trong app của bạn được thực hiện bởi tự động hóa. Một người dùng nhờ trợ lý cập nhật cài đặt. Một tích hợp đối tác tạo bản ghi theo lịch. Một agent nội bộ tạo phiếu hỗ trợ. Chặn mọi client tự động khỏi các route nhạy cảm sẽ làm hỏng công việc đó và đẩy người dùng quay lại thao tác thủ công hoặc các cách обход обход quanh khó bảo vệ hơn.

Câu trả lời không phải là "chặn agent" hay "tin agent". Đó là một mô hình trong đó mức độ rủi ro của hành động, thẩm quyền đứng sau yêu cầu và bằng chứng về actor đều giữ vai trò riêng biệt. Hướng dẫn này xây dựng mô hình đó. Để xem tư duy rộng hơn đằng sau nó, hãy xem thiết kế chính sách ở cấp ứng dụng cho con người và agent AI.

Bước 1: Phân tầng route theo mức độ rủi ro

Hãy bắt đầu bằng cách phân loại hành động, không phải client. Một sơ đồ ba tầng đơn giản phù hợp với hầu hết ứng dụng:

Tầng Ví dụ Yêu cầu mặc định
Thấp Đọc dữ liệu công khai, tìm kiếm, xem dashboard của chính mình Xác thực bình thường, nếu có
Trung bình Tạo hoặc chỉnh sửa nội dung của chính mình, mời người xem Đã xác thực, được phép đối với tài nguyên
Cao Đổi thông tin xác thực, đổi quyền sở hữu, xuất hàng loạt, xóa, phê duyệt Đã được phép cộng với kiểm tra bước lên hoặc rà soát

Ghi tầng vào cấu hình route để nó hiện rõ trong quá trình review code:

TS
type Tier = "low" | "medium" | "high";

export const routeTier: Record<string, Tier> = {
  "GET /projects": "low",
  "POST /projects": "medium",
  "POST /projects/:id/members": "medium",
  "POST /account/password": "high",
  "POST /exports": "high",
  "DELETE /projects/:id": "high",
};

Các tầng thay đổi chậm và dễ suy luận. Mọi thứ khác trong hướng dẫn này đều dựa trên chúng.

Bước 2: Danh tính, thông tin xác thực và phân quyền khác nhau như thế nào?

Ba câu hỏi thường bị trộn lẫn với nhau:

  1. Ai hoặc cái gì đang hành động? Đây là bằng chứng về actor: một người tương tác trực tiếp, một người có sự hỗ trợ của AI, một agent, hoặc Unknown, với cơ sở như Verified hoặc Declared.
  2. Thông tin xác thực nào đã được trình bày? Một session cookie, một API key, một OAuth access token.
  3. Thông tin xác thực này có được phép thực hiện hành động đó trên tài nguyên này không? Đây là phân quyền.

Chữ ký agent đã xác minh (RFC 9421) trả lời tốt câu hỏi đầu tiên. Nó không nói gì về câu hỏi thứ ba, một điểm được trình bày chi tiết trong những gì yêu cầu đã ký của agent AI chứng minh, và những gì chúng không chứng minh. Một OAuth token có đúng scope trả lời câu hỏi thứ ba. Nó nói rất ít về câu hỏi đầu tiên. Quyết định của bạn cho một hành động tầng cao luôn cần một câu trả lời tốt cho câu hỏi thứ ba, và dùng câu trả lời ở câu hỏi đầu tiên để chọn mức độ bảo đảm bổ sung cần yêu cầu.

TS
interface Context {
  actor: { actor: "human" | "human_ai" | "agent" | "unknown"; basis: "verified" | "declared" | "inferred" | "unknown" };
  credential: { type: "session" | "oauth" | "api_key"; scopes: string[]; subject: string };
}

function authorized(ctx: Context, action: string, resourceOwner: string): boolean {
  return ctx.credential.subject === resourceOwner && ctx.credential.scopes.includes(action);
}

Phân quyền không bao giờ được nới lỏng chỉ vì kết quả actor thuận lợi. Ngữ cảnh actor chỉ thêm yêu cầu hoặc chọn giữa các đường đi đều an toàn như nhau.

Bước 3: Dùng thẩm quyền được ủy quyền cho agent hành động thay cho con người

Khi một agent hành động thay mặt người dùng, hãy cấp cho nó thông tin xác thực riêng với thẩm quyền giới hạn thay vì để nó dùng lại session của người dùng. OAuth 2.0 là công cụ chuẩn: người dùng phê duyệt các scope cụ thể cho một client cụ thể, và token ghi lại client đó. RFC 9700, OAuth 2.0 Security Best Current Practice, mô tả cách triển khai an toàn. MCP server đi theo cùng một mô hình: MCP authorization specification xem một MCP server được bảo vệ như một resource server OAuth 2.1. Bài viết ai đang gọi MCP server của bạn? áp dụng điều này cho các lời gọi tool.

Điều đó tạo ra chính xác những gì bạn cần cho các hành động nhạy cảm:

  • Token cho bạn biết client nào đang hành động và thay mặt ai.
  • Scope giới hạn những gì nó có thể làm. Một scheduling agent với calendar:write không thể đổi mật khẩu.
  • Người dùng có thể thu hồi quyền cấp mà không cần đổi thông tin xác thực của chính họ, và OAuth 2.0 Token Revocation (RFC 7009) định nghĩa cách một client thông báo cho authorization server rằng một token không còn hợp lệ.
  • Audit log cho thấy agent, không phải người dùng, đã thực hiện hành động.

Nếu app của bạn chưa hỗ trợ thông tin xác thực được ủy quyền, hãy ưu tiên nó cho các hành động mà agent thường cần nhất. Đây là kiểm soát hiệu quả nhất cho truy cập tự động.

Bước 4: Khi nào nên yêu cầu kiểm tra bước lên?

Step-up nghĩa là yêu cầu thêm bảo đảm vào thời điểm diễn ra một hành động nhạy cảm. Với API được bảo vệ bằng OAuth, RFC 9470 định nghĩa cách resource server cho client biết rằng authentication hiện tại chưa đủ mạnh hoặc chưa đủ mới. Kiểm tra bước lên phù hợp phụ thuộc vào ai đang hành động:

  • Người tương tác trực tiếp (Human, Human + AI): xác thực lại (xem các yêu cầu reauthentication trong NIST SP 800-63B), một lời nhắc passkey dựa trên WebAuthn, hoặc mã dùng một lần.
  • Agent có thông tin xác thực được ủy quyền: một scope dành riêng cho hành động tầng cao, được người dùng cấp rõ ràng, hoặc một xác nhận gửi cho người dùng qua kênh ngoài.
  • Unknown: kiểm tra mạnh hiện có của route, giữ nguyên.
TS
type Requirement = "none" | "reauth" | "explicit_scope" | "user_confirmation" | "review";

function stepUp(tier: Tier, ctx: Context): Requirement {
  if (tier !== "high") return "none";
  const { actor } = ctx.actor;
  if (actor === "human" || actor === "human_ai") return "reauth";
  if (actor === "agent" && ctx.credential.type === "oauth") return "explicit_scope";
  if (actor === "agent") return "user_confirmation";
  return "reauth";
}

Lưu ý rằng kết quả Human không bỏ qua step-up. Nó chọn kiểu step-up phù hợp với một người tương tác trực tiếp. Các suy diễn có thể sai, vì vậy chúng nên thay đổi hình thức của kiểm tra, chứ không xóa bỏ kiểm tra.

Bước 5: Chuyển sang rà soát của con người khi cần phán đoán

Một số hành động hiếm, có tác động lớn và khó tự động hóa an toàn: xuất hàng loạt dữ liệu khách hàng, khôi phục tài khoản sau khi mất thông tin xác thực, hoặc xóa một tổ chức. Với các trường hợp này, một hàng đợi rà soát thường tốt hơn bất kỳ quy tắc tự động nào.

Ngữ cảnh actor giúp việc rà soát nhanh hơn và công bằng hơn bằng cách cung cấp cho người rà soát bằng chứng: class, basis, thông tin xác thực đã dùng, lịch sử của các yêu cầu tương tự. Người rà soát quyết định. Phân loại chỉ cung cấp thông tin.

TS
function shouldReview(route: string, ctx: Context): boolean {
  const reviewRoutes = new Set(["POST /exports", "POST /account/recover"]);
  if (!reviewRoutes.has(route)) return false;
  return !(ctx.credential.type === "oauth" && ctx.credential.scopes.includes("export:bulk"));
}

Bước 6: Chọn hành vi khi thất bại theo từng tầng

Đánh giá actor có thể hết thời gian hoặc thiếu bằng chứng. Hãy quyết định trước điều gì sẽ xảy ra.

  • Tầng thấp: fail open. Yêu cầu tiếp tục bình thường.
  • Tầng trung bình: fail open, với phân quyền bình thường đã có.
  • Tầng cao: fail closed về kiểm tra mạnh hiện có. Thiếu bằng chứng actor không bao giờ làm yếu bảo vệ, và cũng không bao giờ chặn một yêu cầu vốn đã có thể đi qua các kiểm tra bình thường của bạn.

Thuộc tính then chốt là tính đối xứng. Một lỗi trong đánh giá actor nên khiến hành động an toàn y hệt, và khả dụng y hệt, như trước khi bạn thêm ngữ cảnh actor.

Bước 7: Đừng đưa ra quyết định có hậu quả về con người chỉ từ suy diễn

Một phân loại suy diễn là bằng chứng về một tương tác, không phải là sự thật về một con người. Đừng từ chối đơn xin việc, quyết định tín dụng, kết quả thi, nhà ở hoặc quyền truy cập vào một dịch vụ thiết yếu chỉ vì một session trông có vẻ tự động hoặc được hỗ trợ bởi AI. Trong các luồng đó, hãy dùng ngữ cảnh actor để chọn quy trình, chẳng hạn như rà soát của con người hoặc một đường xác minh thay thế, và đảm bảo người đó có thể hoàn tất nó.

Đây vừa là nguyên tắc công bằng vừa là nguyên tắc thực tiễn. Các suy diễn có dương tính giả, và cái giá của dương tính giả trong các luồng này rơi lên người không làm gì sai.

Bước 8: Triển khai với quan sát trước

Trước khi bất kỳ điều gì trong số này ảnh hưởng đến yêu cầu thực, hãy chạy nó ở chế độ observation và preview:

  1. Ghi lại tầng, actor class, basis, loại thông tin xác thực và bước lên mà chính sách của bạn sẽ yêu cầu.
  2. Xem mẫu các yêu cầu tầng cao theo class. Các agent hợp lệ có đang dùng thông tin xác thực được ủy quyền hay đang tái sử dụng session của người dùng?
  3. Sửa các khoảng trống bạn tìm thấy, thường là bằng cách cung cấp thông tin xác thực có scope, trước khi bạn enforce.
  4. Enforce trên một phần nhỏ của một route duy nhất, đo tỷ lệ hoàn thành, rồi mở rộng.

Bước 9: Giữ một audit trail trả lời được câu hỏi "ai và thay mặt ai"

Với mọi hành động tầng cao, hãy ghi lại đủ để có thể tái dựng điều gì đã xảy ra sau này:

  • actor class và basis tại thời điểm yêu cầu,
  • loại thông tin xác thực, client mà nó được cấp cho và subject mà nó đại diện (OAuth 2.0 Token Exchange, RFC 8693, định nghĩa một claim act để ghi lại chính xác kiểu quan hệ "hành động thay mặt" này),
  • các scope đã cho phép hành động,
  • yêu cầu step-up hoặc rà soát đã áp dụng và kết quả của nó.
TS
audit.write({
  action: "project.export",
  actor: ctx.actor,
  client: ctx.credential.type === "oauth" ? clientIdFromToken(req) : undefined,
  subject: ctx.credential.subject,
  scopes: ctx.credential.scopes,
  requirement,
  outcome: "approved",
  at: new Date().toISOString(),
});

Với dấu vết này, một câu hỏi như "một người hay một trợ lý đã xóa dự án này, và ai đã phê duyệt nó" sẽ có câu trả lời rõ ràng. Điều đó giúp xử lý sự cố nhanh hơn và giúp người dùng tin rằng sự hỗ trợ tự động trong app của bạn là có trách nhiệm giải trình.

Một ví dụ thực tế

Một app quản lý dự án thấy một trợ lý cập nhật ngày đến hạn của tác vụ cho người dùng, và thỉnh thoảng cố gắng xuất toàn bộ dự án.

  • Việc cập nhật tác vụ thuộc tầng trung bình. Trợ lý dùng một OAuth token với tasks:write. Đã được phép, không step-up. Được phép.
  • Việc xuất một dự án thuộc tầng cao. Token của trợ lý không có export:bulk. App gửi cho người dùng một yêu cầu xác nhận. Người dùng phê duyệt, và app cấp scope cho client đó.
  • Một client Unknown cố thực hiện cùng việc xuất đó bằng một session cookie đã được tái sử dụng. Lời nhắc re-authentication hiện có được áp dụng, đúng như trước đây.

Không ai bị chặn chỉ vì là agent. Mọi hành động có tác động lớn đều mang theo thẩm quyền rõ ràng.

Proof of Actor phù hợp ở đâu

Proof of Actor cung cấp phía actor của mô hình này: ai hoặc cái gì đang hành động và dựa trên cơ sở nào. Ứng dụng của bạn vẫn giữ phần xác thực, phân quyền và quyết định cuối cùng. Detect actor, Understand cách từng class sử dụng các route nhạy cảm của bạn, và Route từng yêu cầu đến mức bảo đảm phù hợp.

Để thiết kế điều này cho ứng dụng của bạn cùng với nhóm của chúng tôi, hãy đăng ký quyền truy cập sớm. Với các kịch bản SaaS chi tiết hơn, xem SaaS Apps & Websites.

Nguồn

Xây dựng cùng chúng tôi.

Hãy tham gia chương trình nhà phát triển sáng lập của Proof of Actor.

Bạn muốn trao đổi trước? Trao đổi với những người sáng lập