Есть класс уязвимостей, у которого нет ни CVE, ни эксплойта, ни патча. Просто где-то в проекте не написали пять строк. История с сервисом расшифровки встреч tl;dv — образцовый экземпляр, и заодно повод посмотреть на собственный Firebase-проект.
Ниже — хронология по датам, разбор механики и то, что стоит проверить у себя.
28 января: отчёт уходит в компанию
Исследователь под ником BobDaHacker пишет сооснователю tl;dv Рафаэлю Альштадту в LinkedIn и, отдельно, техническому директору. Суть отчёта: коллекция meetings в Firestore отдаётся любому аутентифицированному пользователю сервиса, без разделения по клиентам.
29–30 января Альштадт отвечает, что CTO свяжется немедленно. CTO не связывается.
Что именно было открыто
По публикации исследователя, доступными оказались:
- 181 874 записи о встречах;
- 84 312 уникальных пользователей;
- 35 003 почтовых домена, включая государственные из 23 стран;
- около 1 000 встреч в статусе
recording— то есть идущих прямо сейчас; - более 1 000 публично доступных записей, где вскрылись 715 адресов приглашённых из 228 доменов.
В полях записи лежали почта создателя встречи, платформа конференции, метки времени, статус записи и — это ключевое — идентификатор конференции. То есть строка, по которой в комнату Google Meet или Teams можно просто зайти. Исследователь это проверил: подключился к двум чужим звонкам, включая совещание Министерства образования Малайзии на 157+ участников.
Отдельно стоит зафиксировать сам метод проверки. Подключение к чужому идущему звонку — это уже не «чтение открытых данных», а несанкционированный доступ, независимо от того, насколько дырявой была система. Этичный research останавливается на доказательстве возможности, а не на её реализации. Не повторяйте это ни на чьей инфраструктуре, включая свою SaaS-подписку.
Как это работало технически
Механика на удивление скучная, и в этом всё дело.
Клиент tl;dv логинится, получает JWT, обменивает его на Firebase-токен через gw.tldv.io/v1/users/firebase/token — и дальше ходит напрямую в Firestore, в проект lmi-store. Никакого промежуточного API, который проверял бы «а твоя ли это встреча», в этой цепочке нет вообще.
Именно в этом главная ловушка Firebase-архитектуры. В классической трёхзвенке между клиентом и базой стоит ваш сервер, и «забыть проверить tenant_id» надо в конкретном обработчике. В Firebase сервера посередине нет: Security Rules — это и есть весь бэкенд по части авторизации. Пропущенное правило на одной коллекции означает, что коллекция открыта целиком.
request.auth != null — это проверка «человек зарегистрировался», а не «человек имеет право на эту запись». В продукте, где регистрация открыта всем желающим, первое условие не значит ровно ничего.
14 февраля — 22 июля: тишина
Дальше в хронологии почти нет событий, и это самая неприятная её часть.
| Дата | Что произошло |
|---|---|
| 28 января 2026 | первый отчёт, LinkedIn + письмо CTO |
| 29–30 января | обещание, что CTO ответит немедленно |
| 14–19 февраля | повторные напоминания, ответа нет |
| 6 марта | исследователь перепроверяет — дыра на месте |
| 22 июля | спустя почти полгода дыра всё ещё на месте |
| 4 августа | материал выходит в Dark Reading, реакции по-прежнему нет |
На странице безопасности tl;dv заявлено время реакции на сообщения об уязвимостях — 24 часа. Публичного заявления компании по существу на момент подготовки этого текста найти не удалось.
Вторая находка того же исследования: внутреннее приложение с прогнозами на чемпионат мира на поддомене worldcup.tldv.io отдавало данные сотрудников через эндпоинт /api/entities/* вообще без авторизации. Конкретное число раскрытых записей в пересказах расходится, поэтому цифру здесь не привожу — важен сам паттерн: внутренняя игрушка на том же домене, что и продакшен, живёт по правилам «да кто её найдёт».
Что проверить в своём проекте сегодня
Если у вас есть хоть что-то на Firebase — вот минимальный прогон.
1. Найдите коллекции без правил. Правило по умолчанию в «тестовом режиме» выглядит так и живёт ровно 30 дней, после чего часто заменяется на что-нибудь «временное»:
allow read, write: if request.time < timestamp.date(2026, 9, 1);
Ищите в firestore.rules любые if request.auth != null без дополнительных условий — это и есть уязвимая форма.
2. Привяжите доступ к владельцу, а не к факту логина.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /meetings/{meetingId} {
allow get: if request.auth != null
&& resource.data.ownerId == request.auth.uid;
allow list: if request.auth != null
&& request.query.limit <= 100;
allow create: if request.auth != null
&& request.resource.data.ownerId == request.auth.uid;
allow update, delete: if request.auth != null
&& resource.data.ownerId == request.auth.uid;
}
}
}
3. Помните, что правила — не фильтр. Документация Firebase формулирует это прямо: «Вы не можете написать запрос на все документы коллекции и ожидать, что Cloud Firestore вернёт только те документы, к которым у клиента есть доступ». Если правило требует совпадения ownerId, то и сам клиентский запрос обязан содержать соответствующий where — иначе он просто упадёт. Многих это удивляет, и в этот момент рождается соблазн «пока ослаблю правило, потом верну». Не возвращают.
Регламент, который реально работает:
- правила лежат в репозитории и катятся через
firebase deploy --only firestore:rules, а не правятся в консоли руками; - на каждую коллекцию есть тест в
firebase emulators:start— проверяющий не только «свой видит своё», но и «чужой не видит чужого»; - любой поддомен продакшен-зоны считается продакшеном, включая внутренние поделки;
- в форме приёма сообщений об уязвимостях указан адрес, который читает не только маркетинг.
И потребительский вывод, который касается всех: ИИ-ноутейкер в вашем календаре — это внешняя компания, у которой лежат аудио, транскрипты и список участников всех ваших встреч. Модель угроз тут не «сломают шифрование», а «в одной коллекции забудут одну строчку». Если через встречу проходят персональные данные, коммерческая тайна или NDA, решение о подключении такого сервиса — это решение об обработке данных третьей стороной, со всеми вытекающими вопросами к договору и юрисдикции.
Кто разбирал свои Firestore Rules позже, чем «когда написал первую версию приложения»? И отдельный вопрос к тем, кто отправлял отчёты об уязвимостях в SaaS: сколько раз вам вообще отвечали?
Источники
- tl;dv (Too Lazy; Didn’t Validate): 181,874 Meetings Left Wide Open — первичное исследование, все цифры отсюда
- Inside the tl;dv Flaw That Exposed Live Government and Corporate Meetings — пересказ публикации Dark Reading от 4 августа 2026, независимое подтверждение цифр
- Firebase: Writing conditions for Cloud Firestore Security Rules — про
request.auth, get/list и «правила — не фильтры»

