Забезпечення безпеки ШІ-агентів за допомогою інструментів Laravel MCP

Надання ШІ-агенту доступу до вашого додатка через MCP — це все одно що вручити комусь зв'язку ключів. Якщо ви не встановите правила, він може відкрити не ті двері.

Нещодавно я розробив MCP-інструменти для багатокористувацького (multi-tenant) додатка на Laravel. Моєю метою було дозволити агенту керувати додатком, не надаючи йому доступу до чужих даних.

Проблема з MCP-інструментами

Кожен MCP-інструмент — це ендпоінт. Агент викликає інструмент, і ваш сервер виконує код. У багатокористувацькому додатку кожен інструмент має відповідати на два запитання:

  • Чи дозволено вам це робити?
  • Чи дозволено вам робити це саме в цій організації?

Якщо ви пропустите одне з них, станеться витік даних.

Чому стандартна багатокористувацька архітектура тут не працює

У звичайному вебдодатку у вас є сесії. Ви використовуєте глобальні області видимості (global scopes) для фільтрації даних за ID організації. Це працює, тому що «поточна організація» завжди зберігається в сесії.

MCP-інструменти не використовують сесії. Вони використовують токени. Немає проміжного ПЗ (middleware) для встановлення контексту тенанта. Якщо ви покладаєтеся на глобальну область видимості, яка шукає сесію, вона нічого не знайде. Тоді вона може повернути всі рядки з вашої бази даних. Це прихований витік даних.

Рішення: Явна фільтрація

Ніколи не покладайтеся на неявну область видимості (ambient scope) при автентифікації за токеном. Завжди використовуйте явну фільтрацію.

Я створив один трейт для вирішення цієї задачі:

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 з автоінкрементом. Агенти не повинні мати змоги вгадати ID, просто змінюючи число.
  • Єдине джерело істини: Фільтр організації знаходиться в одному трейті. Кожен інструмент використовує його.
  • Повторно використовуйте дозволи: Не вигадуйте нові дозволи для агентів. Використовуйте ті самі рядки дозволів, які використовує ваш вебдодаток. Це запобігає ситуації, коли агент має більше повноважень, ніж звичайний користувач.
  • Позначайте побічні ефекти: Використовуйте анотації, щоб показати, чи є інструмент лише для читання, чи він дозволяє запис.

Тестування меж

Ви повинні тестувати негативні сценарії. Не просто перевіряйте, чи працює інструмент. Перевірте, що користувач з Організації А не може бачити дані Організації Б.

Якщо агент намагається отримати доступ до UUID іншого тенанта, інструмент має повернути "not found". Він не повинен повідомляти агенту, що дані існують, але належать комусь іншому.

Ставтеся до кожного ШІ-інструмента як до ненадійного ендпоінту. Дисципліна — це єдиний спосіб зберегти ваші дані в безпеці.

Джерело: https://dev.to/nasrulhazim/giving-an-ai-agent-the-keys-without-giving-it-the-building-rbac-org-scoped-mcp-tools-in-laravel-43oi