Mass Assignment: один параметр — рут на проде
Сегодня тема — не баг, а прямо-таки философия ленивых бэкендеров: «пусть модель сама разберётся, что там во входящем JSON». Спойлер: разбирается она плохо. Погнали ковырять Mass Assignment.
Что вообще происходит
Mass Assignment — это когда фреймворк на автомате мапит все поля из тела запроса прямо в объект модели, без allowlist’а разрешённых атрибутов. Разработчик думал, что клиент пришлёт username, password, email — а клиент присылает ещё и isAdmin, role, balance, потому что почему бы и нет.
Классический пример из OWASP WSTG — форма регистрации:
POST /createUser
username=bob&password=supersecretpassword&email=bob@domain.test&isAdmin=true
Если контроллер биндит объект автоматически (Rails ActiveRecord, Spring @ModelAttribute, Django ModelForm без fields, .NET [Bind] без ограничений) — поздравляю, Боб только что стал админом одним лишним параметром в теле запроса. Один параметр — рут на проде, буквально как в заголовке.
Это не теория, это конвейер CVE
Народ любит говорить «да это баян из методичек». Ага, конечно. Вот что происходит прямо сейчас:
- GitHub, 2012 — Egor Homakov показал мass assignment в публичных ключах SSH через PR-форму, классика жанра, вошла во все учебники.
- MISP, CVE-2026-54360 — mass assignment в механизме шаринга объектов, высокая критичность, публикация буквально на днях.
- Bug bounty в 2026 продолжается бодро: репорты про
PUT /api/v1/profile, куда добавляется полеrole, и обычный юзер внезапно становится админом — свежий разбор от марта 2026.
Тренд простой: где есть автобиндинг JSON→модель без явного allowlist, там рано или поздно найдётся лишнее поле, которое бэкенд с радостью проглотит.
Векторы атаки: куда совать лишние параметры
Не жди, что уязвимое поле будет очевидным — они прячутся в самых скучных эндпоинтах.
- Регистрация и профиль:
role,isAdmin,is_staff,permissions,account_type,verified,email_verified. - Смена пароля / сброс: инъекция параметра
idилиtokenв тело запроса, который переопределяет легитимный токен сброса — рабочий вектор для account takeover без взаимодействия с жертвой. - E-commerce и биллинг:
price,discount,chosen_discount,status— меняешь"chosen_discount": "x"и покупаешь люксовую кожаную куртку за копейки, как в лабе PortSwigger. - Bulk-операции:
POST /posts/bulkupdateс массивом объектов, где среди легитимных полей затесалсяuser_idилиemail— меняешь чужой email на свой и потом дергаешь password reset. - Multi-tenant SaaS:
tenantId,organization_id,plan— привет, доступ к чужому арендатору или бесплатный апгрейд тарифа.
Как искать и эксплуатировать — команды и лайфхаки
Burp Suite тут must-have, не благодари 😎.
- Собери baseline-запрос. Перехвати легитимный
POST/PUTчерез Burp Proxy, посмотри JSON-тело. - Найди скрытые поля. Читай доку API, Swagger/OpenAPI-спеку (
/swagger.json,/api-docs), исходники фронта (JS-бандлы часто светят названия полей моделей типаisAdmin,role,tier). Это твой словарь для фаззинга. - Автофаззинг через Burp Intruder или Param Miner — закидывай в тело потенциальные имена полей (
role,admin,isAdmin,permissions,plan,balance) и смотри на разницу в ответе (статус-код, длина, JSON-структура). - Ручной payload:
PUT /api/v1/profile HTTP/1.1
Host: target.com
Authorization: Bearer <your_token>
Content-Type: application/json
{
"name": "Bob",
"email": "bob@test.com",
"role": "admin"
}
Если в ответе "role": "admin" — вот он, твой рут на проде одним параметром.
- Проверь на blind-эффект. Иногда ответ не покажет изменение сразу — перелогинься или дерни другой эндпоинт (
/me,/whoami), чтобы убедиться, что привилегия реально закрепилась на бэкенде. - Не забудь про nested-объекты и массивы — уязвимость часто прячется в bulk-эндпоинтах, где валидация ещё слабее, чем в одиночных.
Как закрывать эту дыру (для отчёта клиенту)
Если ты по защите — вот что зашивать в рекомендации:
- Явный allowlist полей на бэкенде: маппить руками только разрешённые атрибуты, никогда не биндить весь JSON в модель напрямую.
- Sensitive-поля (
role,isAdmin,price,balance) делать read-only относительно клиентского ввода — менять их можно только через отдельные привилегированные эндпоинты с доп. авторизацией. - Использовать DTO/Serializer-паттерн вместо прямого биндинга ORM-модели (Django REST
serializer.fields, отдельныеUpdateUserDTOвместо полнойUser-модели). - Логировать и алертить на неожиданные поля в теле запроса — если клиент прислал поле, которого нет в спеке, это уже сигнал для WAF/IDS.
Короче, бро, Mass Assignment — это баг ленивого биндинга, который живёт ровно столько, сколько разработчики продолжают доверять клиенту больше, чем себе. Один лишний JSON-параметр — и ты уже не юзер, а бог продакшена. Зашивай в чек-лист, тестируй каждый PUT/PATCH на профили и биллинг — там золото копается лопатой.
Похожие статьи

Burp Suite 2026: расширения, которые реально ускоряют работу

gRPC пентест: когда Burp плачет, а ты нет
