CSRF-защита при отправке данных

При использовании компонентов выбора данных на стороне клиента основная опасность возникает не в самом интерфейсе, а в момент передачи выбранных значений на сервер. Choices.js часто применяется поверх стандартных <select> и <input> элементов, расширяя их функциональность: мультивыбор, динамический поиск, создание новых опций и асинхронная подгрузка.

С точки зрения CSRF (Cross-Site Request Forgery), уязвимость формируется там, где происходит HTTP-запрос, изменяющий состояние системы: создание сущностей, обновление профиля, отправка форм, добавление элементов в корзину. Любая интеграция Choices.js с fetch, XMLHttpRequest или библиотеками запросов превращает UI-выбор в потенциальный канал атаки, если отсутствует проверка происхождения запроса.

CSRF возникает в сценарии, когда браузер автоматически прикладывает cookie авторизации к запросу, инициированному с чужого сайта. Если сервер не различает легитимный и поддельный запрос, выбранные через Choices.js данные могут быть отправлены злоумышленником от имени пользователя.


Базовая модель угроз при работе с динамическими формами

Интерактивные формы с Choices.js часто включают:

  • мультиселект категорий;
  • выбор тегов;
  • создание пользовательских значений;
  • автодополнение через API;
  • отправку данных без перезагрузки страницы.

Каждый из этих сценариев обычно завершается AJAX-запросом:

  • POST для создания сущности;
  • PUT/PATCH для обновления;
  • DELETE для удаления.

CSRF-атака становится возможной, когда:

  • запрос аутентифицируется только cookie;
  • отсутствует проверка CSRF-токена;
  • отсутствует проверка Origin/Referer;
  • endpoint доступен извне без ограничения источника.

CSRF-токен как обязательный элемент отправки данных

Наиболее распространённый механизм защиты — CSRF-токен. Он добавляется в форму или передаётся через заголовки при каждом изменяющем запросе.

Генерация токена на сервере

В серверных фреймворках токен создаётся автоматически:

  • Django: middleware CSRF;
  • Laravel: csrf_token();
  • Express: middleware csurf;
  • Spring Security: встроенная CSRF-защита.

Токен передаётся в HTML:

<meta name="csrf-token" content="ABC123TOKEN">

Интеграция CSRF с AJAX-запросами из Choices.js

Основной риск возникает в момент сериализации выбранных значений и отправки их на сервер.

Пример получения значений из Choices.js

const element = document.querySelector('#tags');
const choices = new Choices(element, {
  removeItemButton: true,
  maxItemCount: 5
});

Извлечение выбранных значений:

const selectedValues = choices.getValue(true);

Добавление CSRF-токена в fetch-запрос

При отправке данных, собранных через Choices.js, токен должен передаваться явно:

const token = document.querySelector('meta[name="csrf-token"]').getAttribute('content');

fetch('/api/tags', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-CSRF-Token': token
  },
  body: JSON.stringify({
    tags: selectedValues
  })
});

Ключевой момент заключается в том, что браузер не добавляет CSRF-токен автоматически. Его необходимо прикреплять ко всем изменяющим запросам.


Некоторые архитектуры используют двойную модель:

  • CSRF-токен хранится в cookie;
  • тот же токен передаётся в заголовке запроса.

Пример извлечения cookie:

function getCookie(name) {
  const value = `; ${document.cookie}`;
  const parts = value.split(`; ${name}=`);
  if (parts.length === 2) return parts.pop().split(';').shift();
}

Использование:

const csrfToken = getCookie('csrf_token');

fetch('/api/update', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-CSRF-Token': csrfToken
  },
  body: JSON.stringify({
    items: choices.getValue(true)
  })
});

Проверка Origin и Referer как дополнительный слой

CSRF-защита не должна ограничиваться только токеном. Серверная проверка заголовков:

  • Origin
  • Referer

позволяет отсеивать запросы с внешних доменов.

Пример логики:

если Origin отсутствует → отклонить
если Origin != trusted-domain → отклонить

Такая проверка особенно полезна при работе с API, которые обслуживают формы, построенные на Choices.js.


Сценарий атаки на динамический мультиселект

Типичный уязвимый сценарий:

  1. пользователь авторизован;
  2. страница содержит Choices.js компонент;
  3. отправка происходит через POST без CSRF-защиты;
  4. злоумышленник создаёт страницу с автоматическим POST-запросом;
  5. браузер жертвы отправляет cookie автоматически;
  6. сервер принимает запрос как легитимный.

Особенно опасны случаи, где Choices.js используется для:

  • назначения ролей;
  • изменения email;
  • добавления прав доступа;
  • управления платежными данными.

Защита на уровне REST API

Для API, работающих с Choices.js, применяются следующие практики:

1. CSRF-токен для session-based auth

Используется при cookie-аутентификации.

CSRF становится менее актуальным, если:

  • токен хранится в Authorization: Bearer.

Однако остаётся риск XSS, который может украсть токен.


Пример безопасного эндпоинта (Node.js + Express)

app.post('/api/tags', csrfProtection, (req, res) => {
  const tags = req.body.tags;

  if (!Array.isArray(tags)) {
    return res.status(400).send('Invalid payload');
  }

  saveTags(req.user.id, tags);
  res.json({ status: 'ok' });
});

Связь CSRF и динамического создания опций в Choices.js

Choices.js позволяет пользователю создавать новые значения (createItem: true). Это увеличивает поверхность атаки:

  • можно отправить неожиданные строки;
  • можно внедрить вредоносные payload’ы;
  • можно перегрузить сервер массовыми запросами.

CSRF-защита должна применяться одинаково ко всем типам значений, включая:

  • пользовательские строки;
  • автогенерируемые элементы;
  • значения из API.

Санитизация данных как часть защиты

CSRF не заменяет валидацию. После получения данных из Choices.js необходимо:

  • проверять тип данных;
  • ограничивать длину строк;
  • фильтровать HTML/JS;
  • валидировать по whitelist.

Пример:

function validateTags(tags) {
  return tags
    .filter(t => typeof t === 'string')
    .filter(t => t.length < 30)
    .map(t => t.trim());
}

Итоговая модель безопасной отправки данных

Комбинация мер:

  • CSRF-токен в заголовке;
  • проверка Origin;
  • строгая серверная валидация;
  • ограничение типов данных;
  • контроль API-эндпоинтов;
  • корректная интеграция с Choices.js при каждом AJAX-запросе.

Такая модель закрывает основной класс атак, возникающих при отправке данных из динамических интерфейсов выбора.