Ещё неделю назад я думала, что CORS и CSP — это одно и то же. Оба звучат как какая-то шифрованная аббревиатура из мира информационной безопасности. Оказалось, что это два совершенно разных механизма, которые работают на разных уровнях и защищают от разных угроз.

Что такое CORS

Допустим, у тебя на сайте https://magazin.ru работает фронтенд на JavaScript. И в какой-то момент страница с твоего сайта пытается получить данные с другого домена — https://api.pochta.ru. Браузер говорит: «Стоп, это чужой домен, я не могу просто так забрать данные». Это называется Same-Origin Policy — правило, которое не позволяет сайту читать данные с другого origin.

Но иногда такое чтение нужно. Например, твой фронтенд на magazin.ru обращается к бэкенду на api.magazin.ru. Это разные поддомены, и браузер тоже заблокирует запрос. CORS (Cross-Origin Resource Sharing) — это механизм, который позволяет серверу сказать браузеру: «Я разрешаю запросы с этого конкретного домена».

Делается это через HTTP-заголовок Access-Control-Allow-Origin. Когда браузер отправляет запрос, сервер в ответ добавляет что-то вроде Access-Control-Allow-Origin: https://magazin.ru. Браузер видит этот заголовок и понимает, что запрос безопасен — можно отдать данные.

Если заголовка нет — браузер блокирует ответ, даже если сервер его успешно обработал. Это важно: CORS — это не блокировка на сервере, а инструкция для браузера. Сервер может обработать запрос, но браузер не даст JavaScript'y его прочитать.

Что такое CSP

CSP (Content Security Policy) работает иначе. Это список правил о том, какой контент браузеру разрешено загружать и исполнять на странице. Неважно, откуда этот контент пришёл — CSP контролирует его на уровне браузера.

Простой пример. Допустим, ты добавил на сайт форму комментариев. Кто-то пишет комментарий с текстом <script>alert('хакер!');</script>. Без CSP браузер выполнит этот скрипт — и это XSS-атака. Но если у тебя настроен CSP-заголовок с правилом script-src 'self', браузер заблокирует выполнение скрипта из комментария, даже если злоумышленник его туда встроил.

CSP также умеет блокировать инлайновые стили и скрипты, запрещать загрузку шрифтов и картинок с чужих доменов, ограничивать отправку форм. Это мощный инструмент, но он требует аккуратной настройки — если ошибиться с доменами, часть функционала сайта перестанет работать.

Пять минут настройки — год безопасности

Базовая настройка CSP для большинства сайтов выглядит примерно так:

Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'

'self' означает «только мой домен». 'unsafe-inline' разрешает инлайновые стили — они часто нужны для совместимости, но серьёзная защита требует от них отказаться в пользу внешних CSS-файлов и хэшей. Постепенно ужесточай правила, наблюдая за консолью браузера — она подскажет, какой контент блокируется и что нужно добавить в белый список.

CORS и CSP: в чём разница

CORS решает вопрос «кто может обращаться к моему API». Это фильтр на уровне сервера — разрешаешь только нужные домены, остальные получают ошибку.

CSP решает вопрос «что моя страница может загружать и исполнять». Это фильтр на уровне браузера — скрипты, стили, картинки, фреймы. Даже если злоумышленник внедрил скрипт через уязвимость, CSP может заблокировать его выполнение.

CORS не спасёт от XSS, а CSP не защитит от подделки запросов с твоего домена. Вместе они закрывают разные векторы атаки — и это именно тот случай, когда 2 + 2 дают не четыре, а полноценную многоуровневую защиту.

Как настроить оба

Вот пример для Next.js — один из самых распространённых фреймворков:

// CORS
// app/api/products/route.ts
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
  return NextResponse.json(
    { products: [] },
    {
      headers: {
        'Access-Control-Allow-Origin': 'https://magazin.ru',
        'Access-Control-Allow-Methods': 'GET, POST',
        'Access-Control-Allow-Headers': 'Content-Type',
      },
    }
  );
}

// CSP
// next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [
          {
            key: 'Content-Security-Policy',
            value: "default-src 'self'; script-src 'self' 'unsafe-inline';",
          },
        ],
      },
    ];
  },
};

Протестируй CSP сначала в режиме отчётности (Content-Security-Policy-Report-Only) — браузер будет собирать нарушения, но не блокировать контент. Когда убедишься, что ничего нужного не блокируется — переключай на боевой режим.