Библиотека Joi предназначена для декларативного описания схем данных и их строгой проверки. Однако сама природа схемной валидации создаёт класс уязвимостей, связанных не с логической ошибкой, а с вычислительной нагрузкой. При определённых конструкциях схем возможно организовать отказ в обслуживании (DoS) за счёт экспоненциального роста сложности валидации.
Ключевая особенность таких атак заключается в том, что злоумышленник не ломает систему напрямую, а заставляет валидатор выполнять чрезмерное количество операций при обработке специально сформированного входа.
alternatives и вложенные ветвленияОдним из наиболее опасных механизмов является использование
alternatives().try() и вложенных условий, создающих дерево
вариантов.
Каждое дополнительное альтернативное правило увеличивает количество путей проверки:
Joi.alternatives().try(
Joi.object({
type: Joi.string().valid('a'),
value: Joi.string()
}),
Joi.object({
type: Joi.string().valid('b'),
value: Joi.number()
}),
Joi.object({
type: Joi.string().valid('c'),
value: Joi.boolean()
})
)
В простом виде это линейная проверка, но при вложении альтернатив внутри альтернатив возникает комбинаторный взрыв. Особенно критично, если каждая ветка содержит собственные сложные под-схемы, массивы и регулярные выражения.
when() зависимостяхМеханизм условной валидации через when() позволяет
динамически менять схему в зависимости от значения других полей. При
глубокой цепочке зависимостей возникает ситуация, когда одно поле влияет
на другое, которое влияет на третье и так далее.
Пример структуры риска:
Такие циклы создают необходимость повторной оценки условий и пересборки схемы на каждом этапе проверки. В результате валидатор может многократно проходить по одним и тем же данным.
Особую опасность представляют Joi.string().pattern() при
использовании сложных регулярных выражений.
Регулярные выражения с пересекающимися квантификаторами способны приводить к экспоненциальному backtracking:
Joi.string().pattern(/(a+)+b/)
При подаче строки вида "aaaaaaaaaaaaaaaaaaaaaaaaaab"
количество шагов проверки резко возрастает. В контексте массовых
запросов это становится полноценным вектором DoS, так как один запрос
может занимать непропорционально большое количество CPU-времени.
Валидация объектов с вложенностью через
Joi.object().keys() или Joi.array().items()
создаёт риск переполнения стека и значительного увеличения времени
обработки.
Особенно опасны схемы вида:
При отсутствии ограничений глубины структура данных может быть искусственно расширена до десятков тысяч узлов.
items() и неоднородными схемамиКонструкция:
Joi.array().items(
Joi.object({ ... }),
Joi.string(),
Joi.number(),
Joi.alternatives().try(...)
)
заставляет валидатор для каждого элемента массива проверять соответствие сразу нескольким независимым схемам. При большом размере массива сложность растёт линейно, но коэффициент умножается количеством альтернатив.
Если внутри элементов присутствуют ещё и when(),
фактическая стоимость проверки становится многократно выше линейной.
При отсутствии кэширования промежуточных результатов одна и та же под-схема может пересчитываться многократно в разных ветках проверки.
Типичный пример:
alternativesarray.itemswhenКаждое повторное использование инициирует полный проход валидации, если схема не была заранее скомпилирована или мемоизирована.
Функции custom() открывают возможность внедрения
произвольной логики. Даже непреднамеренно сложная логика внутри таких
функций может привести к линейной или квадратичной деградации
производительности.
Особенно опасны:
При массовой отправке запросов с различными вариациями входных данных эффект от сложных схем усиливается:
Даже при умеренной сложности одной схемы суммарная нагрузка становится критической при параллельной обработке.
Снижение риска DoS через сложные схемы достигается не усложнением защиты, а ограничением выразительности схем:
when() зависимостейalternatives().try() в пользу
явных discriminated union через valid() поляJoi.compile() для
устранения повторных вычислений структурыНа практике наиболее опасные схемы обладают следующими признаками:
alternativesТакие конструкции не приводят к ошибкам исполнения, но создают предсказуемую деградацию производительности при специально сформированных входах.