К основному контенту
secenta
СтатьяБесплатный урок10 мин

Как устроено веб-приложение и где рождаются уязвимости

Разбираем модель клиент-сервер и анатомию HTTP-обмена, объясняем, почему доверие пользовательскому вводу — первопричина большинства веб-багов, и составляем карту поверхности атаки.

текстКлиент, сервер и анатомия HTTP-запроса

Веб как разговор двух сторон

Любое веб-приложение — это диалог двух участников. Клиент (браузер, мобильное приложение, скрипт, другой сервис) формулирует запрос, сервер его обрабатывает и возвращает ответ. Между ними — протокол HTTP: набор правил, как именно оформить вопрос и как понять ответ. Важно сразу усвоить: сервер не видит "пользователя" — он видит только байты, которые пришли по сети. Кто их прислал и можно ли им верить, сервер сам по себе не знает.

Аналогия: сервер — это окошко выдачи на складе. Сотрудник за окошком не видит человека целиком, он видит только бланк-заявку, который ему просунули. Если в бланке написано "выдать товар со стеллажа №7", сотрудник пойдёт к стеллажу №7 — даже если по правилам этому клиенту туда нельзя. Вся защита держится на том, проверяет ли сотрудник бланк, или слепо его исполняет.

Из чего состоит HTTP-запрос

Запрос состоит из нескольких частей, и каждая — потенциальный канал данных от клиента:

  • Строка запроса: метод (GET, POST, PUT, DELETE), путь и query-параметры — например GET /search?q=книга.
  • Заголовки (headers): Host, User-Agent, Cookie, Authorization, Content-Type и десятки других.
  • Тело (body): данные форм, JSON, загружаемые файлы — всё, что не помещается в строку.

Ответ сервера зеркально устроен: строка статуса (200 OK, 404 Not Found, 500), заголовки (Set-Cookie, Content-Type, заголовки безопасности) и тело — HTML, JSON, картинка.

Stateless и роль состояния

HTTP по природе stateless: каждый запрос самодостаточен, сервер не помнит предыдущий. Чтобы узнать вас между запросами, приложение выдаёт сессионную куку или токен — и именно поэтому куки и заголовки авторизации становятся лакомой целью.

Типичная ошибка: считать, что раз UI скрыл кнопку или поле, то и соответствующего действия на сервере не существует. UI — это лишь удобная обёртка; реальный контракт — HTTP-запрос, и его можно отправить вручную в обход любого интерфейса.

Мини-итог: веб — это обмен HTTP-сообщениями между не доверяющими друг другу сторонами; каждая часть запроса (метод, путь, query, заголовки, куки, тело) — это данные, которые приходят извне и которым по умолчанию верить нельзя.

текстПервопричина: слепое доверие пользовательскому вводу

Почему уязвимости вообще существуют

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

Аналогия: представьте редактора, который верстает газету и зачем-то ставит в номер любую записку, просунутую под дверь, как официальную статью. Пока записки безобидны — всё хорошо. Но однажды кто-то подсунет записку "уволить главреда", и она выйдет в печать как редакционное решение. Проблема не в злой записке — проблема в том, что редактор не отделяет свой текст от чужого.

Что значит "недоверенный ввод"

Недоверенным считается любой источник, который контролируется не вами:

  • значения форм и query-параметров;
  • заголовки запроса, включая Host, Referer, User-Agent;
  • содержимое и имена загружаемых файлов;
  • тело JSON/XML, в том числе вложенные поля;
  • данные из внешних API и даже из собственной БД, если туда раньше попал чужой ввод (это называют "вторичной" или stored-инъекцией).

Ключевая ловушка — думать, что клиентская валидация что-то защищает. JavaScript в браузере проверяет ввод для удобства пользователя, но злоумышленник просто не выполняет ваш JS: он шлёт запрос напрямую. Любая проверка, которая существует только на клиенте, для безопасности не существует вовсе.

Симптом против первопричины

Важно различать симптом и причину. "На странице сработал чужой скрипт" — симптом; первопричина — "ввод попал в HTML без кодирования". "Из БД утекли чужие заказы" — симптом; причина — "сервер не проверил, что объект принадлежит этому пользователю". Лечить надо причину: чинить симптом точечно (заблокировать одну нагрузку) бесполезно — найдётся следующая.

Типичная ошибка: фильтровать "плохие слова" (чёрные списки вроде запрета <script>). Чёрные списки всегда обходятся вариациями кодировки и регистра; защищает не запрет конкретных строк, а правильная обработка данных в точке использования — параметризация, кодирование вывода, проверка прав.

Мини-итог: первопричина веб-уязвимостей — смешение недоверенного ввода с доверенной логикой; недоверенным является всё, что приходит по сети, а единственная надёжная защита строится на сервере, в точке, где данные используются.

текстПоверхность атаки веб-приложения

Что такое поверхность атаки

Поверхность атаки — это совокупность всех точек, через которые внешние данные могут попасть в приложение и повлиять на его поведение. Чем больше таких точек и чем хуже они контролируются, тем выше риск. Задача тестировщика на первом этапе — не атаковать, а составить карту: где приложение принимает ввод, что с этим вводом потом происходит и кому он доверяет.

Аналогия: поверхность атаки — это все двери, окна, вентиляция и почтовый ящик здания. Грабитель ищет не самую крепкую дверь, а самое слабое окно. Поэтому инвентаризация всех входов важнее, чем укрепление одного парадного входа.

Основные каналы ввода

  • Формы — логин, регистрация, поиск, профиль, оплата. Классический источник инъекций и XSS.
  • Query-параметры — всё после ? в URL; часто используются для идентификаторов объектов (привет, обход доступа) и для отражённого XSS.
  • HTTP-заголовкиHost (Host header injection), Referer, User-Agent, кастомные X--заголовки; их легко забыть провалидировать, потому что в форме их не видно.
  • Куки — сессии, настройки, иногда сериализованные объекты; подделка или подмена куки бьёт по авторизации.
  • API-эндпоинты — REST/GraphQL принимают JSON и часто скрывают больше полей, чем показывает UI (массовое присваивание, mass assignment).
  • Загрузка файлов — имя файла, MIME-тип, содержимое, путь сохранения; ведёт к веб-шеллам, path traversal, XXE при разборе документов.

Не только вход, но и движение данных

Поверхность атаки — это не только входы, но и маршруты: куда ввод течёт дальше. Один и тот же параметр опасен по-разному в зависимости от назначения — в SQL-запросе это инъекция, в HTML это XSS, в имени файла это traversal, в URL для внутреннего запроса это SSRF. Поэтому в карте важно фиксировать пары "источник → приёмник" (source → sink).

Типичная ошибка: проверять только то, что видно в браузере. Скрытые поля, недокументированные параметры, заголовки и "служебные" API-методы — полноценная часть поверхности и часто наименее защищённая, потому что разработчик о них при защите не вспомнил.

Мини-итог: поверхность атаки — это все точки входа недоверенных данных (формы, query, заголовки, куки, API, загрузки) и маршруты их движения к опасным приёмникам; грамотный анализ начинается с инвентаризации этих точек, а не с эксплойта.

слайдыПуть недоверенного ввода
[ слайд 1 из 5 ]
1. Браузер

Пользователь (или атакующий) формирует запрос: вводит данные в форму, меняет URL, подделывает заголовок. На этом шаге данные полностью под контролем клиента — доверять им нельзя.

Обсуждение

Задавайте вопросы — преподаватели и сокурсники ответят.

0 тем

Здесь пока тихо. Задайте первый вопрос — это поможет другим.