Браузерный токен и серверный секрет
Модель угроз для ключей приёма ActionPulse — что сможет посторонний с браузерным токеном, что произойдёт при утечке серверного ключа, как хранить секрет и что делать при подозрении на утечку.
На этой странице
- Модель угроз простыми словами
- Почему браузерный токен публичен и это нормально
- Что удерживает браузерный токен
- Как хранить серверный ключ
- Ротация и порядок действий при подозрении на утечку
- Если под подозрением серверный ключ
- Если под подозрением браузерный токен
- Чего продукт не гарантирует
- Что проверить
У приёма событий два разных ключа с разными границами доверия, и путаница между
ними — самая дорогая ошибка интеграции. Браузерный токен pp_bt_…
публичен по устройству: его видит каждый посетитель сайта, и это нормально.
Серверный ключ pp_sk_… — секрет: его утечка означает, что кто-то
может писать в ваш проект «достоверные» бизнес-факты.
Здесь — модель угроз, а не справочник по областям действия. Что каждому ключу разрешено по эндпоинтам, какие бывают коды отказа и как выпустить ключ — в типах ключей приёма и в получении ключей.
Модель угроз простыми словами
Начнём с того, чего не может ни один ключ приёма: читать данные. У ключей приёма нет ни одной области действия на чтение аналитики. С ключом нельзя открыть отчёт, выгрузить события, посмотреть список людей, увидеть запись сессии, зайти в кабинет или дотянуться до биллинга. Кабинет и его API работают на другом механизме — сессии пользователя.
Отсюда следует главное: утечка ключа приёма — это угроза целостности данных и расхода квоты, а не утечка ваших аналитических данных.
| Что сможет посторонний | С браузерным токеном | С серверным ключом |
|---|---|---|
Отправлять встроенные события pp.* |
да | нет |
| Отправлять свои события | только имена из списка токена | любые, кроме pp.* |
| Отправлять подтверждённые бизнес-факты (оплаты, заказы, возвраты) | нет | да |
Записывать свойства человека через identify |
да | да |
Связывать идентификаторы через alias |
нет | да |
| Присылать поддельные записи сессий | да | нет |
| Читать публичную конфигурацию сбора | да | нет |
| Читать любые ваши данные | нет | нет |
| Расходовать месячную квоту события | да | да |
Что это значит на практике.
Браузерный токен в чужих руках — это мусор в отчётах: накрутка кликов и
просмотров, поддельные записи сессий, произвольные свойства у существующих
user_id, расход месячной квоты вплоть до отказов quota_exceeded. Неприятно и
требует чистки, но ни один финансовый показатель так не подделать: имена
registered, subscription_started, payment_succeeded, order_paid,
refund и остальные подтверждённые факты браузеру закрыты на уровне политики
событий, а не настройкой.
Утечка серверного ключа — это уже подделка бизнес-фактов. Ключ принимается
для тех самых имён, на которых строятся выручка и конверсии. Плюс к этому он
несёт alias: чужой запрос может склеить два разных человека в одного, и граф
идентичности проекта будет искажён. Именно поэтому серверный ключ никогда не
должен покидать бэкенд.
Почему браузерный токен публичен и это нормально
Токен обязан попасть в браузер: без него страница не сможет отправить событие. Спрятать значение, которое исполняется на устройстве посетителя, невозможно — любые «обфускация» и «получение токена с бэкенда» лишь добавляют шаг, который всё равно виден в инструментах разработчика. Поэтому ActionPulse не притворяется, что токен секретен, а строит модель на том, что он публичен.
<script
src="https://YOUR_ACTIONPULSE_HOST/v1/t.js"
data-token="YOUR_BROWSER_TOKEN"
data-consent="required"
defer
></script>
Значение в разметке — ожидаемое состояние. В браузерное хранилище трекер токен не складывает: там лежат только идентификаторы посетителя, метка сессии и очередь доставки событий. Значение подставляет ваш шаблон или сборка, а трекер читает его из атрибута тега или из параметра инициализации.
Считать браузерные события недоверенным вводом — заявленное свойство продукта, а не оговорка. Всё, что приходит публичным токеном, обрабатывается как ввод неизвестного происхождения: с ограниченными правами, поэлементной проверкой и жёсткими лимитами.
Что удерживает браузерный токен
Публичность компенсируется четырьмя ограничениями, и все четыре задаются в момент выпуска.
Область действия. У токена ровно пять прав: чтение публичной конфигурации
сбора, запись событий, identify, запись сессий, захват элемента выбором на
странице. Права identity:alias у него нет вообще — не «выключено по
умолчанию», а отсутствует как возможность. Проверяются одновременно тип ключа и
явная область действия: область сама по себе никогда не расширяет тип.
Список разрешённых origin. От 1 до 32 точных адресов. Проверка работает так:
приём берёт заголовок Origin запроса, приводит его к каноническому виду
(регистр схемы и хоста, завершающий слеш, точка на конце имени хоста, порт по
умолчанию) и сравнивает на точное совпадение со списком токена. Маски
запрещены и при выпуске, и при сравнении. Пустой или некорректный список означает
отказ всегда — токен без origin нерабочий. Несовпадение даёт 403 с кодом
credential_origin_forbidden, и присланный origin в ответе не отражается.
Подробности нормализации — в CORS и origin.
Срок действия. Браузерный токен обязан иметь срок; если он не задан явно,
применяется 90 дней с момента выпуска. Истёкший токен отвечает 401 так же, как
отозванный. Срок виден в списке ключей проекта в колонке «Истекает»; отдельного
напоминания об истечении продукт не присылает — поставьте себе календарное.
Список своих имён событий. От 1 до 128 точных имён. Если при выпуске список
не задан, применяется узкий стартовый список из одного имени —
signup_completed. Любое другое своё имя отклоняется на уровне отдельного
элемента батча с кодом credential_event_forbidden. Встроенные события pp.*
списком не ограничены и работают независимо от него. Если событие объявлено в
реестре проекта как серверное, браузерный токен его не отправит даже при наличии
имени в списке — реестр авторитетнее.
Сверх этого действуют лимиты частоты на префикс ключа и месячная квота событий тарифа, поэтому «бесконечной» накрутки с одного токена не получится.
Как хранить серверный ключ
Единственная защита серверного ключа — секретность. Правила простые.
Можно:
- переменная окружения процесса, значение подставляется из менеджера секретов (Vault, AWS Secrets Manager, Yandex Lockbox, Kubernetes Secret, GitHub Actions secret и подобные);
- отдельное значение на каждое окружение: рабочее, тестовое, локальная разработка;
- отдельное значение на каждый сервис, если их несколько — тогда отзыв одного не останавливает остальные.
Нельзя:
- исходный код и репозиторий, даже приватный;
- фронтенд-бандл, разметка, контейнер тег-менеджера, мобильное приложение;
- адрес запроса — новые ключи в адресе не принимаются вообще, а сами адреса попадают в логи прокси, CDN и систем мониторинга на всём пути запроса, и убрать их оттуда ActionPulse не может;
- логи приложения, сообщения об ошибках, трассировки, отчёты об исключениях;
- переписка, задачи, скриншоты, экраны демонстраций.
Как ключ устроен со стороны ActionPulse: значение имеет вид
pp_<тип>_<префикс>_<секрет>, в базе хранится только хеш полного значения и
безопасный 8-символьный префикс. Полное значение показывается один раз, при
выпуске или ротации, и восстановить его позже нельзя. Префикс не секретен —
именно его безопасно называть в обращении в поддержку.
Ещё две детали, которые полезны при отладке:
- сообщение при
401одинаково для всех причин — отсутствует, не разбирается, отозван, истёк, передан недопустимым способом. По ответу нельзя подбирать ключи; - если заголовок
Authorizationв запросе присутствует, но собран неверно, запрос получает401и не «проваливается» на менее доверенный способ передачи.
Ротация и порядок действий при подозрении на утечку
Сначала важное свойство ротации: она выдаёт новое значение и отзывает старое в той же транзакции. Окна, когда работают оба значения, не существует. Профиль сохраняется целиком — тип, области действия, список origin, список имён событий и исходный срок действия. Поэтому ротация подходит там, где вы можете заменить значение практически одновременно, и не подходит там, где деплой занимает время.
Если под подозрением серверный ключ
Порядок, который не теряет события:
- Выпустите новый серверный ключ (роль «Админ» или «Владелец», Настройки → Проект → Credentials проекта). Именно выпуск, а не ротацию: старый ключ пока продолжает работать.
- Положите новое значение в менеджер секретов и раскатите на все процессы, которые отправляют события. Не забудьте фоновые обработчики и cron.
- Убедитесь, что события идут новым ключом: ответ
202с пустымrejected, свежие события на экране проверки интеграции. - Отзовите старый ключ. Отзыв необратим и действует сразу; если внутренний канал мгновенного оповещения недоступен, отказ наступает по истечении кэша — в пределах пяти минут.
- Смените всё, что лежало рядом. Если ключ утёк вместе с файлом окружения, считайте скомпрометированным весь файл, а не одну строку.
- Разберитесь с последствиями в данных. Проверьте за подозрительный период подтверждённые бизнес-события и связывания идентификаторов: подделанные оплаты и заказы искажают выручку, а склейка идентичности не отменяется сама.
- Зафиксируйте инцидент у себя. ActionPulse не определяет утечку и не присылает о ней уведомлений; в списке ключей видно только префикс, статус и дату последнего использования.
Если под подозрением браузерный токен
Браузерный токен публичен, поэтому «утечка» здесь — не событие безопасности, а злоупотребление: кто-то использует ваш токен со своей страницы или из скрипта. Признаки видны в данных: необъяснимый рост числа событий, события с чужих адресов страниц в списке событий, мусорные значения свойств.
- Проверьте, что список origin токена действительно узкий и не содержит лишних адресов.
- Выпустите новый токен с точным списком origin и минимальным списком своих имён событий. Добавить домен или имя события в существующий токен нельзя — ротация профиль не меняет.
- Замените значение в разметке или в вызове инициализации и раскатите.
- Проверьте приём и отзовите старый токен.
Чего продукт не гарантирует
Прямой список, чтобы не пришлось выяснять это в инциденте.
- Не доказывает, что браузерное событие отправил ваш посетитель. Публичный токен по определению может использовать кто угодно. Достоверность даёт только серверный ключ.
- Не проверяет origin у не-браузерных клиентов. Заголовок можно подделать; список origin — гигиена, а не аутентификация.
- Не обнаруживает утечку ключа. Нет ни поиска ключей в публичном интернете, ни оповещений об аномалиях по конкретному ключу.
- Не показывает полное значение ключа повторно. Потерянное значение заменяется только выпуском нового.
- Не даёт окна, когда работают старое и новое значения при ротации.
- Не откатывает данные. Ни подделанные события, ни выполненные связывания идентификаторов не отменяются автоматически.
- Не отвечает за секрет на вашей стороне. Ключ в репозитории, в логе CI или в браузерном бандле — это ваша граница, и никакая проверка на приёме её не восстановит.
Что проверить
- Поиск по префиксу серверного ключа во всём фронтенде, в контейнере тег-менеджера и в репозитории не даёт совпадений.
- Серверный ключ читается из окружения, а окружение — из менеджера секретов; значение не появляется в логах, в сообщениях об ошибках и в трассировках.
- У браузерного токена в списке origin только рабочие адреса, а в списке имён событий — только те, что реально вызывает фронтенд.
- Подтверждённые бизнес-факты отправляются исключительно с бэкенда.
- В календаре есть напоминание о сроке действия браузерного токена: 90 дней
проходят незаметно, а истечение выглядит как внезапный
401. - Порядок действий при утечке записан у вас в runbook: кто выпускает ключ, кто раскатывает, кто отзывает старый и кто проверяет данные за период.
- Ключи выпускают только те, кому это нужно: выпуск, ротация и отзыв доступны роли «Админ» и «Владелец» — см. роли и права.