Bảo mật các AI Agent với các công cụ Laravel MCP

Việc cấp quyền truy cập ứng dụng của bạn cho một AI agent thông qua MCP giống như trao cho ai đó một chùm chìa khóa. Nếu bạn không thiết lập các quy tắc, họ có thể mở nhầm những cánh cửa không nên mở.

Gần đây, tôi đã xây dựng các công cụ MCP cho một ứng dụng Laravel multi-tenant. Tôi có một mục tiêu duy nhất: cho phép agent điều khiển ứng dụng mà không để nó nhìn thấy dữ liệu của người khác.

Vấn đề với các công cụ MCP

Mỗi công cụ MCP là một endpoint. Một agent gọi một công cụ, và máy chủ của bạn sẽ thực thi mã. Trong một ứng dụng multi-tenant, mỗi công cụ phải trả lời được hai câu hỏi:

  • Bạn có được phép làm việc này không?
  • Bạn có được phép thực hiện nó trong tổ chức cụ thể này không?

Nếu bạn bỏ lỡ một trong hai, bạn sẽ làm rò rỉ dữ liệu.

Tại sao mô hình Multi-Tenancy tiêu chuẩn lại thất bại ở đây

Trong một ứng dụng web thông thường, bạn có các session. Bạn sử dụng global scopes để lọc dữ liệu theo organization ID. Nó hoạt động vì "tổ chức hiện tại" luôn nằm trong session.

Các công cụ MCP không sử dụng session. Chúng sử dụng token. Không có middleware nào để thiết lập ngữ cảnh tenant (tenant context). Nếu bạn dựa vào một global scope tìm kiếm session, nó sẽ không tìm thấy gì cả. Khi đó, nó có thể trả về mọi dòng trong cơ sở dữ liệu của bạn. Đó là một vụ rò rỉ dữ liệu âm thầm.

Giải pháp: Lọc dữ liệu một cách tường minh

Đừng bao giờ dựa vào ambient scope khi sử dụng xác thực bằng token. Hãy lọc một cách tường minh trong mọi trường hợp.

Tôi đã tạo một trait duy nhất để xử lý việc này:

trait ResolvesOrgEvents
{
    protected function resolveOrgEvent(Authenticatable $user, string $uuid): ?Event
    {
        if (empty($user->organization_id)) {
            return null;
        }

        return Event::query()
            ->withOrganization($user->organization_id)
            ->where('uuid', $uuid)
            ->first();
    }
}

Cách tiếp cận này tuân theo bốn quy tắc:

  • Sử dụng UUIDs: Đừng bao giờ sử dụng ID tự tăng (auto-increment). Agent không nên có khả năng đoán được ID bằng cách thay đổi một con số.
  • Nguồn sự thật duy nhất (Single Source of Truth): Bộ lọc tổ chức nằm trong một trait duy nhất. Mọi công cụ đều sử dụng nó.
  • Tái sử dụng quyền (Permissions): Đừng tạo ra các quyền mới cho agent. Hãy sử dụng cùng các chuỗi quyền (permission strings) mà ứng dụng web của bạn đang dùng. Điều này ngăn chặn việc agent có nhiều quyền hạn hơn người dùng thực tế.
  • Đánh dấu các tác dụng phụ (Side Effects): Sử dụng các chú thích (annotations) để cho biết một công cụ là chỉ đọc (read-only) hay có thể ghi (write-enabled).

Kiểm thử ranh giới

Bạn phải kiểm thử các trường hợp tiêu cực (negative path). Đừng chỉ kiểm tra xem công cụ có hoạt động hay không. Hãy kiểm tra xem một người dùng từ Tổ chức A có thể xem được dữ liệu từ Tổ chức B hay không.

Nếu một agent cố gắng truy cập một UUID từ một tenant khác, công cụ nên trả về "not found". Nó không nên nói với agent rằng dữ liệu đó có tồn tại nhưng thuộc về người khác.

Hãy coi mọi công cụ AI như một endpoint không đáng tin cậy. Kỷ luật là cách duy nhất để giữ cho dữ liệu của bạn an toàn.

Source: https://dev.to/nasrulhazim/giving-an-ai-agent-the-keys-without-giving-it-the-building-rbac-org-scoped-mcp-tools-in-laravel-43oi