Защита от XSS при работе с вводом

Сценарии работы с вводом в веб-приложениях с использованием Cleave.js часто воспринимаются как исключительно фронтенд-задача форматирования, однако именно на этом уровне нередко формируются условия для XSS-уязвимостей. Форматирование телефонных номеров, дат, валют и произвольных масок меняет поведение DOM-ввода, но не отменяет базовые принципы безопасности пользовательских данных.

Форматирование поля ввода не изменяет факт того, что данные поступают из недоверенного источника. Любое значение, введённое пользователем, должно рассматриваться как потенциально вредоносное.

Типовые угрозы при работе с форматированным вводом:

  • внедрение HTML через последующую вставку значения в DOM;
  • выполнение скриптов при использовании небезопасных API рендера;
  • подмена структуры данных при сериализации/десериализации;
  • накопление опасных символов при маскировании ввода;
  • утечки через атрибуты DOM (например, onerror, onclick).

Особенность Cleave.js заключается в том, что библиотека изменяет представление значения в поле, но не выполняет его очистку с точки зрения безопасности. Это формирует ложное ощущение защищённости.

Форматирование и отсутствие санитарной обработки

Cleave.js работает на уровне отображения и синхронизации значения input. Основной поток данных остаётся неизменным:

  • пользователь вводит исходную строку;
  • библиотека применяет форматирование (маски, разделители, группировки);
  • DOM input обновляется с визуально преобразованным значением.

Критический момент: форматирование не экранирует HTML-сущности и не удаляет опасные конструкции.

Пример опасного значения:

"><img src=x oner ror=alert(1)>

Если такое значение впоследствии используется без обработки, Cleave.js не препятствует его сохранению в DOM или отправке на сервер.

Типичная ошибка: использование innerHTML

Основной источник XSS в связке с форматированными полями — некорректная вставка данных в DOM.

output.innerHTML = input.value;

Даже при использовании Cleave.js значение остаётся пользовательским вводом. При наличии HTML-конструкций происходит интерпретация браузером.

Безопасный вариант:

output.textContent = input.value;

textContent гарантирует трактовку значения как строки, исключая интерпретацию HTML.

Разделение слоёв: форматирование и безопасность

Архитектурно важно разделять:

  • слой отображения (Cleave.js);
  • слой бизнес-логики;
  • слой валидации и санитизации;
  • слой рендеринга.

Cleave.js относится только к первому уровню. Попытка перенести в него ответственность за безопасность приводит к системным уязвимостям.

Санитизация входных данных

Перед сохранением или передачей данных на сервер требуется явная очистка:

  • удаление HTML-тегов;
  • экранирование специальных символов;
  • нормализация Unicode-последовательностей;
  • приведение к ожидаемому формату.

Пример базовой санитизации:

function sanitize(input) {
  return input
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#039;");
}

Важно: Cleave.js не выполняет подобную обработку автоматически.

Опасность работы с raw value

Cleave.js предоставляет доступ к «сырому» значению через API (например, получение unformatted value). Именно это значение чаще всего используется для отправки на сервер.

Типичная ошибка заключается в смешивании:

  • formatted value (для отображения);
  • raw value (для логики);
  • DOM value (для хранения).

Неправильная обработка приводит к тому, что вредоносная строка сохраняется в backend без очистки.

Маски ввода и обходы ограничений

Маски ввода (например, телефонные номера или даты) не являются механизмом защиты.

Пример конфигурации:

new Cleave(input, {
  phone: true,
  phoneRegionCode: 'KZ'
});

Такая маска ограничивает структуру ввода, но не гарантирует отсутствие опасных символов в обходных сценариях:

  • вставка через clipboard;
  • программная подмена value;
  • отправка запроса напрямую, минуя UI;
  • модификация DOM через DevTools.

Безопасная интеграция с DOM

При выводе форматированных данных требуется строгая модель рендеринга:

  1. данные всегда трактуются как строка;
  2. HTML-интерпретация исключается;
  3. любые вставки выполняются через безопасные API.

Пример безопасного обновления UI:

function renderValue(el, value) {
  el.textContent = value;
}

Даже если Cleave.js формирует визуально сложное представление (например, 1 234 567), это значение не должно попадать в HTML через опасные механизмы.

XSS через атрибуты и динамические шаблоны

Опасность возникает не только при innerHTML, но и при формировании атрибутов:

element.setAttribute("data-value", input.value);

Если значение не экранируется, возможно внедрение управляющих последовательностей в контексте других частей DOM.

Особенно критичны сценарии:

  • генерация inline-обработчиков событий;
  • вставка значений в URL без encodeURIComponent;
  • динамическая генерация HTML-строк.

Смешивание форматированного и неформатированного ввода

Cleave.js поддерживает синхронизацию отображаемого и чистого значения. Однако смешивание этих данных в одном контексте приводит к логическим уязвимостям.

Неправильный подход:

const value = input.value;
send(value);

Корректный подход требует явного разделения:

  • formattedValue — для UI;
  • rawValue — для логики и API.

Защита на уровне сервера как обязательный слой

Фронтенд-обработка с Cleave.js не заменяет серверную валидацию.

Сервер должен:

  • повторно валидировать формат;
  • отклонять неожиданные символы;
  • нормализовать вход;
  • игнорировать форматирование UI.

Даже при корректной работе Cleave.js данные могут быть модифицированы до отправки.

Политика безопасности контента

Дополнительным уровнем защиты выступает Content Security Policy (CSP):

  • запрет inline-скриптов;
  • ограничение источников скриптов;
  • блокировка unsafe-eval.

CSP снижает риск эксплуатации XSS даже при наличии ошибок в обработке ввода.

Итоговая модель безопасного использования

При интеграции Cleave.js в систему обработки ввода формируется трёхуровневая модель:

  • Cleave.js отвечает за визуальное форматирование;
  • слой приложения отвечает за очистку и преобразование;
  • сервер отвечает за финальную валидацию и хранение.

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