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

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

Одной из базовых возможностей Ajv является автоматическое приведение типов, которое активируется через опцию конфигурации:

const ajv = new Ajv({
  coerceTypes: true
});

При включённой опции строковые значения могут преобразовываться в числа, булевы значения или другие типы в соответствии со схемой:

const schema = {
  type: "object",
  properties: {
    age: { type: "integer" },
    active: { type: "boolean" }
  },
  required: ["age", "active"]
};

const validate = ajv.compile(schema);

const data = {
  age: "25",
  active: "true"
};

validate(data);

// результат после коэрции:
// data = { age: 25, active: true }

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

Однако приведение типов должно использоваться осознанно, поскольку оно может скрывать некорректные входные данные. Например, строка "123abc" может быть преобразована в число 123 в некоторых сценариях обработки вне строгих схем.

Удаление дополнительных полей

Одним из ключевых механизмов санитизации является удаление лишних свойств, которые не описаны в схеме. Ajv поддерживает поведение через параметр removeAdditional:

const ajv = new Ajv({
  removeAdditional: "all"
});

Схема:

const schema = {
  type: "object",
  additionalProperties: false,
  properties: {
    username: { type: "string" }
  }
};

Входные данные:

const data = {
  username: "admin",
  role: "superuser",
  token: "abc123"
};

После валидации:

// результат
{ username: "admin" }

Варианты removeAdditional:

  • "all" — удаляет все дополнительные поля
  • "failing" — удаляет только те, которые не прошли валидацию
  • false — не удаляет ничего (по умолчанию)

Удаление дополнительных свойств особенно важно при работе с API, где входные данные могут содержать скрытые или потенциально вредоносные поля.

Значения по умолчанию

Санитизация часто включает автоматическую подстановку значений по умолчанию. Ajv поддерживает это через опцию useDefaults:

const ajv = new Ajv({
  useDefaults: true
});

Схема:

const schema = {
  type: "object",
  properties: {
    role: { type: "string", default: "user" },
    active: { type: "boolean", default: true }
  }
};

Вход:

const data = {};

Результат:

{
  role: "user",
  active: true
}

Механизм default-значений позволяет формировать полные структуры данных даже при частичном вводе, снижая необходимость дополнительных проверок на уровне бизнес-логики.

Очистка строковых значений

Сырые пользовательские данные часто содержат лишние пробелы, управляющие символы или неконсистентное форматирование. Для этого используется расширение через ajv-keywords, предоставляющее дополнительные инструменты санитизации:

const Ajv = require("ajv");
const addKeywords = require("ajv-keywords");

const ajv = new Ajv();
addKeywords(ajv);

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

const schema = {
  type: "object",
  properties: {
    username: {
      type: "string",
      transform: ["trim", "toLowerCase"]
    }
  }
};

Входные данные:

{
  username: "  AdminUser  "
}

Результат:

{
  username: "adminuser"
}

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

Удаление некорректных значений через валидацию

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

Пример схемы:

const schema = {
  type: "object",
  properties: {
    email: { type: "string", format: "email" },
    age: { type: "integer", minimum: 0 }
  },
  additionalProperties: false
};

При включённой строгой проверке любые некорректные данные будут отклоняться, но при использовании комбинации с removeAdditional и коэрцией можно добиться автоматического приведения входа к допустимому виду.

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

Ajv позволяет расширять систему через пользовательские ключевые слова, что особенно полезно для сложной очистки данных:

ajv.addKeyword({
  keyword: "sanitizeTrim",
  type: "string",
  modify: true,
  compile: () => (data, dataPath, parentData, parentDataProperty) => {
    if (typeof data === "string") {
      parentData[parentDataProperty] = data.trim();
    }
  }
});

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

const schema = {
  type: "object",
  properties: {
    comment: {
      type: "string",
      sanitizeTrim: true
    }
  }
};

Такие механизмы позволяют внедрять произвольные правила очистки, например:

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

Работа с вложенными объектами

Санитизация особенно важна для сложных структур:

const schema = {
  type: "object",
  properties: {
    user: {
      type: "object",
      properties: {
        name: { type: "string", transform: ["trim"] },
        contacts: {
          type: "object",
          properties: {
            email: { type: "string", format: "email" }
          },
          additionalProperties: false
        }
      },
      additionalProperties: false
    }
  }
};

При обработке вложенных объектов механизмы removeAdditional, coerceTypes и transform применяются рекурсивно, обеспечивая целостную нормализацию всей структуры данных.

Безопасность и границы санитизации

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

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

В системах с повышенными требованиями к безопасности часто разделяют этапы:

  1. Строгая валидация входных данных
  2. Явная санитизация
  3. Формирование доменной модели

Такой подход снижает вероятность непредсказуемого поведения при обработке внешнего ввода.

Комбинирование механизмов Ajv

Максимальный эффект достигается при одновременном использовании нескольких возможностей:

const ajv = new Ajv({
  coerceTypes: true,
  useDefaults: true,
  removeAdditional: "all"
});

В сочетании со схемами это позволяет добиться автоматического приведения данных к строгой структуре без дополнительного кода:

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

Практическая модель обработки данных

Типичный поток санитизации в Ajv можно представить как последовательность:

  1. Получение необработанного JSON
  2. Приведение типов (coerceTypes)
  3. Применение значений по умолчанию (useDefaults)
  4. Очистка структуры (removeAdditional)
  5. Строковые трансформации (transform keywords)
  6. Финальная валидация схемы

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