Белый список по источнику (Origin)
Ограничение, с каких веб-источников можно пользоваться браузерным API-ключом.
Allow-list по Origin
Если API-ключ лежит в браузерном коде — считайте, что прочесть его может кто угодно. Allow-list по Origin делает этот сценарий допустимым: AACsearch отбрасывает запросы, у которых заголовок Origin не входит в список, настроенный для ключа.
Это не CORS. CORS управляет тем, разрешит ли браузер прочитать ответ. Allow-list по Origin — серверная проверка, которая отбрасывает запрос до выполнения поиска, независимо от того, браузер ли отправил запрос.
Модель безопасности браузера
Браузерный поиск держится на трёх слоях, которые работают вместе:
- Allow-list по Origin — ключом можно пользоваться только с ваших доменов.
- Scope
search— ключ не может мутировать данные даже с вашего домена. - Жёсткий rate limit в минуту — ограничивает злоупотребления с одного клиента.
Для пер-юзер фильтрации добавьте четвёртый слой: Scoped-токены.
Настройка allow-list
В дашборде:
- Project → API keys → Create key (или редактирование существующего).
- В Allowed origins — по одному источнику в строке. Origin — это
scheme://host[:port], без пути, без слеша в конце, без подстановок. - Сохраните.
Примеры:
https://shop.example.com
https://www.example.com
https://staging.example.com:3000Для локальной разработки добавьте http://localhost:3000 (или ваш порт). Уберите его из боевого ключа перед выкаткой.
Как идёт сверка
На каждом публичном запросе сервер берёт заголовок Origin и сверяет с allow-list — точное, регистрозависимое равенство. Если список непуст, а заголовок отсутствует или не совпадает — HTTP 403 origin_not_allowed.
Если список пуст — проверка не выполняется. Это правильный дефолт для серверного ключа (его не вызывает ни один браузер). И неправильный дефолт для браузерного ключа.
Браузер шлёт: Сервер сверяет:
Origin: https://shop.example.com → allowedOrigins.includes("https://shop.example.com") ?
да → продолжаем
нет → 403 origin_not_allowedСравнение — O(n) по короткому списку. Никаких startsWith, подстановок, regexp. Если нужен *.example.com — перечисляйте поддомены.
Когда применять
| Где используется ключ | Allow-list? |
|---|---|
| Браузер (виджет, ваш JS) | Обязательно. Без него ключ ничем не ограничен. |
| Мобильное приложение (native HTTP) | Не нужно — приложение не шлёт браузерный Origin. |
| Серверный воркер | Не нужно. Используйте серверный ingest / admin ключ. |
| CMS-коннектор (PrestaShop, Bitrix) | Не нужно. ss_connector_* — серверные. |
От чего allow-list не защищает
- От украденного ключа. Серверный атакующий вообще не пошлёт
Origin— и получит 403. Но если он запускает реальный браузер на разрешённом домене (XSS, вредоносное расширение),Originкорректный и запрос пройдёт. Здесь работают остальные слои (scopesearch, rate limit, scoped-токен). - От уязвимого расширения. Расширения иногда инжектят скрипты на ваши страницы. CSP и жёсткий scope снижают эффект.
- От XSS на вашем домене. Если у сайта XSS, атакующий ходит к API с вашего же origin. Чините XSS.
Частые ошибки
- Слеши и пути.
https://example.com/≠https://example.com. В заголовкеOriginпути нет — уберите. - Забыли staging / preview. Если preview-окружение крутится на другом хосте (
pr-123.preview.example.com), проверка упадёт. Либо отдельный ключ на окружение, либо включите preview-хосты. - Один ключ в мобильном приложении. Нативное приложение не шлёт
Origin— строгий allow-list его отвергнет. Заводите отдельный ключ или оставляйте список пустым на мобильном и опирайтесь на другие слои. Origin: null. Песочные iframe иfile://-страницы шлютOrigin: null.nullсовпадает, только если он явно в списке — обычно не нужно.
Пример: продакшн-настройка
Имя ключа: Frontend Search (prod)
Scopes: search
Allowed origins: https://example.com
https://www.example.com
Rate limit: 600 / минута / ключ
Индексы: products, articlesЭтот ключ, утёкший из бандла, можно использовать только с example.com или www.example.com. Записать он ничего не сможет, а пики выше 600 запросов в минуту режутся до Typesense.
См. также
- API-ключи — scopes и ротация
- Scoped-токены — пер-юзер фильтрация
- Best practices — общий чек-лист