Bỏ qua để vào nội dung chính
Bảo mật API key cho MCP server và AI agent: vault + broker

Bảo mật API key cho MCP server và AI agent: vault + broker

Bởi Olivia Reed
01 thg 10, 20267 phút đọc

Báo cáo GitGuardian đếm 24.008 secret trong file cấu hình MCP công khai. Cách giữ API key ngoài tầm model: vault, blind injection và broker.

Agent của bạn đọc một trang web, một ticket hỗ trợ, hoặc một email do người lạ viết. Ngay lúc đó, trong context window vẫn còn GITHUB_TOKEN mà bạn dán vào system prompt "cho nhanh" hôm thứ Sáu.

Bài này chỉ cách giữ credentials ở nơi model không bao giờ chạm tới: mô hình vault cộng proxy, pattern blind injection bảy bước, và một biến thể broker đang chạy thật với PingFederate 13.1.

Con số làm nền cho vấn đề

Bài hướng dẫn gốc dẫn báo cáo GitGuardian State of Secrets Sprawl 2026: 24.008 secret nằm trong các file cấu hình MCP trên GitHub công khai, và 2.117 trong số đó là credential còn hiệu lực. Tác giả nhấn mạnh chúng không lọt ra qua một exploit tinh vi nào cả.

Chúng bị commit thẳng lên repo công khai, trong một hệ sinh thái mà theo bài viết, "popular setup guides often recommend putting keys straight into config files" — các hướng dẫn cài đặt phổ biến vẫn khuyên nhét key thẳng vào file cấu hình.

Vì sao env var và config file không đủ

Lập luận cốt lõi của bài gốc rất gọn: một language model không phân biệt được đâu là dữ liệu, đâu là chỉ thị. Mọi thứ trong context window đều có thể bị model lặp lại, tóm tắt, hoặc gửi đi nơi khác.

Tác giả gọi context window là "a shared surface" — một bề mặt dùng chung chạm vào prompt, tool, log stack và memory store của bạn. Secret rơi vào bất kỳ điểm nào trên bề mặt đó chỉ an toàn bằng đúng thành phần yếu nhất gắn vào nó.

Bốn đường rò mà bài liệt kê, đáng để bạn soi lại hệ thống của mình:

  • Prompt và context: key dán vào system prompt hoặc tool description. Tool response cũng tính — một API echo lại header Authorization, hoặc một error message chứa connection string, là đủ.
  • Log: application log thường ghi full prompt và tool payload để debug. Token đi qua model sẽ nằm trong log store có quyền truy cập rộng hơn và retention dài hơn vault.
  • Trace: công cụ observability ghi lại mọi bước của agent run, gồm cả tool input/output. Trace được share cho đồng nghiệp, vendor, ticket hỗ trợ — một lần lộ thành bản sao vĩnh viễn.
  • Memory: conversation history và vector store giữ lại những gì model đã thấy. Credential vào memory một lần có thể bị replay ở session sau, hoặc hiện ra cho user khác.

Còn environment variable? Bài gốc nói thẳng: env var giải một bài toán khác. Nó giữ key ra khỏi source code, nhưng để key nằm nguyên trong process đang chạy.

Hệ quả theo bài viết: một lệnh printenv từ shell tool hoặc code-execution tool là lộ toàn bộ secret trong process. Env var còn được kế thừa sang child process, xuất hiện trong crash dump và CI log, và không cho bạn tách theo tenant, không có audit trail, không có câu chuyện rotation.

Blind injection: bảy bước giữ token ngoài tầm model

Pattern mà bài đề xuất đặt tên là blind injection. Chữ "blind" mô tả model: nó không thấy secret lúc đi vào, cũng không thấy lúc đi ra.

  1. Agent phát tool call với placeholder. Model gọi hành động và tham chiếu connection bằng tên hoặc placeholder kiểu {{github_token}}. Nó không bao giờ nhận giá trị thật. Với typed tool, bạn thậm chí không cần placeholder.
  2. Runtime chặn lại. Một lớp tin cậy ngoài model nhận call trước khi bất cứ gì rời hạ tầng. Model không có đường bỏ qua lớp này.
  3. Kiểm tra policy. Tool này có được phép với tenant này không? Endpoint là read, write hay destructive? Có cần người duyệt không? Code xác định trả lời, không phải model.
  4. Lấy và giải mã credential. Runtime tra credential đã mã hoá của tenant đó và decrypt trong memory, bằng một key model không với tới.
  5. Tiêm credential. Token được gắn vào header Authorization hoặc request body ở thời điểm muộn nhất có thể.
  6. Thực thi request. Runtime gọi provider API. Theo bài gốc, token tồn tại trong memory đúng một request rồi bị huỷ.
  7. Làm sạch response. Trước khi trả về model, strip auth header, token bị echo, và error text chứa credential. Redact log và trace y hệt.

Bài gốc kèm một đoạn pseudocode và tự ghi rõ đó là "illustrative pseudocode, not a specific library" — minh hoạ, không phải thư viện cụ thể. Đọc nó như sơ đồ luồng, đừng copy vào production.

Biến thể broker: token nhà cung cấp ở lại backend

Dự án darkedges/pingfederate-graph-broker là một hiện thực cụ thể để đối chiếu: một directory broker đứng trước Microsoft Graph.

Bài viết đặt vấn đề bằng hai lựa chọn tồi quen thuộc. Đưa agent một Microsoft token: agent cùng log, prompt và tool output của nó có thể làm lộ một credential dùng được với Graph. Cho agent quyền app-wide: nó đọc được cả directory, không chỉ phần user đồng ý chia sẻ.

Đường thứ ba: Entra token ở lại backend, agent chỉ nhận dữ liệu directory. Thiết kế tách làm hai hệ thống phân quyền riêng biệt — Microsoft delegated consent cho phép broker gọi Graph với danh nghĩa user, còn broker delegation cho phép đúng agent client đó dùng connection này cho các operation này đến thời điểm này.

Phần đáng học nhất là độ hẹp của bề mặt. Chỉ ba read operation tồn tại: directory.find_users, directory.find_groups, directory.list_group_members. Và chỉ hai Graph delegated scope được chấp nhận là User.ReadBasic.All và GroupMember.Read.All (cộng OIDC scope và User.Read). Rộng hơn là bị từ chối.

Agent phải trình đồng thời một PF token và một delegation ID — token client-credentials đứng một mình không chọn hay cấp quyền cho connection của ai cả.

Một cảnh báo tác giả tự đặt ra, nên tôn trọng: đây là "a runnable Go starter for PingFederate 13.1" và là "a single-instance starter, not a production-ready identity platform". Dùng làm tham chiếu kiến trúc, đừng bê nguyên vào hệ thống thật.

Áp vào hệ thống của bạn tuần này

Phần dưới là phân tích của chúng tôi dựa trên hai nguồn trên, không phải khuyến nghị của tác giả gốc. Bắt đầu bằng việc kiểm kê, không phải viết code. Grep repo và file cấu hình MCP tìm key dạng thô. Mở một trace gần nhất trong observability tool và đọc xem tool input có chứa header auth không. Hai việc này thường đủ tìm ra lỗ đầu tiên.

Sau đó chọn ranh giới. Nếu bạn gọi nhiều API bên thứ ba, lớp runtime chặn-và-tiêm là hướng gọn hơn. Nếu bạn chỉ cần đọc danh bạ hoặc một nguồn dữ liệu duy nhất, broker với vài operation cố định cho bề mặt hẹp hơn nhiều, và dễ review hơn.

Việc dễ bỏ qua nhất là bước bảy: sanitize response. Một runtime tiêm token hoàn hảo vẫn vô nghĩa nếu API trả về error message có chứa connection string và bạn log nguyên văn.

Cần theo dõi tiếp

Hai thứ đáng theo dõi trong vài tháng tới: liệu các MCP server phổ biến có chuyển mặc định sang vault-plus-proxy thay vì key inline, và liệu pattern broker có xuất hiện ở các nhà cung cấp identity ngoài PingFederate hay không.

Không spam, hủy đăng ký bất kỳ lúc nào.

Bài viết liên quan