Аудит экспозиции storage bucket: остановите публичные файлы и листинг
Чеклист аудита экспозиции storage bucket: как найти публичные ACL, рискованные умолчания и небезопасные правила загрузки, чтобы приватные файлы пользователей оставались приватными.

Как выглядит экспозиция storage bucket в реальности
Storage bucket — это большая папка в облаке, где приложение хранит файлы: фото профиля, счета, голосовые заметки, экспортированные отчёты и всё, что загружают пользователи. Команды любят bucket’ы за их дешевизну, скорость и простоту интеграции с приложением.
Экспозиция обычно происходит не потому, что кто‑то «взломал» bucket. Чаще это связано с оставленной открытой настройкой во время прототипа, демо или спешного запуска. Один чекбокс может сделать весь bucket публичным. Или кто‑то предполагает «приватно по умолчанию», хотя реальный дефолт — более разрешающий.
Экспозиция проявляется обычно двумя способами:
- Публичный доступ: любой может получить файл, а иногда и просмотреть весь bucket как директорию.
- Неиндексированное, но угадываемое: файлы не индексируются, но шаблон URL предсказуем, поэтому люди могут угадывать имена вроде
uploads/user_123/id.jpgили пробовать распространённые имена файлов.
Второй вариант хитрее. Основатель может протестировать «приватную» загрузку, не увидеть её в приложении и предположить, что всё в порядке. Но если ссылку можно угадать (или она случайно станет доступна), файл фактически остаётся публичным.
Часто такое начинается с прототипирования. Инструменты для быстрой генерации приложений могут использовать простые правила вроде «разрешить чтение всем», чтобы ничего не ломалось во время демо. Потом прототип перерастает в продакшен, а настройки bucket’а остаются старые.
Аудит экспозиции storage bucket ищет эти реальные точки отказа до того, как они превратятся в утечку: флаги публичного доступа, разрешающие умолчания и правила загрузки, которые делают приватные файлы легко доступными или просматриваемыми.
Составьте инвентаризацию bucket’ов и их содержимого
Начните с непривлекательного шага: выпишите каждый bucket и его назначение. Многие утечки происходят в «лишних» bucket’ах, о которых никто не помнит — экспорты, бэкапы или старые staging‑проекты.
Сопоставьте bucket’ы с реальными фичами приложения. Если ваше приложение позволяет загружать аватары, прикладывать счета, отправлять изображения в чате или экспортировать CSV‑отчёты, каждая такая функция пишет файлы куда‑то. Не полагайтесь на догадки — проверьте код и консоль облака, чтобы получить полную картину.
Вместо организации по названию команды, организуйте по тому, что хранят файлы:
- Загрузки пользователей (фото, документы, аудио)
- Сгенерированные файлы (PDF, миниатюры)
- Экспорты и отчёты
- Бэкапы и снимки состояния
- Внутренние артефакты (логи, сборки)
Для каждой категории зафиксируйте три вещи: где живут файлы (bucket и путь/префикс), как файлы туда попадают (какая служба или endpoint пишет их) и как долго они должны храниться.
Затем решите, кто должен иметь возможность читать каждый тип файлов. «Все в интернете» — редкое требование. Часто команды помечают что‑то как «публичное», хотя достаточно, чтобы это видели только залогиненные пользователи или один клиент и ваша служба поддержки.
Быстрая проверка здравого смысла для каждого bucket или префикса:
- Этот контент действительно должен быть публичным навсегда?
- Если это приватно — кто именно может читать?
- Что случится, если кто‑то угадает имя файла?
- Нужны ли истекающие ссылки?
Быстрый чеклист: 5 проверок, которые можно сделать сегодня
За 15 минут можно узнать многое, проверив, что сможет сделать случайный незарегистрированный пользователь. Пройдите эти пять проверок и зафиксируйте «пройдено/не пройдено» для каждого bucket’а:
- Откройте реальный URL файла в приватном окне браузера. Если файл загружается без входа — считайте его публичным.
- Проверьте, доступен ли листинг. Если вы можете просматривать объекты (или получать ответ, похожий на индекс), вы близки к полной утечке.
- Пройдите поток загрузки полностью. Если загрузки работают без аутентификации или сервер не валидирует базовые ограничения — предположите возможное злоупотребление.
- Посмотрите шаблоны URL. Если ключи содержат имена пользователей, метки времени, номера заказов или простые ID, люди могут угадывать URL и собирать файлы.
- Поискать чувствительные «блуждающие» файлы:
.env, дампы баз данных, бэкапы, экспортированные логи или временные папки.
Реальность такова: если приложение сохраняет фото профиля как /uploads/jane-1700000000.png, даже не‑листируемый bucket может просочиться через угадывание.
Проверьте настройки публичного доступа и ACL
Начните с уровня bucket. Один «публичный» переключатель может переопределить продуманную логику приложения и сделать любой объект доступным для тех, кто угадает URL.
Контролы публичного доступа часто присутствуют на нескольких уровнях: настройка блокировки публичного доступа на bucket’е, политика bucket’а (кто может читать или листить) и object ACL (права на отдельные файлы). Если любой из уровней позволяет анонимное чтение — приватные файлы могут утечь.
Что проверить в первую очередь:
- Подтвердите, что на уровне настроек bucket блокирует публичный доступ.
- Подтвердите, что листинг отключён. Публичный листинг часто хуже публичных чтений, потому что он раскрывает всё.
- Просканируйте на предмет шаблонов object ACL вроде
public-readна пользовательских загрузках. - Зафиксируйте, кто владеет bucket’ом (аккаунт/проект/роль сервиса) и кто может менять настройки.
ACL требуют особого внимания, потому что их можно ставить на каждый объект отдельно. Один багнутый путь загрузки может тихо создавать публичные файлы, даже если политика bucket’а выглядит в порядке.
Если вы нашли публичный доступ — действуйте срочно. Сначала закройте чтения и листинг. Затем исправьте дефолты и правила загрузки, чтобы проблема не повторилась.
Проверьте умолчания прав и унаследованные политики
Многие утечки происходят потому, что bucket «не публичный на бумаге», но новые файлы наследуют права, делающие их читаемыми. Быстрый выигрыш — понять, что происходит по умолчанию при загрузке нового объекта вашим приложением.
Проверьте дефолты на уровне bucket для новых загрузок. В некоторых настройках применяется дефолтный ACL или «canned» permission, которые делают объекты читаемыми для любого, у кого есть URL.
Затем просмотрите политики на уровнях выше bucket’а. Политика аккаунта может переопределять настройки bucket’а. Широкие правила вроде чтения для «всех пользователей» (или эквивалентные глобальные принципы) — частый источник неожиданной экспозиции.
Проверьте, что делает приложение при загрузке
Многие приложения автоматически задают права при загрузке, часто скопированные из туториала. Поискать в коде можно по таким словам, как public-read, acl, make public или по падениям обратно на публичные URL при ошибках.
Чтобы проверить реальное поведение, загрузите тестовый файл и проверьте:
- Можно ли его прочитать без входа в систему?
- Появляется ли он через какой‑то endpoint для листинга?
- Влияют ли изменения дефолтов bucket’а на уже существующие объекты?
Следите за расхождением dev, staging и production
Команды иногда закрывают продакшен, но оставляют dev или staging открытыми, а потом случайно указывают фронтенд или мобильное приложение на неверный bucket. Частая ошибка — «временный» staging‑bucket с глобальным чтением, плюс релиз, который всё ещё использует staging‑конфиг.
Проведите аудит правил загрузки, чтобы приватные файлы оставались приватными
Большинство утечек хранилища начинается во время загрузки. Если ваше приложение позволяет кому‑то загружать файл, нужны чёткие правила: кто может загружать, куда он попадает и кто сможет его читать позже.
Требуйте аутентификацию перед любой загрузкой или используйте подписанные загрузки (signed uploads) с коротким сроком жизни. Подписанная загрузка должна позволять один объект в одно место на короткое время и только для залогиненного пользователя, который её запросил.
Валидация важна даже при приватном хранении. Небезопасные загрузки всё равно могут создать проблемы с безопасностью и стоимостью. Держите правила простыми:
- Принимайте только нужные типы (проверяйте содержимое, а не только расширение).
- Установите жёсткий лимит размера файла.
- Используйте per‑user пути и случайные ID (не имена пользователей или метки времени).
- Блокируйте перезапись, если «заменить файл» не является продуманной функцией.
Простой пример: uploads/jane/profile.jpg легко угадать. uploads/user_123/9f3a2c... гораздо сложнее угадать и проще контролируется политиками.
Прекратите угадывание, листинг и утечки метаданных
Bucket может быть «приватным» и всё равно протекать, если люди могут угадывать ключи, листить объекты или узнавать лишнее из метаданных.
Отключите листинг там, где это позволяет провайдер. Листинг превращает одну удачную догадку в список файлов пользователя.
Далее исправьте предсказуемые имена. Если файлы называются вроде users/[email protected]/avatar.png или invoices/1042.pdf, посторонние могут перебрать шаблоны и подтвердить, какие пользователи есть в системе. Используйте случайные, неугадываемые ID для ключей объектов и не храните идентификаторы пользователей в путях.
Метаданные тоже могут просвечивать. Оригинальные имена файлов часто содержат реальные имена, названия проектов или ID клиентов. Некоторые системы также сохраняют пользовательские метаданные вроде user_id или account_tier, что становится проблемой, если объект случайно станет публичным.
Если вы пытаетесь уменьшить риск обнаружения, сосредоточьтесь на трёх шагах: блокировать листинг, использовать непредсказуемые ключи и минимизировать метаданные.
Логирование и алерты, которые действительно ловят экспозицию
Многие утечки bucket’ов обнаруживают поздно, после того как файлы скачивали днями. Логирование превращает разовый аудит в постоянную защиту.
Включите логи на чтения, записи и удаления, а не только на ошибки. Чтения важнее всего, потому что публичный bucket можно скрейпить без каких‑либо ошибок.
Полезная запись лога отвечает на четыре вопроса:
- Кто обращался (пользователь, сервисный аккаунт или аноним)?
- Что произошло (чтение, загрузка, удаление)?
- Какой объект был затронут (полный путь/ключ)?
- Когда это произошло (метка времени)?
Для алертов фокусируйтесь на паттернах, которые обычно сигналят о проблеме: анонимный доступ и необычные объёмы. Одиночное анонимное чтение может быть тестом конфигурации. Всплеск анонимных чтений — часто признак скрейпера.
Держите алерты маленькими и полезными. Например: анонимные чтения в приватном bucket’е, внезапные всплески скачиваний или много «not found» чтений (признак угадывания).
Также решите, какой срок хранения логов нужен — если у вас только семь дней, вы можете не доказать, к чему был доступ во время инцидента.
Исправления, которые снижают риск и остаются простыми
Проще всего быть в безопасности, если выбрать одну модель и придерживаться её: bucket либо для публичных ресурсов (логотипы, маркетинговые картинки), либо для приватных пользовательских файлов (счета, фото, экспорты). Смешивание двух типов — обычная причина «упс, это публично».
Сначала — приватно по умолчанию. Заблокируйте публичный доступ на уровне bucket’а, затем открывайте только то, что действительно должно быть публичным. Если что‑то должно быть публичным — сделайте это очевидно публичным по дизайну (отдельный bucket, понятная схема имен), а не оставляйте это случайностью.
Для приватных скачиваний избегайте постоянных публичных URL. Отдавайте файлы через приложение после проверки логина и прав или используйте подписанные URL с коротким сроком жизни. Пример: пользователь запрашивает PDF‑экспорт, приложение проверяет владение и выдаёт ссылку, действительную 5 минут.
Запишите окончательные правила (кто может загружать, кто может читать, что публично). Большинство повторяющихся инцидентов происходят, когда кто‑то «поправил в консоли», но приложение всё ещё загружает с прежним поведением.
Пример сценария: стартап с экспонированными пользовательскими загрузками
Небольшой стартап быстро выпускает продукт. Пользователи могут загружать фото профиля, приложение сохраняет их в cloud storage bucket. Всё кажется в порядке, пока кто‑то не опубликует скриншот, где в браузере открывается чужая фотография.
Как это произошло: bucket разрешал публичные чтения, либо на загрузке всё ещё применялся старый public-read ACL. Приложение также использовало предсказуемые имена вроде user_123/avatar.jpg. Никто не хотел делать фото публичными, но дефолты и выбор имен тихо это сделали.
Оказавшись даже с одним файлом, читаемым без входа, угадывание становится простым. Атакующий может перебрать распространённые ID (1, 2, 3...) и запрашивать avatar.jpg для каждого. Если листинг включён, ему даже не придётся угадывать.
Практический план исправлений, который можно сделать за день:
- Заблокировать публичный доступ и убрать публичные ACL на существующих объектах.
- Перейти на короткоживущие signed URLs для приватных скачиваний.
- Сменить схему имён на непредсказуемые ключи (рандомные ID) и не использовать ID пользователей в путях.
- Ввести правила загрузки (тип, размер, путь назначения) и отклонять непреднамеренные перезаписи.
- Просмотреть логи на предмет необычных всплесков чтений и повернуть любые скомпрометированные секреты.
Частые ошибки, приводящие к случайно публичным bucket’ам
Большинство публичных bucket’ов — не результат атаки. Они возникают, потому что временное решение стало постоянным или потому что после запуска никому не принадлежит управление настройками.
Распространённая ловушка — считать, что «не связано» означает приватно. Если bucket позволяет анонимное чтение, файл можно открыть любому, кто угадает путь. Если включён листинг, атакующему даже не нужно угадывать.
Ещё одна частая ошибка — копирование настроек разработки в продакшен. Dev‑bucket’ы часто дают широкий доступ, чтобы команда могла быстро работать. Когда такой шаблон используют для реальных данных, приватные файлы оказываются под демо‑правилами.
Ошибки, которые повторяются снова и снова:
- Позволять браузеру или клиентскому приложению напрямую ставить ACL без строгой серверной проверки.
- Использование постоянных публичных URL для файлов, которые должны быть приватными.
- Полагаться на скрытность имён (случайно выглядящие имена) вместо контроля доступа.
- Оставлять старые тестовые bucket’ы с реальными данными клиентов.
- Смешивать публичные и приватные файлы в одном bucket’е и добавлять исключения, которые расползаются.
Следующие шаги: закройте доступ и проведите аудит
Решите, как должно выглядеть «правильно» для каждого типа файлов. Аватары и маркетинговые изображения могут быть публичными специально. Квитанции, экспорты, загрузки из чатов и всё, что связано с учётной записью пользователя, должны быть приватными по умолчанию с доступом через ваше приложение.
Когда у вас будет целевое поведение, проверьте настройки bucket’ов, а затем кодовые маршруты, которые загружают, читают и шарят файлы. Bucket’ы редко «текут сами по себе». Большинство утечек происходит потому, что приложение ставит неправильную ACL, генерирует угадываемый ключ или пишет загрузки в публичный путь.
Практичный способ зафиксировать это без усложнения:
- Сделайте простую таблицу: тип файла, публичный или приватный, и кто имеет доступ.
- Проверьте настройки и политики bucket’ов в соответствие с таблицей.
- Просмотрите код загрузки: путь назначения, права, устанавливаемые при загрузке, и любые флаги «make public».
- Добавьте регрессионный чек после деплоя: попробуйте листинг bucket’а и получить файл, к которому не должны иметь доступа.
- Включите логирование и алерты на изменения политик и всплески анонимных чтений.
Если вы унаследовали AI‑сгенерированный прототип (например от Lovable, Bolt, v0, Cursor или Replit), настройки storage и пути загрузки часто превращают «работало в демо» в реальный инцидент приватности. Если хотите помощи с этим, FixMyMess (fixmymess.ai) может провести бесплатный аудит кода, найти открытые bucket’ы, сломанные проверки доступа и рискованные дефолты загрузки, а затем подтвердить исправления ручной проверкой.
Часто задаваемые вопросы
Как быстро понять, открыт ли мой bucket?
Откройте реальный URL файла в приватном/инкогнито окне. Если он загружается без входа в систему — считайте его публичным.
Также попробуйте получить доступ к файлу, к которому вы не должны иметь прав (например, аватар другого пользователя или его счёт) по той же схеме URL. Если вы можете его скачать — контролей доступа нет.
Почему листинг хуже, чем один публичный файл?
Листинг позволяет просматривать много объектов, а не только один угаданный файл. Это превращает одну ошибку в полный дамп данных.
Даже если листинг открыт только частично, он часто показывает имена файлов, префиксы и шаблоны, которые сильно упрощают целенаправленное угадывание.
Что значит «неиндексированное, но угадываемое»?
Если ваши URL следуют предсказуемым шаблонам — идентификаторы пользователей, email’ы, метки времени или номера заказов — посторонние могут брутфорсить вероятные пути. Тогда «неиндексированное» фактически становится публичным.
Решение — использовать непредсказуемые ключи объектов и проверять доступ при каждом запросе, а не просто прятать ссылку.
В чём разница между политикой bucket и объектными ACL?
Политики bucket’а задают широкие правила, которые могут разрешать (или блокировать) чтение и листинг для большого количества объектов. Object ACL — это права на уровне отдельного файла, и они могут тихо делать отдельные загрузки публичными, даже если основной policy выглядит закрытым.
Нужно проверять и то, и другое: одна разрешающая ACL в пути загрузки пользователя может слить приватные файлы, не трогая основную политику bucket’а.
Как составить инвентаризацию bucket’ов, не пропустив «забытые»?
Составьте простой реестр всех bucket’ов и того, что в них хранится, включая забытые экспорты, бэкапы и старые staging-проекты. Затем сопоставьте каждый bucket или префикс с фичей, которая туда пишет.
Когда вы поймёте, кому что принадлежит, можно решить, что должно быть публичным, а что — приватным, и какие пути никогда не должны быть доступны без аутентификации.
Как staging и dev bucket’ы умудряются слить продакшен-данные?
Команды часто защищают продакшен, но оставляют dev или staging открытыми, а потом случайно деплоят сборку, указывающую на открытый bucket. Ещё частая оплошность — копирование шаблона, удобного для демо, в реальную среду.
Проверьте конфигурации приложения, какие bucket’ы используются в каждом окружении, и убедитесь, что dev/staging не содержат реальные пользовательские данные.
Какие самые безопасные правила загрузки для приватных файлов пользователей?
Безопасный дефолт: требовать аутентификацию перед загрузкой или использовать короткоживущие signed uploads, которые действуют недолго и привязаны к одному объекту и одному пользователю.
Даже при приватном хранении валидация важна. Простые правила:
- Принимать только нужные типы файлов (проверять содержимое, а не только расширение).
- Установить жёсткий лимит размера.
- Использовать пути per-user и рандомные ID (не имена пользователей или метки времени).
- Блокировать перезапись, если функция «заменить файл» не предусмотрена.
Если используете прямые загрузки в хранилище, пусть сервер решает конечный путь и права, а не клиент.
Как именовать файлы, чтобы по URL их нельзя было угадать?
Используйте рандомные, непредсказуемые ключи объектов и не кладите идентификаторы пользователей в пути. Не используйте email’ы, последовательные ID, номера счетов или метки времени как главный ключ.
Также следите за оригинальными именами файлов и пользовательской metadata — если объект случайно станет публичным, эти данные утекут вместе с файлом.
Какие логи и алерты помогают поймать утечку bucket’а на раннем этапе?
Включите логирование чтений, не только ошибок и записей — скрейпинг выглядит как обычные успешные чтения. Самые полезные сигналы: доступы анонимных пользователей, внезапные всплески загрузок и много 404, что указывает на угадывание ключей.
Делайте алерты простыми и конкретными, чтобы на них реагировали, и храните логи достаточно долго, чтобы расследовать инциденты, которые заметили не сразу.
Что делать в первую очередь, если я обнаружил публичный доступ к bucket’у?
Сначала немедленно закройте публичный доступ: заблокируйте public access на уровне bucket’а, уберите публичные ACL и проверьте, отключён ли листинг. Затем проверьте код загрузки, чтобы новые файлы не создавались снова публичными.
Если вы получили прототип, сгенерированный AI, часто проблемой являются дефолты хранения и пути загрузки. FixMyMess (fixmymess.ai) может выполнить бесплатный аудит кода, найти точные ошибки конфигурации и подтвердить исправления вручную.