Отсутствие проверки безопасности в эндпоинте GET-запроса для коллекций позволяло любому пользователю с базовым аккаунтом CoopCycle выгрузить полную адресную книгу всех магазинов в общем экземпляре, раскрывая имена, уличные адреса и почтовые индексы бесчисленного количества клиентов. Уязвимость была устранена в течение двух дней, и пользователям настоятельно рекомендуется обновиться до последней выпущенной версии.

Как произошла утечка

CoopCycle — логистическая платформа с открытым исходным кодом, используемая продовольственными кооперативами, — определяет свой API с помощью PHP-фреймворка API Platform. В этом фреймворке каждая операция (POST, GET и т. д.) должна сопровождаться выражением безопасности (security expression); если выражение пропущено, фреймворк выполняет код без какой-либо проверки авторизации.

Разработчики защитили POST-запрос, который создает или обновляет список адресов магазина, стандартным выражением is_granted('edit', object). Это работает, так как запрос нацелен на одну конкретную сущность магазина, предоставляя фреймворку определенный «объект» для оценки.

GET-запрос, который считывает тот же ресурс, нацелен на коллекцию: /api/stores/{id}/addresses. У коллекции нет одного конкретного объекта, поэтому к ней нельзя применить то же выражение is_granted('edit', object). Поскольку разработчики не добавили строку безопасности, фреймворк предоставлял данные об адресах любому аутентифицированному пользователю, независимо от принадлежности к арендатору (tenancy).

В общем экземпляре CoopCycle злоумышленник мог просто перебирать ID магазинов, отправлять GET-запросы к эндпоинту и собирать домашние адреса каждого клиента, хранящегося в системе. Для этого не требовалось никаких дополнительных привилегий, кроме обычной учетной записи.

Почему баг не был обнаружен

Проблема не была простой оплошностью. В декларативной модели безопасности API Platform отсутствует простой способ выразить условие: «пользователь должен принадлежать к тому же арендатору, что и каждый объект в коллекции». Пропущенная строка кода находилась именно там, где фреймворк делает авторизацию затруднительной.

Ситуацию усугубляло то, что набор тестов проекта фактически подтверждал, что GET-ответ, содержащий все адреса, является ожидаемым поведением. Другими словами, автоматизированные тесты проходили успешно, потому что фикстуры, используемые при тестировании, допускали доступ между арендаторами (cross-tenant access), фактически маскируя уязвимость. В данном случае «зеленый» набор тестов создал ложное чувство безопасности.

Кто выиграл, а кто проиграл

  • Клиенты: Их персональные данные (PII) — полные имена и домашние адреса — стали доступны любому пользователю платформы. Несмотря на то, что данные не были опубликованы в открытом доступе, утечка нарушила конфиденциальность во многих кооперативах.
  • Кооперативы, использующие CoopCycle: Доверие к способности платформы защищать данные арендаторов было подорвано. Любой кооператив, который еще не обновился, подвергался риску дальнейшей утечки.
  • Мейнтейнеры CoopCycle: Их быстрая реакция — выпуск патча в течение двух дней и добавление регрессионных тестов — ограничила окно для эксплуатации уязвимости и продемонстрировала ответственное управление open-source проектом. Однако инцидент подчеркивает необходимость более строгого процесса проверки безопасности, особенно в отношении настроек по умолчанию, определяемых фреймворком.

На что следует обратить внимание разработчикам и аудиторам

  • Асимметрия операций: Если путь (path) защищен для POST (или любой другой мутирующей операции), но соответствующий GET-запрос открыт, это тревожный сигнал. Наличие защиты в POST-запросе указывает на намерение разработчиков защитить ресурс.
  • Эндпоинты коллекций: Все, что возвращает список, а не один элемент, часто выпадает из привычных паттернов безопасности. Убедитесь, что проверки авторизации явно добавлены для массового чтения данных.
  • Реалистичность набора тестов: Убедитесь, что фикстуры отражают реальные границы арендаторов. Проходящий тест, который подтверждает утечку данных между арендаторами, — это предупреждающий знак, а не сигнал о том, что всё в порядке.

Исправление и следующие шаги

После сообщения об уязвимости основная команда CoopCycle добавила недостающее выражение безопасности для операции GET-коллекции и внедрила регрессионные тесты, обеспечивающие изоляцию арендаторов как для одиночных элементов, так и для эндпоинтов коллекций. Патч был включен в последующую версию программного обеспечения.

Пользователям CoopCycle следует:

  1. Убедиться, что они используют последнюю версию программного обеспечения.
  2. Проверить любые пользовательские расширения или плагины, которые могут вносить подобные пробелы на уровне коллекций.
  3. Повторно провести сканирование безопасности, уделяя особое внимание асимметрии чтения/записи на всех маршрутах API.

Вывод

Фреймворки, реализующие декларативный подход к безопасности, могут скрывать опасные уязвимости, когда разработчики полагаются на паттерны, работающие только для одиночных объектов. Простая проверка — есть ли у стороны чтения эндпоинта та же защита, что и у стороны записи? — может выявить целый класс утечек данных между тенантами, которые в противном случае остались бы незамеченными за «зелеными» наборами тестов.