сеньорчикОткрыть в Telegram
← вся теориятеория к собесу · FastAPI, Django и API

Валидация и сериализация данных

Валидация входа - граница доверия

Валидация на сервере это не про удобство, а про безопасность: клиент может слать в API что угодно в обход твоего UI. Собес проверяет, понимаешь ли ты, что сервер - единственная надёжная граница доверия, и знаешь ли классическую дыру mass assignment.

Типовые формулировки: «зачем валидировать на сервере, если фронт уже проверил?», «что такое mass assignment?», «почему разные модели на вход и выход».

Сервер - единственная граница доверия

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

// Отсюда правило: любое поле, от которого зависит логика или безопасность, перепроверяется на бэкенде, каким бы «проверенным» оно ни пришло с фронта.

серверная валидация
единственная надёжная граница доверия к данным
mass assignment
уязвимость слепого маппинга лишних полей тела

Mass assignment и раздельные схемы

Слепой маппинг всего тела запроса в модель это mass assignment: клиент добавит в JSON is_admin или is_verified, и если схема принимает всё подряд, эти поля проставятся. Защита - принимать только явно разрешённые поля через схему-белый-список, а не биндить сущность целиком.

И разделяй вход с выходом: на входе есть password и нет id, на выходе есть id и created_at, но нет password. Одна модель на оба направления рано или поздно протечёт - пароль просочится наружу или лишнее пролезет внутрь.

// В FastAPI это ровно UserCreate (вход) и UserOut (выход, он же response_model), в DRF (Django REST Framework) - разные сериализаторы или поля read_only.

schema in/out
разные модели приёма и отдачи

Как отвечать: «Фронт уже проверил форму - зачем валидировать на сервере?»

Потому что клиентская проверка защищает UX, а не данные. Любой может отправить запрос напрямую в API - через curl, Postman или свой скрипт - в обход браузера и всей фронтовой валидации. Значит фронт легко обойти, и единственная надёжная граница доверия - сервер. Особенно это критично для полей вроде роли или флага is_admin: если слепо замапить всё тело в модель, клиент их себе и проставит это mass assignment. Поэтому на бэкенде я принимаю только белый список полей через схему и перепроверяю всё, от чего зависят логика и права.

Названа суть (фронт про UX, обходится напрямую), приведена конкретная атака mass assignment и защита белым списком - ответ безопасника, а не «так положено».

На чём валят

  • Доверять клиентской валидации: её легко обойти прямым запросом к API.
  • Принимать лишние поля тела в модель - клиент проставит is_admin (mass assignment).
  • Одна модель на вход и выход: пароль просочится наружу или лишнее пролезет внутрь.
  • Валидировать формат, но не бизнес-правила (диапазоны, права на объект) - дыра остаётся.

Проверьте себя

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

  1. #validation_serialization1 / 5
    Клиент прислал тело с лишним полем is_admin, которого нет в схеме. Правильное поведение?
    A)Сохранить поле как есть — вдруг оно понадобится приложению позже
    B)Игнорировать или отклонить — не маппить вслепую в модель
    C)Вернуть 500, ведь наличие лишнего поля — это сбой сервера
    D)Автоматически завести новую колонку в таблице под это поле
    показать ответ и разбор
    +B)Игнорировать или отклонить — не маппить вслепую в модель

    // разбор: Слепой маппинг всего тела в модель — уязвимость mass assignment: клиент проставит поля, которые не должен (is_admin, is_verified). Схема ввода должна принимать только разрешённые поля, а лишние игнорировать или отклонять. Pydantic/DRF-сериализаторы для этого и берут белый список полей.

  2. #validation_serialization2 / 5
    Зачем разделять входную и выходную модели (schema in / schema out)?
    A)Вход и выход имеют разные поля — пароль внутрь, но не наружу
    B)Выходная модель обязана буквально повторять входную один в один
    C)Разделение нужно только чтобы удвоить объём кода на ревью
    D)Это требование фреймворка, иначе эндпоинт вообще не запустится
    показать ответ и разбор
    +A)Вход и выход имеют разные поля — пароль внутрь, но не наружу

    // разбор: Приём и отдача — разные контракты: на входе есть password и нет id, на выходе есть id/created_at и нет password. Отдельные модели (UserCreate vs UserOut) не дают пароль утечь наружу и лишним полям — попасть внутрь. Одна модель на оба направления рано или поздно протечёт.

  3. #validation_serialization3 / 5
    Что такое сериализация в контексте API?
    A)Превращение объекта в формат для передачи (JSON) и обратно
    B)Сжатие тела ответа для уменьшения объёма передаваемого по сети трафика
    C)Проверка входных данных на соответствие ограничениям и бизнес-правилам приложения
    D)Шифрование данных перед отправкой их по сети клиенту или другому сервису
    показать ответ и разбор
    +A)Превращение объекта в формат для передачи (JSON) и обратно

    // разбор: Сериализация — преобразование объекта в представление для передачи/хранения (например, Python-объект → JSON), десериализация — обратно. Отдельно стоит валидация: проверка, что данные корректны. Библиотеки вроде Pydantic делают и то и другое: разбирают вход, валидируют, отдают типизированный объект, а на выходе сериализуют его назад.

  4. #validation_serialization4 / 5
    Почему валидацию входных данных делают на границе приложения, а не в глубине логики?
    A)Граница — место, где удобнее всего перехватить сырые данные запроса
    B)Дальше по коду данные считаются уже корректными — меньше проверок и багов
    C)Так валидация выполняется быстрее, потому что граница ближе к сетевому сокету
    D)В глубине логики валидация недоступна — данные туда не доходят
    показать ответ и разбор
    +B)Дальше по коду данные считаются уже корректными — меньше проверок и багов

    // разбор: Валидация на входе (в схеме запроса) создаёт чёткий рубеж: за ним данные уже типизированы и корректны, и внутренний код не засоряется повторными проверками «а вдруг тут не число». Это уменьшает дублирование, локализует ошибки ввода в одном месте и делает 422-ответы единообразными. Размазанная по коду валидация — источник дыр и багов.

  5. #validation_serialization5 / 5
    Клиент прислал "42" строкой, а поле объявлено int. Что разумно сделать схеме?
    A)Зависит от режима: строгий отвергнет, мягкий приведёт "42" к 42
    B)Отвергать: строку в целочисленное поле принимать недопустимо
    C)Принять как есть и хранить строкой, проигнорировав объявленный тип поля
    D)Молча приводить строку к числу, даже заведомо нечисловую
    показать ответ и разбор
    +A)Зависит от режима: строгий отвергнет, мягкий приведёт "42" к 42

    // разбор: Есть два подхода. Коэрсинг (мягкий, дефолт Pydantic для многих типов): числовую строку '42' приведут к 42 — удобно для форм и query. Strict-режим: типы должны совпадать точно, '42' для int отвергается. Выбор зависит от источника данных и требований. Важно осознанно задать политику, а не полагаться на случай — иначе тихие приведения дадут сюрпризы.

дальше

Теорию прочитали. Навык ставится повторением

В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.