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

Механизмы санитизации входных данных в Joi

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


Одной из ключевых особенностей Joi является автоматическое приведение типов (coercion). Библиотека стремится преобразовать входные значения к ожидаемому типу схемы.

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

import Joi from 'joi';

const schema = Joi.object({
  age: Joi.number().integer()
});

const result = schema.validate({
  age: '25'
});

console.log(result.value);
// { age: 25 }

Входная строка '25' автоматически преобразуется в число 25. Это снижает необходимость ручной предобработки данных на уровне приложения.

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

const schema = Joi.object({
  isActive: Joi.boolean()
});

schema.validate({ isActive: 'true' }).value;
// { isActive: true }

Санитизация строковых значений

Для строк Joi предоставляет встроенные методы нормализации, позволяющие привести текст к единому формату.

Удаление пробелов

const schema = Joi.object({
  username: Joi.string().trim()
});

schema.validate({ username: '  admin  ' }).value;
// { username: 'admin' }

Метод trim() удаляет начальные и конечные пробелы, предотвращая ошибки сравнения и поиска.

Изменение регистра

const schema = Joi.object({
  email: Joi.string().lowercase()
});

schema.validate({ email: 'Test@Example.COM' }).value;
// { email: 'test@example.com' }

Метод lowercase() нормализует строку, что особенно важно для email, идентификаторов и логинов.

Также доступен uppercase():

const schema = Joi.object({
  code: Joi.string().uppercase()
});

schema.validate({ code: 'abc123' }).value;
// { code: 'ABC123' }

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

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

const schema = Joi.object({
  role: Joi.string().default('user')
});

schema.validate({}).value;
// { role: 'user' }

Значения по умолчанию позволяют устранять undefined и обеспечивать стабильность структуры данных.

Также возможно использование вычисляемых значений:

const schema = Joi.object({
  createdAt: Joi.date().default(() => new Date(), 'current date')
});

Удаление лишних полей

Одним из важных аспектов санитизации является контроль структуры объекта. Joi позволяет удалять поля, не описанные в схеме.

const schema = Joi.object({
  username: Joi.string()
}).unknown(false);

const result = schema.validate({
  username: 'admin',
  role: 'superuser'
});

console.log(result.value);
// { username: 'admin' }

По умолчанию лишние поля могут быть либо запрещены, либо игнорированы в зависимости от настроек:

Joi.object({
  username: Joi.string()
}).options({ stripUnknown: true });

В этом режиме все неизвестные ключи автоматически удаляются, что снижает риск обработки неожиданных данных.


Глубокая санитизация вложенных объектов

Joi применяет правила рекурсивно ко всей структуре данных.

const schema = Joi.object({
  user: Joi.object({
    name: Joi.string().trim(),
    email: Joi.string().lowercase()
  })
});

schema.validate({
  user: {
    name: '  Ivan  ',
    email: 'IVAN@MAIL.COM'
  }
}).value;

Результат:

{
  user: {
    name: 'Ivan',
    email: 'ivan@mail.com'
  }
}

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


Кастомная санитизация через transform и custom

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

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

const schema = Joi.object({
  tags: Joi.array().items(
    Joi.string().custom((value) => value.trim().toLowerCase())
  )
});

schema.validate({
  tags: ['  NodeJS ', ' JAVASCRIPT ']
}).value;

Результат:

{
  tags: ['nodejs', 'javascript']
}

Использование messages и комплексной логики трансформации

Хотя messages не изменяет данные, он часто используется совместно с кастомной логикой для контроля качества данных.


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

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

const schema = Joi.object({
  list: Joi.array().items(Joi.string().trim().lowercase())
});

schema.validate({
  list: [' A ', ' B ', ' C ']
}).value;

Также возможно удаление пустых строк через кастомную обработку:

Joi.array().items(
  Joi.string().trim().min(1)
).custom((arr) => arr.filter(Boolean));

Условная санитизация

Joi поддерживает динамическое изменение правил в зависимости от входных данных.

const schema = Joi.object({
  type: Joi.string(),
  value: Joi.when('type', {
    is: 'number',
    then: Joi.number().integer(),
    otherwise: Joi.string().trim()
  })
});

В этом случае санитизация value зависит от значения type, что позволяет строить адаптивные схемы обработки данных.


Работа с небезопасными данными и защита структуры

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

Удаление неизвестных ключей:

const schema = Joi.object({
  username: Joi.string(),
  password: Joi.string()
}).prefs({ stripUnknown: true });

Игнорирование лишних данных предотвращает их дальнейшее распространение по системе, снижая поверхность атаки и вероятность логических ошибок.


Преобразование null и undefined

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

const schema = Joi.object({
  nickname: Joi.string().empty('').default('anonymous')
});

В этом случае пустая строка будет преобразована в значение по умолчанию.


Стабилизация формата данных

Санитизация часто применяется для обеспечения консистентного формата хранения данных:

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

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