CSRF и CORS: правильная защита браузерных запросов
Вы создали веб-приложение с чистым API, но затем замечаете странные запросы в логах — или, что хуже, сканер безопасности помечает ваш сайт из-за ошибок в настройках CSRF и CORS. Эти две аббревиатуры часто путают, но они решают разные задачи. Непонимание их различий может сделать ваших пользователей уязвимыми или нарушить легитимные кросс-доменные запросы.
В этой статье мы разберём, что такое CSRF и CORS на самом деле, как они взаимодействуют с безопасностью браузера, и дадим конкретные шаги для защиты ваших приложений без потери функциональности.
Что такое CSRF и почему это важно?
Межсайтовая подделка запроса (CSRF) — это атака, которая заставляет браузер пользователя отправить запрос на сайт, где он аутентифицирован. Представьте, что вы вошли в свой банк на bank.com. Затем вы посещаете вредоносный сайт, содержащий тег изображения вроде <img src="https://bank.com/transfer?to=attacker&amount=1000">. Ваш браузер автоматически включает cookies банка в запрос, и если банк не имеет защиты от CSRF, перевод проходит.
Ключевой момент: CSRF эксплуатирует доверие сайта к браузеру пользователя. Злоумышленнику не нужно красть вашу сессию; ему достаточно, чтобы ваш браузер сделал запрос от вашего имени.
Как работают CSRF-атаки
Для успеха CSRF-атаки должны быть выполнены три условия:
- Жертва должна быть аутентифицирована (например, иметь действующий сессионный cookie).
- Злоумышленник должен знать структуру запроса (endpoint, параметры).
- Запрос не должен содержать непредсказуемых параметров, которые злоумышленник не может угадать.
Обычные цели — операции, изменяющие состояние: смена email, перевод средств, публикация контента или изменение прав доступа.
Что такое CORS и зачем он существует?
Cross-Origin Resource Sharing (CORS) — это механизм браузера, который разрешает или запрещает веб-страницам делать запросы к домену, отличному от того, который обслуживал страницу. Это расширение политики одного источника (Same-Origin Policy, SOP), которая ограничивает взаимодействие документа или скрипта, загруженного из одного источника, с ресурсами из другого источника.
Без CORS вредоносный сайт мог бы использовать JavaScript для чтения данных из API вашего банка, если вы авторизованы. CORS даёт серверам способ явно разрешать определённые кросс-доменные запросы.
Политика одного источника (SOP)
Два URL имеют один источник, если совпадают протокол, хост и порт. Например, https://example.com/app и https://example.com/api имеют один источник, а http://example.com (другой протокол) и https://api.example.com (другой хост) — нет.
SOP запрещает скриптам из одного источника читать ответы из другого источника. CORS ослабляет это ограничение, добавляя HTTP-заголовки, которые сообщают браузеру, разрешать ли запрос.
CSRF vs CORS: ключевые различия
Важно понимать, что CSRF и CORS не противоположны; они решают разные задачи безопасности. CSRF — о предотвращении несанкционированных запросов, изменяющих состояние, а CORS — о контроле того, какие источники могут читать ответы.
| Аспект | CSRF | CORS |
|---|---|---|
| Основная цель | Предотвратить поддельные запросы | Контролировать кросс-доменное чтение |
| Вектор атаки | Вредоносный сайт инициирует запросы | Вредоносный сайт читает ответы |
| Механизм защиты | Токены, SameSite cookies | HTTP-заголовки (Access-Control-*) |
| Принудительно браузером | Нет (сервер должен проверять) | Да (браузер блокирует чтение) |
Как защититься от CSRF
Существует несколько проверенных стратегий для смягчения CSRF. Вам не нужны все, но многоуровневая защита разумна.
1. Используйте анти-CSRF токены
Самая надёжная защита — включить уникальный, непредсказуемый токен в каждый запрос, изменяющий состояние. Сервер проверяет токен перед обработкой. Токены должны быть привязаны к сессии пользователя и не раскрываться в URL (чтобы избежать утечки через заголовки referrer).
// Пример: генерация и проверка CSRF-токена в Node.js/Express
const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });
app.get('/form', csrfProtection, (req, res) => {
res.render('form', { csrfToken: req.csrfToken() });
});
app.post('/process', csrfProtection, (req, res) => {
// Токен проверяется автоматически
res.send('OK');
});
2. Установите SameSite cookies
Современные браузеры поддерживают атрибут SameSite для cookies. Установка его в Lax или Strict предотвращает отправку cookies браузером при кросс-сайтовых запросах, что блокирует многие CSRF-атаки. Lax разрешает cookies при навигации верхнего уровня (например, клик по ссылке), а Strict блокирует все кросс-сайтовые запросы.
Set-Cookie: sessionId=abc123; SameSite=Lax; Secure; HttpOnly
3. Проверяйте заголовки Origin и Referer
Проверяйте заголовок Origin или Referer во входящих запросах. Если они не совпадают с ожидаемым доменом, отклоняйте запрос. Это простой, но эффективный дополнительный уровень защиты.
4. Используйте кастомные заголовки для AJAX
Если ваш API используется через JavaScript, требуйте кастомный заголовок вроде X-Requested-With. Браузеры применяют CORS preflight для кастомных заголовков, поэтому простой POST-запрос формы с вредоносного сайта не будет его содержать.
Правильная настройка CORS
Неправильная конфигурация CORS также может привести к проблемам безопасности. Самая распространённая ошибка — установка Access-Control-Allow-Origin: * при одновременном разрешении учётных данных. Такая комбинация запрещена браузерами, но разработчики иногда пытаются обойти это небезопасным способом.
Лучшие практики для заголовков CORS
- Указывайте точные источники вместо wildcard, когда задействованы учётные данные.
- Ограничьте разрешённые методы только теми, которые поддерживает ваш API (например,
GET, POST). - Ограничьте разрешённые заголовки теми, которые действительно использует ваше приложение.
- Установите
Access-Control-Max-Ageдля кэширования preflight-ответов и снижения накладных расходов. - Избегайте слепого отражения заголовка
Origin; проверяйте его по белому списку.
# Пример: конфигурация Nginx для CORS
location /api/ {
if ($http_origin ~* (https://(app|admin)\.example\.com)) {
add_header 'Access-Control-Allow-Origin' $http_origin;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
}
if ($request_method = 'OPTIONS') {
return 204;
}
}
Помните, что CORS применяется браузером, а не сервером. Это не замена аутентификации или авторизации; это способ безопасно ослабить политику одного источника.
Собираем всё вместе
Защита браузерных запросов требует многоуровневого подхода. Вот краткий чек-лист:
- Используйте анти-CSRF токены для всех операций, изменяющих состояние.
- Установите SameSite cookies в
LaxилиStrict. - Проверяйте заголовки Origin/Referer на сервере.
- Настройте CORS точно: белый список источников, ограничение методов и отказ от wildcard с учётными данными.
- Регулярно тестируйте ваше приложение сканерами безопасности и вручную.
Понимая различные роли CSRF и CORS, вы сможете избежать распространённых ошибок и создать более безопасное веб-приложение.
FAQ
Может ли CORS предотвратить CSRF-атаки?
Нет, CORS не предотвращает CSRF. CSRF-атаки не полагаются на чтение ответов; они просто инициируют запросы. CORS контролирует, какие источники могут читать ответы, но не блокирует отправку запроса. Для предотвращения CSRF нужны токены, SameSite cookies или проверка источника.
Безопасно ли устанавливать Access-Control-Allow-Origin в '*'?
Установка '*' безопасна только если ваш API не использует учётные данные (cookies, HTTP-аутентификацию). Если задействованы учётные данные, браузеры отклонят ответ. Для аутентифицированных API всегда указывайте точные источники.
Нужна ли мне защита от CSRF, если я использую JWT в заголовках Authorization?
Если вы храните JWT в cookies, защита от CSRF всё ещё нужна, потому что cookies отправляются автоматически. Если вы храните JWT в памяти и отправляете их через заголовок Authorization, CSRF не является проблемой, поскольку злоумышленник не может установить кастомные заголовки кросс-доменно. Однако вы должны защищаться от XSS, чтобы сохранить токен в безопасности.
Готовы протестировать заголовки CORS вашего API? Используйте наш Nginx Log Analyzer для анализа шаблонов запросов и выявления подозрительных кросс-доменных попыток.