使用 Laravel MCP 工具保障 AI Agent 的安全性

通过 MCP 让 AI Agent 访问你的应用,就像把钥匙串交给某人一样。如果你不设定规则,他们可能会打开错误的门。

我最近为一款多租户 Laravel 应用构建了 MCP 工具。我的目标只有一个:让 Agent 能够驱动应用,同时又不让它看到别人的数据。

MCP 工具存在的问题

每个 MCP 工具都是一个端点。Agent 调用工具,你的服务器运行代码。在多租户应用中,每个工具都必须回答两个问题:

  • 你被允许执行此操作吗?
  • 你被允许在特定的组织中执行此操作吗?

如果你漏掉其中一个,就会导致数据泄露。

为什么标准的多租户机制在这里会失效

在普通的 Web 应用中,你拥有 Session(会话)。你使用全局作用域(global scopes)根据组织 ID 来过滤数据。这之所以有效,是因为“当前组织”始终存在于 Session 中。

MCP 工具不使用 Session,而是使用 Token(令牌)。没有中间件来设置租户上下文。如果你依赖于寻找 Session 的全局作用域,它将一无所获。随后,它可能会返回数据库中的每一行数据。这是一种隐蔽的数据泄露。

解决方案:显式过滤

在使用 Token 认证时,永远不要依赖环境作用域(ambient scope)。每次都要进行显式过滤。

我创建了一个单一的 Trait 来处理这个问题:

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();
    }
}

这种方法遵循四个规则:

  • 使用 UUID:永远不要使用自增 ID。Agent 不应该能够通过更改数字来猜测 ID。
  • 单一事实来源:组织过滤器存在于一个 Trait 中。每个工具都使用它。
  • 复用权限:不要为 Agent 发明新的权限。使用与你的 Web 应用相同的权限字符串。这可以防止 Agent 拥有比人类用户更高的权限。
  • 标记副作用:使用注解来显示工具是只读的还是可写的。

测试边界

你必须测试负面路径(negative path)。不要只测试工具是否有效,还要测试来自组织 A 的用户是否无法看到来自组织 B 的数据。

如果 Agent 尝试访问另一个租户的 UUID,工具应该返回“未找到(not found)”。它不应该告诉 Agent 数据存在但属于别人。

将每个 AI 工具都视为不可信的端点。严谨是保护数据安全的唯一途径。

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