Валидация как защита от инъекций

Инъекционные уязвимости возникают там, где внешние данные без достаточной проверки попадают в интерпретируемый контекст: SQL-запросы, команды операционной системы, шаблоны поиска, NoSQL-выражения или генераторы HTML. Основная причина — отсутствие строгого разделения между данными и управляющей логикой.

Типовой сценарий:

const query = `SEL ECT * FR OM users WH ERE email = '${email}'`;

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

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


Роль class-validator в контроле входного потока

Библиотека class-validator строится вокруг декларативного описания ограничений через декораторы. Она работает на уровне объектов, позволяя описывать контракт входных данных как класс с набором правил.

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

Пример базовой DTO-модели:

import { IsEmail, IsString, MinLength } fr om 'class-validator';

export class CreateUserDto {
  @IsEmail()
  email;

  @IsString()
  @MinLength(8)
  password;
}

Такая модель формирует жесткий фильтр: любые отклонения от структуры приводят к ошибке валидации до попадания в слой доступа к данным.


Инъекции через неконтролируемую структуру данных

SQL-инъекции и типовая ошибка доверия к строкам

Основной источник уязвимости — конкатенация строк:

const sql = "SEL ECT * FR OM users WH ERE name = '" + name + "'";

Даже если используется ORM, проблема сохраняется при динамических фильтрах.

Валидация снижает риск за счет ограничения допустимого набора символов и формата:

import { IsString, Matches, MaxLength } fr om 'class-validator';

export class SearchDto {
  @IsString()
  @MaxLength(50)
  @Matches(/^[a-zA-Z0-9_ ]+$/)
  query;
}

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


Ограничение структуры как защита от NoSQL-инъекций

NoSQL-базы часто принимают JSON-объекты напрямую, что открывает возможность подмены логики запроса:

// потенциально опасный input
{
  "email": { "$ne": null }
}

Если такой объект проходит без фильтрации, он может изменить смысл запроса.

Жесткая типизация через class-validator

import { IsString, IsEmail } fr om 'class-validator';

export class LoginDto {
  @IsEmail()
  email;

  @IsString()
  password;
}

Использование примитивных типов (string, number) вместо объектов критично: любые структуры, отличные от строки, будут отклонены.


Принцип белого списка допустимых значений

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

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

import {
  IsString,
  IsInt,
  Min,
  Max,
  IsOptional
} from 'class-validator';

export class ProductFilterDto {
  @IsString()
  category;

  @IsOptional()
  @IsInt()
  @Min(0)
  @Max(1000)
  priceMin;

  @IsOptional()
  @IsInt()
  @Min(0)
  @Max(1000)
  priceMax;
}

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


Ограничение типов как защита от логических инъекций

Инъекции могут быть не только синтаксическими, но и логическими: подмена условий, обход авторизации, изменение бизнес-логики.

Пример:

if (isAdmin === "true") {
  grantAccess();
}

Без строгой типизации строка "false" может быть интерпретирована как truthy-значение в других контекстах.

Решение через boolean-валидацию

import { IsBoolean } from 'class-validator';

export class AccessDto {
  @IsBoolean()
  isAdmin;
}

Важно, что без трансформации входных данных строки "true" и "false" не будут автоматически приведены к boolean, что предотвращает скрытые ошибки интерпретации.


Контроль числовых диапазонов и предотвращение переполнений логики

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

import { IsInt, Min, Max } from 'class-validator';

export class PaginationDto {
  @IsInt()
  @Min(0)
  @Max(100)
  lim it;

  @IsInt()
  @Min(0)
  offset;
}

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


Защита через строгие UUID и идентификаторы

Подмена идентификаторов — частый вектор атак, особенно при доступе к ресурсам.

import { IsUUID } from 'class-validator';

export class GetUserDto {
  @IsUUID()
  id;
}

Без такого ограничения возможно внедрение строк, содержащих SQL/NoSQL-операторы или обход фильтров через альтернативные форматы идентификаторов.


Ограничение строковых полей и подавление управляющих символов

Строковые поля — основной канал инъекций. Даже при использовании ORM или prepared statements они остаются опасными при логировании, шаблонизации или построении динамических фильтров.

import { IsString, MaxLength, Matches } from 'class-validator';

export class CommentDto {
  @IsString()
  @MaxLength(300)
  @Matches(/^[^<>$]*$/)
  text;
}

Ограничение символов <, > и $ снижает риск внедрения шаблонов и управляющих конструкций.


Валидация вложенных объектов и сложных структур

Инъекции часто прячутся во вложенных структурах JSON.

import { ValidateNested, IsString } from 'class-validator';
import { Type } from 'class-transformer';

class ProfileDto {
  @IsString()
  bio;
}

export class UserDto {
  @IsString()
  name;

  @ValidateNested()
  @Type(() => ProfileDto)
  profile;
}

Без ValidateNested вложенные поля остаются неконтролируемыми, создавая обходной путь для внедрения вредоносных данных.


Разделение ответственности: валидация vs экранирование

Валидация не заменяет экранирование и параметризацию. Она решает другую задачу: отсечение некорректной структуры до уровня бизнес-логики.

  • Валидация: проверка формы, типа, диапазона, структуры
  • Параметризация: безопасная передача данных в SQL/NoSQL
  • Экранирование: защита в UI/HTML-контексте

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


Белые ограничения как стратегия минимизации поверхности атаки

Чем меньше допустимых форм данных, тем ниже вероятность успешной инъекции. class-validator позволяет строить именно такую модель:

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

Эта стратегия уменьшает энтропию входных данных и делает поведение системы предсказуемым даже под нагрузкой некорректных запросов.