Браузерные границы: origin, cookies, CORS и CSRF
Origin — тройка scheme, host, port. https://site.example и
https://cms.example — разные origins, хотя могут быть одним site для правил
cookies. Same-site и same-origin нельзя подменять друг другом. Серверный fetch
из Next.js не ограничивается CORS браузера; ошибка CORS не доказывает отказ CMS.
Три независимых решения
Аутентификация устанавливает, кто делает запрос. Авторизация разрешает конкретную операцию над конкретным объектом. CORS определяет, может ли браузерный JavaScript читать cross-origin ответ. Злоумышленник может вызвать API вне браузера; разрешённый Origin не является учётной записью.
Исходник схемы
sequenceDiagram participant B as Браузер другого origin participant A as API B->>A: OPTIONS, метод и заголовки A-->>B: Разрешённые origin, методы, заголовки B->>A: Запрос, если preflight разрешён Note over A: Отдельно проверка входа, личности и прав A-->>B: Ответ
Некоторые запросы отправляются без preflight, например обычная HTML-форма.
CORS может закрыть чтение ответа, когда эффект уже выполнен. mode: "no-cors"
даёт opaque ответ и не превращает закрытый API в читаемый. Для разрешённого
credentialed fetch нужен конкретный Access-Control-Allow-Origin, не *,
и Access-Control-Allow-Credentials: true. При отражении разрешённого Origin
добавляют Vary: Origin, чтобы общий кеш не смешал ответы разных origins.
Cookies и токены
Cookie браузер отправляет автоматически по правилам домена, пути, Secure,
SameSite и запроса. HttpOnly запрещает чтение из JS, но не отправку cookie.
Secure требует HTTPS, SameSite ограничивает cross-site отправку. SameSite=None
требует Secure; политика браузера может дополнительно ограничить third-party cookies.
Для cross-origin fetch обычно нужен credentials: "include", но он не отменяет
SameSite и CORS. Не рассчитывайте на Path как на изоляцию доверия приложений.
Bearer token передают явно в Authorization. Серверный CMS token проекта остаётся
на сервере; его нельзя помещать в Client Component, публичную переменную
NEXT_PUBLIC_*, HTML или URL. URL попадает в историю и логи. Доступ к Strapi admin
не даёт автоматически права произвольному читателю сайта.
CSRF — использование автоматически прикладываемых полномочий для чужого запроса. Для cookie-authenticated изменения нужны согласованные меры: запрет эффектов в GET, проверка Origin/Referer по allowlist и CSRF token либо другой принятый механизм. SameSite — дополнительный барьер, а не универсальное доказательство. XSS способен выполнять код внутри разрешённого origin, поэтому CSRF-защита не заменяет безопасный вывод контента. HttpOnly уменьшает кражу cookie, но не возможности XSS отправлять действия от имени пользователя.
Лаборатория двух origins
В отдельной локальной среде откройте статическую учебную страницу на
http://127.0.0.1:4181 и собственный тестовый HTTP API на :4182.
Не используйте CMS credentials или production endpoints.
- Вызовите GET через fetch без CORS-ответа: сравните Network, Console и серверный лог. Сервер мог получить запрос, хотя JS не прочитал тело.
- Разрешите ровно
http://127.0.0.1:4181; повторите GET.localhost:4181является другим origin, проверьте запрет отдельно. - Добавьте JSON POST или нестандартный заголовок: найдите OPTIONS до POST. Запретите метод в ответе preflight и убедитесь, что POST не отправился.
- Сравните POST формы без custom headers: он может уйти без preflight. Поэтому тестовый обработчик не выполняет реальный эффект без проверки прав.
- При cookie-auth сравнении отдельно фиксируйте hostname, SameSite, Secure, credentials и browser policy. HTTPS-cookie опыт требует локального HTTPS; результаты plain HTTP нельзя переносить на production.
- Вызовите тот же API из Node/curl: успешное чтение вне браузера не исправляет CORS.
Приёмка: таблица «запрос отправлен / эффект разрешён / ответ читается JS» для каждого случая. Это ручной браузерный опыт; happy-dom и серверные тесты не воспроизводят полную cookie/CORS-политику настоящего браузера.
В Atmanki браузер получает публичный контент через сайт, сервер обращается к CMS. Webhook проверяет отдельный секрет; это не пользовательская сессия и не универсальная модель auth для будущих mutations. Ограничивайте размер входа, валидируйте форму и проверяйте права прежде эффекта, независимо от транспорта.