Защита веб-приложений: принцип работы Fetch Metadata Headers
#Веб-разработка #Безопасность (XSS/CSRF) #Веб-безопасностьДля кого эта статья:
- Разработчики веб-приложений
- Специалисты по информационной безопасности
- Профессионалы в области DevSecOps и системного администрирования
Разработчики веб-приложений постоянно находятся в гонке вооружений с злоумышленниками. Каждая новая защитная техника сталкивается с попытками обхода, создавая цикл непрекращающихся атак и контрмер. Fetch Metadata Headers представляют собой передовой подход защиты веб-приложений, позволяющий серверам принимать обоснованные решения о запросах на основе их контекста. Эта технология может стать мощным барьером против целого класса кросс-сайтовых атак, которые годами преследуют веб-сайты. Разбираемся, как использовать эти заголовки для создания по-настоящему защищенных приложений. 🔒
Что такое Fetch Metadata Headers и их роль в безопасности
Fetch Metadata Headers — это набор HTTP-заголовков, отправляемых браузером при выполнении запроса, которые передают серверу контекстную информацию об источнике и назначении запроса. Они помогают серверу понять, почему и как был инициирован запрос, что критически важно для блокировки потенциально вредоносных межсайтовых запросов.
Ключевым отличием от традиционных механизмов защиты является то, что Fetch Metadata Headers не требуют ручного поддержания списков источников или установки токенов. Вместо этого они используют контекстные данные, автоматически передаваемые браузером.
Алексей Соколов, руководитель отдела веб-безопасности
В 2021 году наша команда столкнулась с постоянными атаками на корпоративный портал, несмотря на внедрение стандартных механизмов защиты, включая CSRF-токены и заголовки CSP. Злоумышленники находили обходные пути через устаревшие API и легаси-эндпоинты. Ситуация стала критической, когда была скомпрометирована система загрузки внутренних документов.
После аудита безопасности мы решили внедрить Fetch Metadata Headers как дополнительный уровень защиты. Задача казалась тривиальной, но потребовала двухнедельного анализа всех возможных сценариев использования портала, включая интеграции с внешними сервисами. После внедрения Resource Isolation Policy количество подозрительных запросов упало до нуля, а производительность системы даже улучшилась за счет отсечения ненужного трафика.
Fetch Metadata заголовки делятся на несколько ключевых типов:
- Sec-Fetch-Site — указывает отношение между источником и целью запроса (same-origin, same-site, cross-site, none)
- Sec-Fetch-Mode — определяет режим запроса (navigate, cors, no-cors, same-origin, websocket)
- Sec-Fetch-Dest — определяет тип ресурса, который запрашивается (document, image, script, style, font и др.)
- Sec-Fetch-User — указывает, был ли запрос инициирован пользовательским взаимодействием (?1)
Важно понимать, что эти заголовки добавляются браузером автоматически и не могут быть подделаны скриптами злоумышленников, что обеспечивает высокую степень надежности. 🛡️
| Заголовок | Возможные значения | Применение в безопасности |
|---|---|---|
| Sec-Fetch-Site | same-origin, same-site, cross-site, none | Блокировка нежелательных кросс-сайтовых запросов |
| Sec-Fetch-Mode | navigate, cors, no-cors, same-origin, websocket | Ограничение способов доступа к ресурсам |
| Sec-Fetch-Dest | document, image, script, style, font, etc. | Контроль контекста использования ресурса |
| Sec-Fetch-User | ?1, null | Разделение автоматизированных и пользовательских действий |

Архитектура и технический принцип работы заголовков
Архитектура Fetch Metadata Headers базируется на модели безопасности современных браузеров и концепции Same-Origin Policy. Когда браузер отправляет запрос к серверу, он автоматически добавляет заголовки Sec-Fetch-* с информацией о контексте этого запроса.
Процесс работы этого механизма включает несколько этапов:
- Создание запроса — При инициации запроса (через элементы HTML, JavaScript API или навигацию) браузер определяет его контекст
- Добавление заголовков — Браузер автоматически добавляет соответствующие Sec-Fetch-* заголовки на основе контекста
- Отправка на сервер — Запрос с заголовками отправляется на сервер
- Анализ на сервере — Сервер оценивает контекст запроса по полученным заголовкам
- Принятие решения — На основе заголовков и определенной политики, сервер принимает решение о выполнении или блокировке запроса
Технически, отправка Fetch Metadata заголовков выглядит следующим образом в сетевом потоке:
GET /api/sensitive-data HTTP/1.1
Host: example.com
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
...
Ключевой особенностью этих заголовков является их неподделываемость. Хотя JavaScript может создавать пользовательские HTTP-заголовки, браузеры не позволяют устанавливать, изменять или удалять заголовки с префиксом "Sec-", что делает их надежным источником информации для сервера.
Рассмотрим технические детали каждого заголовка:
| Заголовок | Технические детали | Пример использования |
|---|---|---|
| Sec-Fetch-Site | Определяется путем сравнения истоков (схема + хост + порт) запроса и целевого URL | Блокировка запросов к API, если Sec-Fetch-Site=cross-site |
| Sec-Fetch-Mode | Основан на типе запроса и атрибутах, используемых для его инициации | Разрешение только режима navigate для страниц аутентификации |
| Sec-Fetch-Dest | Указывает предполагаемое место использования запрашиваемого ресурса | Ограничение загрузки скриптов только с атрибутом dest=script |
| Sec-Fetch-User | Присутствует только если запрос вызван пользовательским действием | Требование наличия user=?1 для критических операций |
Поддержка Fetch Metadata Headers реализована во всех современных браузерах на базе Chromium (Chrome, Edge, Opera) начиная с версии 76, а также в Firefox начиная с версии 90. Safari пока не имеет полной поддержки этой функциональности. 🌐
Защита от кросс-сайтовых атак с помощью метаданных
Fetch Metadata Headers предоставляют эффективную защиту от широкого спектра кросс-сайтовых атак, которые десятилетиями преследуют веб-приложения. Вместо поиска и устранения отдельных уязвимостей, этот подход блокирует целые классы атак, основываясь на контексте запроса.
Рассмотрим основные типы атак, от которых защищают Fetch Metadata заголовки:
- Cross-Site Request Forgery (CSRF) — злоумышленник вынуждает аутентифицированного пользователя выполнить нежелательное действие
- Cross-Site Script Inclusion (XSSI) — похищение чувствительных данных через включение JSON или JavaScript файлов в контекст атакующего сайта
- Timing attacks — извлечение информации через анализ времени ответа на различные запросы
- Cross-Origin Resource Sharing (CORS) misconfiguration — эксплуатация ошибок в настройке политик CORS
- Spectre-based side-channel attacks — атаки на микроархитектурном уровне, использующие спекулятивное выполнение
Принцип защиты основан на том, что большинство легитимных запросов веб-приложения попадают в определённые шаблоны использования, а атаки обычно выходят за рамки этих шаблонов.
Марина Волкова, консультант по информационной безопасности
Мой клиент, крупный финансовый сервис, обратился с проблемой — их система API была уязвима для CSRF атак, несмотря на использование токенов. При анализе выяснилось, что токены верифицировались не на всех эндпоинтах из-за ошибок в коде.
Мы разработали стратегию внедрения Fetch Metadata Headers в качестве дополнительного уровня защиты. Сначала провели мониторинг всех запросов в течение недели, записывая значения Sec-Fetch-* заголовков в логи без блокирования. Анализ логов позволил выявить легитимные шаблоны использования API.
Затем мы постепенно внедрили Resource Isolation Policy — сначала на тестовой среде, затем на 10% продакшн-трафика, и наконец полностью. Через месяц после внедрения был зафиксирован и автоматически предотвращен реальный CSRF-атака на эндпоинт перевода средств, который раньше был уязвим. Ценность этого подхода была в том, что он защитил даже те эндпоинты, о которых разработчики забыли или еще не реализовали.
Для защиты от этих атак обычно используют две основные политики безопасности на основе Fetch Metadata:
- Resource Isolation Policy — блокирует все кросс-сайтовые запросы к не публичным ресурсам, кроме навигационных запросов и запросов из разрешенных источников
- Navigation Isolation Policy — более строгая политика, которая дополнительно проверяет, что навигационные запросы выполняются только через разрешенные векторы
Пример реализации простой Resource Isolation Policy на сервере (псевдокод):
function shouldBlockRequest(request) {
// Игнорируем запросы без Fetch Metadata (устаревшие браузеры)
if (!request.headers['sec-fetch-site']) {
return false;
}
// Пропускаем same-site и same-origin запросы
if (request.headers['sec-fetch-site'] === 'same-origin' ||
request.headers['sec-fetch-site'] === 'same-site') {
return false;
}
// Пропускаем навигационные запросы и запросы с preflight
if (request.headers['sec-fetch-mode'] === 'navigate' ||
request.headers['sec-fetch-mode'] === 'websocket' ||
request.method === 'OPTIONS') {
return false;
}
// Блокируем все остальные cross-site запросы
return true;
}
Эта защита особенно эффективна против CSRF атак, так как злоумышленники обычно используют cross-site запросы, которые не являются навигационными (значения Sec-Fetch-Site: cross-site и Sec-Fetch-Mode отличное от navigate). 🛑
Реализация политик безопасности на основе Fetch Metadata
Внедрение политик безопасности на основе Fetch Metadata Headers требует системного подхода и тщательного планирования. Рассмотрим пошаговый процесс реализации:
- Аудит трафика — сбор данных о типичных паттернах использования вашего приложения
- Определение защищаемых ресурсов — разделение ресурсов на публичные и защищаемые
- Выбор политики — выбор между Resource Isolation Policy и Navigation Isolation Policy
- Тестирование без блокировки — внедрение логики обнаружения без фактической блокировки запросов
- Анализ логов — изучение потенциально заблокированных запросов для выявления ложных срабатываний
- Постепенное внедрение — поэтапный запуск блокировки на небольшом проценте трафика
- Полное развертывание — активация политики для всего трафика с мониторингом и настройкой
При реализации политик безопасности важно учитывать исключения для легитимных сценариев. Вот основные категории исключений, которые следует учесть:
- Публичные API, предназначенные для использования другими сайтами
- Интеграции с третьими сторонами, которые должны отправлять запросы к вашим эндпоинтам
- Устаревшие браузеры без поддержки Fetch Metadata Headers
- CDN и ресурсы для загрузки, которые должны быть доступны из любого контекста
- Webhooks и обратные вызовы, инициируемые внешними системами
Пример более сложной реализации Resource Isolation Policy с учетом исключений (Node.js):
function applyResourceIsolationPolicy(req, res, next) {
// Список публичных путей, которые не требуют защиты
const publicPaths = ['/api/public', '/cdn', '/assets'];
// Проверяем, является ли путь публичным
if (publicPaths.some(path => req.path.startsWith(path))) {
return next();
}
// Пропускаем браузеры, которые не поддерживают Fetch Metadata
if (!req.headers['sec-fetch-site']) {
return next();
}
// Пропускаем same-origin и same-site запросы
if (['same-origin', 'same-site'].includes(req.headers['sec-fetch-site'])) {
return next();
}
// Пропускаем навигационные запросы
if (req.headers['sec-fetch-mode'] === 'navigate' &&
req.headers['sec-fetch-dest'] === 'document') {
return next();
}
// Пропускаем запросы preflight
if (req.method === 'OPTIONS') {
return next();
}
// Блокируем потенциально опасные кросс-сайтовые запросы
return res.status(403).send('Cross-site request blocked by policy');
}
Дополнительно рекомендуется использовать механизм отчетов для отслеживания заблокированных запросов, особенно на этапе внедрения. Это поможет выявить ложные срабатывания и уточнить политику. ⚙️
| Политика | Защищаемые атаки | Сложность внедрения | Потенциальное влияние |
|---|---|---|---|
| Resource Isolation Policy | CSRF, XSSI, Timing attacks | Средняя | Минимальное для большинства приложений |
| Navigation Isolation Policy | CSRF, XSSI, Timing attacks, Clickjacking | Высокая | Может затронуть легитимные фреймы и встраивания |
| Комбинированный подход | Максимальная защита от кросс-сайтовых атак | Очень высокая | Требует тщательной настройки и тестирования |
Практическое внедрение заголовков в веб-приложения
Практическая имплементация Fetch Metadata Headers зависит от используемого фреймворка и серверной платформы. Рассмотрим примеры реализации для популярных серверных стеков.
Для Express.js (Node.js) middleware:
// Middleware для реализации Resource Isolation Policy
app.use((req, res, next) => {
// Исключаем пути, не требующие защиты
if (isPublicPath(req.path)) {
return next();
}
const site = req.headers['sec-fetch-site'];
const mode = req.headers['sec-fetch-mode'];
// Пропускаем запросы без заголовков (старые браузеры)
if (!site) {
return next();
}
// Пропускаем same-origin и same-site запросы
if (site === 'same-origin' || site === 'same-site') {
return next();
}
// Пропускаем навигационные запросы
if (mode === 'navigate') {
return next();
}
// Пропускаем preflight запросы
if (req.method === 'OPTIONS') {
return next();
}
// Логируем и блокируем подозрительные запросы
console.log(`Blocked cross-site request: ${req.method} ${req.path}`);
return res.status(403).json({ error: 'Cross-site request blocked' });
});
Для Django (Python):
# middleware.py
class ResourceIsolationPolicyMiddleware:
def __init__(self, get_response):
self.get_response = get_response
self.public_paths = ['/api/public', '/static', '/media']
def __call__(self, request):
# Проверяем, является ли путь публичным
if any(request.path.startswith(path) for path in self.public_paths):
return self.get_response(request)
# Получаем Fetch Metadata заголовки
site = request.META.get('HTTP_SEC_FETCH_SITE')
mode = request.META.get('HTTP_SEC_FETCH_MODE')
# Пропускаем запросы без заголовков
if not site:
return self.get_response(request)
# Пропускаем same-origin и same-site запросы
if site in ['same-origin', 'same-site']:
return self.get_response(request)
# Пропускаем навигационные запросы
if mode == 'navigate':
return self.get_response(request)
# Пропускаем preflight запросы
if request.method == 'OPTIONS':
return self.get_response(request)
# Блокируем потенциально опасные кросс-сайтовые запросы
return HttpResponseForbidden('Cross-site request blocked by policy')
При внедрении рекомендуется следовать следующим практическим советам:
- Используйте постепенное внедрение — начните с режима мониторинга без блокировки
- Сегментируйте приложение — разделите ресурсы на категории с разными уровнями защиты
- Создайте белый список исключений — поддерживайте список разрешенных кросс-сайтовых запросов
- Реализуйте систему уведомлений — настройте оповещения о заблокированных запросах
- Тестируйте интеграции — убедитесь, что все интеграции с третьими сторонами работают корректно
- Документируйте политику — опишите применяемую политику для разработчиков и партнеров
Один из наиболее эффективных подходов — комбинирование Fetch Metadata Headers с другими механизмами защиты:
- Content-Security-Policy (CSP) — для контроля загрузки ресурсов
- Cross-Origin Resource Sharing (CORS) — для управления кросс-доменными запросами
- SameSite cookie flags — для контроля отправки куки в кросс-сайтовых контекстах
- CSRF токены — как дополнительный уровень защиты для критических операций
- Subresource Integrity (SRI) — для проверки целостности загружаемых ресурсов
Мониторинг и оценка эффективности после внедрения также критически важны. Ключевые метрики для отслеживания:
- Количество заблокированных запросов (с разбивкой по типам)
- Частота ложных срабатываний и их причины
- Влияние на производительность (задержки в обработке запросов)
- Количество инцидентов безопасности до и после внедрения
Правильно реализованные Fetch Metadata Headers становятся практически незаметным для пользователей уровнем защиты, при этом значительно повышая безопасность всего приложения. 🔐
Fetch Metadata Headers предлагают принципиально новый подход к защите веб-приложений, основанный на контексте запросов, а не на традиционных шаблонах безопасности. Их внедрение позволяет защититься от целого класса кросс-сайтовых атак, даже тех, которые еще не известны. Ценность этой технологии — в её неинвазивности и широком охвате. Вместо защиты каждого эндпоинта по отдельности, вы устанавливаете общий барьер для всех потенциально вредоносных запросов. Взгляните на безопасность вашего приложения с позиции контекста взаимодействия, а не только содержимого запросов — именно этот подход делает защиту по-настоящему эффективной.
Элина Баранова
разработчик Android