Что такое заголовки безопасности, Content Security Policy и CORS
Что проверяют
Понимаете ли вы, кто и от чего защищается каждым механизмом. CSP защищает пользователя страницы от внедрённого кода, CORS решает, каким чужим сайтам можно читать ответы вашего API. Следом почти всегда дают ситуацию: в консоли ошибка CORS, что будете делать.
Как отвечать
Заголовки безопасности это инструкции браузеру, которые сервер отдаёт вместе с ответом. Базовый набор: Strict-Transport-Security (только HTTPS), X-Content-Type-Options: nosniff (не угадывать тип файла), Referrer-Policy, Permissions-Policy (доступ к камере, геолокации) и главный из них, Content-Security-Policy.
CSP задаёт, откуда странице можно загружать и исполнять ресурсы:
- директивы:
script-src,style-src,img-src,connect-srcдля запросов,frame-ancestorsвместо устаревшегоX-Frame-Options; - nonce: сервер генерирует случайное значение на каждый ответ и ставит его в заголовок и в атрибут разрешённых
<script>, а'strict-dynamic'пропускает то, что они подгружают; - внедрение: начинают с
Content-Security-Policy-Report-Only, чтобы собрать нарушения, ничего не ломая.
CORS ослабляет, а не усиливает политику одного источника. По умолчанию скрипт не может прочитать ответ с другого origin, и сервер разрешает это заголовком Access-Control-Allow-Origin. Перед «непростым» запросом, например с Authorization или Content-Type: application/json, браузер шлёт предварительный OPTIONS. При credentials: "include" сервер обязан вернуть конкретный origin вместо * и Access-Control-Allow-Credentials: true.
Где спотыкаются
Чинят CORS на фронтенде: ставят mode: "no-cors" и получают непрозрачный ответ, который нельзя прочитать. Правится это на сервере, а в разработке помогает прокси дев-сервера, например server.proxy в Vite.
Считают CORS защитой API. Curl и Postman его игнорируют, а простой POST с чужого сайта до сервера доходит, браузер лишь не отдаёт ответ скрипту. От CSRF защищают куки с SameSite и токены.
Добавляют в CSP без nonce 'unsafe-inline' для скриптов, чтобы всё заработало. Такая политика при XSS почти ничего не блокирует.