使用 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 工具都视为不可信的端点。严谨是保护数据安全的唯一途径。
