Санитизация пользовательского ввода

При использовании Flatpickr ключевой источник риска — не сам календарь, а путь данных от пользователя к приложению. Компонент формирует строковое представление даты, но при включённом ручном вводе или кастомных форматах эта строка становится внешним вводом, который может попасть в DOM, бизнес-логику или запросы к серверу без дополнительной обработки.

Основные угрозы:

  • XSS через подмену input value при небезопасной вставке значения в DOM
  • Инъекции логического уровня (подмена даты, диапазона, формата)
  • Несоответствие форматов между клиентом и сервером
  • Обход ограничений UI через ручной ввод или paste-события

Flatpickr сам по себе не является валидатором безопасности. Он лишь формирует интерфейс выбора даты.


Разделение отображаемого и фактического значения

Одной из базовых защитных практик является разделение:

  • altInput — человекочитаемое отображение
  • altFormat — формат для отображения пользователю
  • dateFormat — формат внутреннего значения

При использовании:

flatpickr("#date", {
  altInput: true,
  altFormat: "d.m.Y",
  dateFormat: "Y-m-d"
});

в DOM появляется два поля:

  • визуальное (альтернативное)
  • скрытое (истинное значение)

Ключевой момент: любое последующее использование должно опираться только на dateFormat, а не на отображаемый input.


Ручной ввод как основной источник неконтролируемого состояния

Параметр:

allowInput: true

открывает возможность ввода строки напрямую, минуя календарь. Именно здесь чаще всего возникает рассинхронизация состояния.

Типичные проблемы:

  • ввод невалидной даты (32.13.2025)
  • частично корректные строки (2025-..-01)
  • внедрение символов, влияющих на парсинг
  • обход ограничений min/max

Нормализация входных данных через parseDate

Flatpickr позволяет переопределить парсинг:

parseDate: (str, format) => {
  return new Date(str);
}

Но такой подход опасен без явной нормализации.

Корректная стратегия:

  • жёсткое соответствие формату
  • отказ от невалидных строк
  • отсутствие «догадок» при парсинге

Пример более строгой логики:

parseDate: (str) => {
  const match = str.match(/^(\d{4})-(\d{2})-(\d{2})$/);
  if (!match) return null;

  const [_, y, m, d] = match;
  const date = new Date(`${y}-${m}-${d}T00:00:00`);
  return isNaN(date) ? null : date;
}

Валидация через onChange и onClose

События:

  • onChange
  • onClose
  • onValueUpdate

используются как точки контроля целостности данных.

Пример концепции:

  • onChange — первичная проверка формата
  • onClose — финальная валидация состояния
  • onValueUpdate — синхронизация с моделью данных

Важно: события не заменяют валидацию, а лишь сигнализируют о необходимости проверки.


Ограничение диапазона как форма санитизации

Параметры:

  • minDate
  • maxDate
  • disable

используются для создания белого списка допустимых значений.

Пример:

flatpickr("#date", {
  minDate: "2025-01-01",
  maxDate: "2026-01-01"
});

Это не только UX-ограничение, но и механизм первичной фильтрации ввода.


Обработка вставки (paste) и внешнего ввода

Даже при наличии календаря пользователь может вставить значение напрямую.

Основные сценарии риска:

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

Стратегия защиты:

  • перехват input события
  • принудительная нормализация значения
  • откат при невалидном формате

DOM-инъекции через некорректную обработку значения

Flatpickr не вставляет HTML в DOM, но приложение может сделать это позже:

element.innerHTML = fp.input.value;

Если значение не экранируется, возникает риск XSS.

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

  • textContent вместо innerHTML
  • явное кодирование перед вставкой
  • запрет интерпретации строки как HTML

Серверная валидация как обязательный слой

Клиентская санитизация не является гарантией безопасности.

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

  • повторно парсить дату
  • проверять диапазон
  • игнорировать форматирование клиента
  • работать только с ISO-подобными строками

Пример допустимого формата:

  • YYYY-MM-DD
  • YYYY-MM-DDTHH:mm:ssZ (при необходимости времени)

Локализация и нестабильные форматы

При использовании локалей формат ввода может меняться:

  • dd.mm.yyyy
  • mm/dd/yyyy
  • текстовые месяцы

Это увеличивает риск:

  • неправильного парсинга
  • неоднозначности значений
  • обхода логики фильтрации

Решение — всегда фиксировать dateFormat независимо от локали отображения.


Защита через отказ от доверия к UI-слою

Flatpickr следует рассматривать как:

  • интерфейс выбора
  • генератор строки
  • визуальный контроллер

Но не как:

  • валидатор
  • фильтр безопасности
  • источник истины

Любая бизнес-логика должна опираться на:

  • нормализованную дату (Date object или ISO string)
  • серверную проверку
  • явные ограничения диапазонов

Поведение при программной подмене значения

Пример уязвимого сценария:

document.querySelector("#date").value = "<script>alert(1)</script>";

Flatpickr не интерпретирует это как HTML, но дальнейшая логика приложения может.

Механизмы защиты:

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

Стратегия многоуровневой санитизации

Надёжная модель включает несколько слоёв:

  1. UI-слой Flatpickr

    • ограничение выбора
    • UX-подсказки
  2. Клиентская валидация

    • parseDate
    • onChange фильтрация
    • проверка диапазона
  3. Приложение

    • нормализация формата
    • преобразование в Date/ISO
  4. Сервер

    • финальная проверка
    • игнорирование входных форматов клиента

Обработка ошибок и невалидных состояний

Любое отклонение должно приводить к предсказуемому поведению:

  • сброс значения
  • откат к последнему валидному состоянию
  • блокировка отправки формы

Недопустимо:

  • «мягкое принятие» некорректных дат
  • автоматическое исправление без логики
  • скрытое преобразование неоднозначных строк