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

Роль санитизации при работе с локализованными данными

Обработка пользовательского ввода в многоязычных приложениях требует учета различий в форматах чисел, дат, валют и текстовых сообщений. Библиотека Globalize опирается на данные CLDR (Unicode Common Locale Data Repository), что позволяет выполнять корректное форматирование и парсинг значений в зависимости от локали. При этом сама по себе локализация не решает задачу безопасности и целостности данных, поэтому санитизация становится отдельным слоем обработки.

Санитизация в данном контексте включает:

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

Локаль и влияние на интерпретацию данных

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

  • десятичный разделитель может быть «.» или «,»;
  • разделитель тысяч может отсутствовать или быть пробелом;
  • порядок дня, месяца и года отличается;
  • используются разные календари и форматы времени.

Globalize учитывает эти особенности при разборе строк:

Globalize.load(
  require("cldr-data").entireSupplemental(),
  require("cldr-data").entireMainFor("ru")
);

const globalize = new Globalize("ru");

Парсинг чисел выполняется с учетом локали:

const number = globalize.numberParser()("1 234,56");

Без корректной санитизации входных данных подобные операции могут давать неожиданные результаты, если строка содержит лишние символы или смешанные форматы.

Нормализация числового ввода

Числовой ввод часто поступает из HTML-форм и может содержать пробелы, неразрывные пробелы, символы валют или текстовые суффиксы. Перед передачей в парсер Globalize выполняется нормализация.

Типовые шаги:

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

Пример предварительной обработки:

function sanitizeNumberInput(input) {
  return input
    .replace(/\s/g, " ")
    .replace(/[^\d.,\- ]/g, "")
    .trim();
}

Далее результат передается в парсер:

const parser = globalize.numberParser();
const value = parser(sanitizeNumberInput(" 1 234,56 ₽ "));

Санитизация здесь обеспечивает предсказуемость интерпретации строки независимо от источника ввода.

Работа с датами и неоднозначными форматами

Парсинг дат в Globalize зависит от локали и заданного шаблона. Входные данные могут содержать неоднозначные значения, например «01/02/2024», которые интерпретируются по-разному в зависимости от региона.

const dateParser = globalize.dateParser({ skeleton: "yMd" });
const date = dateParser("01.02.2024");

Санитизация дат включает:

  • удаление лишних символов (например, текста вокруг даты);
  • унификацию разделителей;
  • приведение к ожидаемому шаблону;
  • контроль диапазонов значений (день ≤ 31, месяц ≤ 12).

Пример нормализации:

function sanitizeDateInput(input) {
  return input
    .replace(/[^\d./\- ]/g, "")
    .replace(/\s+/g, "")
    .trim();
}

Даже при наличии корректного парсера Globalize входные данные без предварительной очистки могут приводить к ошибкам разбора или неверной интерпретации даты.

Контроль форматов при использовании форматтеров

Globalize предоставляет форматтеры для чисел и дат:

const formatter = globalize.numberFormatter({ minimumFractionDigits: 2 });
formatter(1234.5);

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

Основная задача — исключить ситуации, когда в форматтер попадают:

  • строки вместо чисел;
  • NaN или undefined;
  • частично очищенные данные.

Пример защитной обработки:

function safeNumberFormat(value) {
  const num = Number(value);
  if (!Number.isFinite(num)) {
    return null;
  }
  return globalize.numberFormatter()(num);
}

Санитизация сообщений и интерполяции

Globalize поддерживает форматирование сообщений с параметрами:

const msgFormatter = globalize.messageFormatter("balance");
msgFormatter({ amount: 1000 });

При работе с пользовательскими данными внутри сообщений важна защита от некорректных значений параметров. Санитизация включает:

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

Пример подготовки параметров:

function sanitizeMessageParams(params) {
  return {
    amount: Number(params.amount) || 0,
    name: String(params.name || "").replace(/[<>]/g, "")
  };
}

Защита от некорректных локализованных строк

Локализованные строки могут содержать символы, которые изменяют структуру числа или даты. Например:

  • неразрывные пробелы (U+00A0);
  • узкие пробелы (U+202F);
  • направление текста (RTL-метки);
  • смешанные системы записи.

Для повышения устойчивости обработки выполняется Unicode-нормализация:

function normalizeUnicode(input) {
  return input.normalize("NFKC");
}

Дополнительно может применяться фильтрация управляющих символов:

function stripControlChars(input) {
  return input.replace(/[\u0000-\u001F\u007F-\u009F]/g, "");
}

Согласование санитизации и локали

Санитизация не должна противоречить локализации. Например, удаление запятых как «потенциально опасных символов» делает невозможным корректный парсинг чисел в некоторых локалях.

Корректный подход заключается в разделении этапов:

  1. сохранение локально значимых символов;
  2. удаление только структурно опасных или лишних элементов;
  3. передача результата в Globalize для парсинга;
  4. повторная валидация результата после преобразования.

Валидация после парсинга

После обработки Globalize результат должен проверяться на корректность диапазонов и логических ограничений.

Пример:

const parser = globalize.numberParser();
const value = parser(input);

function validateValue(v) {
  return Number.isFinite(v) && v >= 0 && v <= 1000000;
}

Санитизация на входе и валидация на выходе образуют двухуровневую систему защиты от некорректных данных.

Обработка смешанных пользовательских вводов

Часто ввод содержит комбинацию чисел, текста и символов форматирования:

"Баланс: 1 200,50 USD"

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

function extractNumericPart(input) {
  const match = input.match(/[\d\s.,\-]+/);
  return match ? match[0] : "";
}

Затем применяется санитизация и парсинг через Globalize.

Устойчивость к ошибкам парсинга

Globalize возвращает NaN при невозможности интерпретации строки. Санитизация снижает вероятность таких случаев, но не исключает их полностью.

Обработка ошибок включает:

  • проверку результата парсинга;
  • fallback-значения;
  • логирование некорректных входных данных.
const parser = globalize.numberParser();
const result = parser(input);

const safeResult = Number.isNaN(result) ? 0 : result;

Итоговая структура обработки данных

Санитизация в связке с Globalize обычно формирует следующий конвейер:

  • получение пользовательского ввода;
  • Unicode-нормализация;
  • удаление лишних символов;
  • локально-ориентированная подготовка строки;
  • парсинг через Globalize;
  • валидация результата;
  • использование в бизнес-логике или форматирование для вывода