Основы HTTP для кибербезопасности

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

Что ж, начнём с основ: сегодня большинство приложений, которыми мы пользуемся, постоянно взаимодействуют с интернетом - как веб-, так и мобильные. Большинство этих коммуникаций идёт через веб-запросы по протоколу HTTP (HyperText Transfer Protocol) - прикладному протоколу для доступа к ресурсам Всемирной паутины. Термин «гипертекст» означает текст, содержащий ссылки на другие ресурсы и легко интерпретируемый программами-клиентами.

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

Клиент-серверная модель
Клиент-серверная модель

Структура URL

URL предоставляет значительно больше возможностей, чем простое указание домена. Рассмотрим его компоненты:

Синтаксическая диаграмма URI/URL
Синтаксическая диаграмма URI/URL

Компонент

Пример

Назначение

Scheme

http://, https://

Протокол взаимодействия с сервером. Завершается разделителем ://

User Info

admin:password@

Учётные данные для HTTP-аутентификации в формате логин:пароль. Отделяются от хоста символом @. Передаются в открытом виде - использовать не рекомендуется

Host

example.com

Адрес сервера - доменное имя (FQDN) или IP-адрес

Port

:80

Порт подключения. По умолчанию: 80 для HTTP, 443 для HTTPS. При стандартных портах можно опустить

Path

/dashboard.php

Путь к ресурсу на сервере. При отсутствии запрашивается индексный файл (обычно index.html)

Query String

?login=true

Параметры запроса в формате ключ=значение. Начинается с ?, параметры разделяются символом &

Fragment

#status

Якорь для навигации внутри страницы. Обрабатывается браузером, на сервер не передаётся

Обязательные компоненты - схема и хост. Остальные элементы опциональны.

Жизненный цикл HTTP-запроса

На схеме ниже показан обобщённый поток HTTP-запроса. Когда пользователь впервые вводит URL в браузере, тот обращается к DNS-серверу (системе доменных имён), чтобы разрешить домен и получить соответствующий IP-адрес.

Например, для домена example.com DNS может вернуть IP-адрес 198.51.100.42. DNS-сервер находит нужный IP и возвращает его клиенту. Любое доменное имя должно пройти такую процедуру разрешения, иначе сервер просто не узнает, куда отправлять ответ.

Упрощённый поток: вводим домен, происходит DNS-разрешение, затем HTTP-запрос и ответ 200 OK
Упрощённый поток: вводим домен, происходит DNS-разрешение, затем HTTP-запрос и ответ 200 OK

При обращении к веб-ресурсу происходит следующая последовательность (добавлены детали кэшей, TCP/TLS и HTTP):

  1. Браузер отправляет DNS-запрос для разрешения доменного имени. Сначала проверяются локальный кэш браузера и ОС, затем файл /etc/hosts, потом системный резолвер. Ответ кэшируется на время TTL. Вместо UDP/53 всё чаще применяется DoH/DoT (DNS поверх HTTPS/TLS).

  2. Получив IP, клиент устанавливает TCP-соединение с сервером (порт 80 для HTTP или 443 для HTTPS): три шага SYN -> SYN-ACK -> ACK.

  3. Если используется HTTPS, сразу после TCP идёт TLS-рукопожатие: клиент указывает домен через SNI, договаривается о протоколе (ALPN: http/1.1, h2, h3) и проверяет сертификат сервера.

  4. Браузер отправляет HTTP-запрос (GET / HTTP/1.1) с ключевыми заголовками: Host, User-Agent, Accept, Accept-Encoding, Cookie, Cache-Control. Для HTTPS эти данные уже передаются внутри зашифрованного канала.

  5. Ответ может прийти от ближайшего узла на пути: CDN/прокси/балансировщик или origin-сервер. Сервер возвращает статус-код (2xx/3xx/4xx/5xx), тело (например, index.html), заголовки кэширования (Cache-Control, ETag, Last-Modified), редиректы, Set-Cookie и сжатие (Content-Encoding: gzip/br).

  6. Браузер обрабатывает ответ: рендерит HTML, загружает дополнительные ресурсы (CSS/JS/изображения/шрифты), может открывать несколько TCP/TLS-соединений или использовать мультиплексирование HTTP/2/3. Кэшированные ответы могут быть переиспользованы или валидированы (304 Not Modified).

Примечание о DNS: браузер сначала проверяет локальный файл /etc/hosts на наличие записи для домена. Это позволяет вручную задавать DNS-разрешение, добавляя строки в формате IP домен.

HTTPS: защищаем канал

HTTP удобен для наглядных примеров и отладки, но у него есть критичный минус: весь трафик передаётся в открытом виде. Любой, кто окажется между клиентом и сервером (публичный Wi-Fi, скомпрометированный роутер, злонамеренный провайдер), может устроить MITM-атаку (Man-in-the-Middle), подсмотреть логины, пароли, токены сессий и содержимое запросов. В лёгком варианте это заканчивается трекингом и сбором статистики, в тяжёлом - угоном аккаунтов и подменой ответов сервера. Именно эту проблему и решает переход на HTTPS.

Захват HTTP POST-запроса с логином и паролем в открытом виде
Захват HTTP POST-запроса с логином и паролем в открытом виде

С приходом HTTPS (по сути тот же HTTP, но поверх TLS) полезная нагрузка перестаёт быть читаемой: тело запросов и ответов шифруется сессионным ключом, и перехватчик видит лишь поток зашифрованных байтов, а не логины, пароли или JSON. В сниффере это выглядит как «каша» из символов в окне анализа потока - без ключей расшифровать такой трафик невозможно:

Захват зашифрованного TLS-трафика в Wireshark
Захват зашифрованного TLS-трафика в Wireshark

Один из очевидных признаков защищённого канала - префикс https:// в адресной строке и иконка замка (или другого индикатора безопасности) рядом с доменом. В большинстве современных браузеров по клику на эту иконку открывается всплывающее окно со сведениями о сертификате, уровне шифрования и разрешениях сайта; если с сертификатом что-то не так, здесь же появятся предупреждения.

Небольшой нюанс: даже при HTTPS раскрывается домен, если DNS-запросы идут без шифрования. В боевых сетях лучше включить DoH/DoT или завернуть трафик в VPN, чтобы не светить, что именно вы смотрите.

Поток HTTPS

Схема перехода с HTTP на HTTPS и TLS-рукопожатия
Схема перехода с HTTP на HTTPS и TLS-рукопожатия

Если вбить http:// для сайта, который на самом деле ожидает HTTPS, браузер сначала делает обычный HTTP-запрос на порт 80. Сервер отвечает редиректом 301 Moved Permanently (иногда 308 Permanent Redirect) на тот же ресурс, но уже по https:// и порту 443. После этого начинается TLS-рукопожатие: клиент отправляет Client Hello с поддерживаемыми версиями и наборами шифров, сервер отвечает Server Hello и сертификатом, стороны обмениваются ключами и проверяют валидность цепочки. Лишь после успешного завершения рукопожатия поверх этого зашифрованного канала начинает работать обычный HTTP - уже в виде HTTPS.

Злоумышленник теоретически может попытаться провести атаку понижения и подменить HTTPS на HTTP через свой прокси, заставляя клиента остаться на незащищённой схеме. Но современные браузеры и серверы стараются этому противодействовать: HSTS, preload-списки, отключение старых версий TLS и жёсткие политики шифров сильно усложняют реализацию таких атак.

Анатомия HTTP-запроса и ответа

Чтобы понимать, что именно уходит и приходит по сети, удобно разобрать сырые пакеты. Вот пример запроса GET:

Детали HTTP-запроса: метод, путь, версия протокола и заголовки
Детали HTTP-запроса: метод, путь, версия протокола и заголовки

Первая строка состоит из трёх полей, разделённых пробелами:

Поле

Пример

Что значит

Метод

GET

Глагол, описывающий действие (получить, отправить, удалить…).

Путь

/users/login.html

Конкретный ресурс; сюда же попадает query вида ?username=user.

Версия

HTTP/1.1

Версия протокола, которую ожидает сервер.

Дальше идут пары «заголовок: значение» (Host, User-Agent, Cookie, Accept и десятки других). Заголовки закрываются пустой строкой, после которой может идти тело запроса (форма, JSON, файл).

Если при брутфорсе или тестах что-то идёт не так, сервер часто отвечает 400 Bad Request или вообще молчит - и не всегда понятно, в чём причина. По моему опыту, одна из самых частых ошибок - отсутствие пустой строки (\r\n\r\n) между заголовками и телом запроса. Без неё сервер не может понять, где заканчиваются метаданные и начинаются данные. Ещё частые проблемы: лишние пробелы в заголовках, неправильная кодировка спецсимволов в URL или битый Content-Length. Если запрос молча отваливается - первым делом проверяю структуру в hex-редакторе или через curl -v, чтобы увидеть, что реально уходит на сервер.

HTTP-ответ строится аналогично:

Структура HTTP-ответа: статусная строка, заголовки и тело сообщения
Структура HTTP-ответа: статусная строка, заголовки и тело сообщения

Первая строка - версия протокола и статус (200 OK, 404 Not Found и т.п.). Затем идут заголовки (Date, Server, Set-Cookie, Content-Type, длина тела). После пустой строки - само тело: HTML, JSON, изображение, PDF или любой другой контент, на который настроен сервер.

Небольшая деталь про версии: HTTP/1.x - это читаемый текст, поля разделяются \r\n. HTTP/2 уже бинарный и по проводам выглядит иначе, хотя логика запрос/ответ сохраняется.

Для пентестера это важно: в Wireshark HTTP/2 и HTTP/3 нельзя просто прочитать как текст - нужен декодер. Зато Burp Suite и mitmproxy автоматически разбирают бинарный формат и показывают запросы в привычном виде. Ещё один нюанс - мультиплексирование: в HTTP/2 несколько запросов идут по одному соединению параллельно, что усложняет анализ тайминга и race condition атак. HTTP/3 работает поверх QUIC (UDP), что добавляет свои особенности: соединение сложнее перехватить и проксировать.

HTTP-методы и коды состояния

Метод в первой строке запроса говорит серверу, что именно мы хотим сделать, а код в ответе показывает, как сервер обработал попытку. В curl -v метод видно сразу (GET / HTTP/1.1), а в браузерных DevTools это колонка Method.

Методы запросов

Самые часто встречающиеся:

Метод

Что делает

На что смотреть при тесте

GET

Получить ресурс. Параметры в query (?param=value). Кешируется, не меняет состояние.

IDOR через подмену ID в параметрах, утечка данных в URL и логах, обход авторизации.

POST

Отправить данные в теле: текст, JSON, файлы. Формы, логины, загрузки.

Инъекции (SQL, XSS), отсутствие CSRF-токена, mass assignment, загрузка shell'ов.

HEAD

Только заголовки без тела. Разведка перед скачиванием.

Утечка информации в заголовках, обход проверок размера контента.

PUT

Создать или заменить ресурс целиком. Идемпотентен.

Произвольная запись файлов, перезапись конфигов, загрузка без авторизации.

DELETE

Удалить ресурс. Идемпотентен.

IDOR на удаление чужих данных, DoS через массовое удаление.

OPTIONS

Какие методы поддерживает сервер. Возвращает CORS и Allow.

Разведка доступных методов, неправильная CORS-конфигурация.

PATCH

Частичные изменения ресурса. Не идемпотентен.

Mass assignment, изменение чужих данных, privilege escalation.

Методы зависят от конфигурации приложения. В REST-API чаще всего встречается четвёрка GET/POST/PUT/DELETE, но на тестах я всегда пробую OPTIONS, чтобы быстро понять, что вообще разрешено.

Коды ответа

Классы HTTP-кодов показывают общий результат:

Класс

Что означает

1xx

Информируют о ходе обработки.

2xx

Запрос успешно выполнен.

3xx

Клиента просят перейти по другому адресу.

4xx

Проблема на стороне запроса (не тот URL, формат, права).

5xx

Сервер сам не смог обработать запрос.

Примеры по классам:

Код

Комментарий

200 OK

Запрос выполнен успешно, тело обычно содержит ресурс.

302 Found

Временный редирект, браузер перейдёт на новый URL.

400 Bad Request

Неверный запрос: например, не хватает перевода строки.

403 Forbidden

Нет доступа; иногда причина в WAF, блокирующем подозрительный запрос.

404 Not Found

Ресурс отсутствует.

500 Internal Server Error

Внутренняя ошибка сервера - стоит исследовать подробнее.

Когда я получаю код 403/401, сразу сверяю, нет ли жёсткой привязки к заголовкам или методу. Иногда достаточно сменить GET на POST или добавить X-Forwarded-For, и ресурс внезапно открывается.

Несколько кодов особенно интересны при тестировании:

  • 401 vs 403 - если сервер отдаёт 401 на несуществующий ресурс и 403 на существующий, это позволяет энумерировать скрытые эндпоинты.

  • 500 Internal Server Error - часто сопровождается stack trace или отладочной информацией. Смотри тело ответа: там могут быть пути к файлам, имена таблиц, версии фреймворков.

  • 302/301 на страницу логина - выдаёт защищённые эндпоинты. Если /admin редиректит на /login, значит /admin существует.

  • 405 Method Not Allowed - сервер говорит, что метод не поддерживается, но сам эндпоинт есть. Пробуй другие методы.

  • 429 Too Many Requests - rate limiting. Полезно знать порог для брутфорса и обхода.

cURL: быстрая разведка из терминала

Для ручных проверок и автоматизации удобно использовать cURL (client URL) - CLI-утилиту и библиотеку, которая помимо HTTP понимает кучу других протоколов. Её плюсы для пентестера: скриптуемость, контроль над заголовками и возможность быстро сравнить ответы без полноценного браузера. Плюс через ключи легко подложить куки/токены или прятаться за прокси, мгновенно сверяя различия в ответах.

Базовый запрос:

curl example.com

Загрузка страницы или файла с сохранением имени удалённого ресурса:

curl -O example.com/index.html

Тихий режим, чтобы не видеть прогресс-бар и служебные сообщения:

curl -s -O example.com/index.html

Пара опций, которые часто выручают:

  • -i - включить заголовки ответа;

  • -v - подробный разбор запроса/ответа (удобно для отладки);

  • -o <file> - явно указать имя выходного файла;

  • -u user:pass - передать простую HTTP-аутентификацию.

Когда нужно видеть весь протокол, добавляйте -v:

curl example.com -v

Вывод покажет, что именно отправил клиент (GET / HTTP/1.1, Host, User-Agent) и что вернул сервер (строка статуса, заголовки, тело). Если приходит 401 Unauthorized или редирект, это заметно сразу. Тройной verbose (-vvv) дополнительно раскрывает TLS-детали и мелкие нюансы соединения.

Для полного списка ключей можно вызвать curl --help all или почитать man curl. Даже краткое знакомство с ними заметно ускоряет рутину при проверке веб-ресурсов.

HTTPS и cURL

При работе по HTTPS curl сам проводит TLS-рукопожатие и проверяет сертификаты. Если цепочка подписи битая или используется самоподписанный сертификат, утилита оборвёт соединение - это нормальная защита от MITM:

user@linux:~$ curl https://example.com

curl: (60) SSL certificate problem: Invalid certificate chain
More details here: https://curl.haxx.se/docs/sslcerts.html
...SNIP...

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

user@linux:~$ curl -k https://www.example.com

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
...SNIP...

DevTools: быстрый аудит из браузера

Практически в каждом современном браузере есть инструменты разработчика. В Chrome/Firefox их можно открыть CTRL+SHIFT+I или F12, и для сетевых задач нам нужна вкладка Network.

При обновлении страницы там появится список запросов: статус ответа, метод, домен, путь и размер. Если страница тянет сотни ресурсов, помогает поле Filter - по нему легко отыскать интересующий URL или тип контента.

Кликнув по запросу, можно посмотреть вкладки Headers, Response и Cookies. В Response есть переключатель на сырой вид, чтобы увидеть исходный HTML/JSON без рендера. Это почти бесплатный аналог сниффера, который всегда под рукой.

Киллер-фича для пентестера - Copy as cURL. Правый клик на любом запросе -> Copy -> Copy as cURL - и в буфере окажется готовая команда со всеми заголовками, cookie и телом. Вставляешь в терминал, меняешь параметры, отправляешь - идеально для быстрого тестирования без настройки прокси. В Chrome также есть Copy as Fetch для воспроизведения запроса прямо в консоли браузера.

Ещё полезные трюки:

  • Preserve log - сохранять запросы при переходах между страницами (иначе список очищается);

  • Disable cache - отключить кэш, чтобы видеть реальные запросы к серверу;

  • фильтр method:POST или status-code:500 - быстро найти интересные запросы в большом потоке.

GET-запросы на практике

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

Что делает браузер, когда мы просто открываем страницу

Когда мы вбиваем адрес в строку браузера и жмём Enter, клиент отправляет к серверу GET-запрос: просит отдать HTML-документ, а затем по мере разбора кода страницы догружает скрипты, стили, шрифты, картинки и запросы к API - тоже чаще всего через GET или POST.

Это хорошо видно во вкладке Network: один наш клик превращается в целый «веер» запросов к разным URL. Такой взгляд особенно полезен при аудите и багбаунти: становится понятно, какие именно эндпоинты на самом деле существуют у приложения и какие параметры оно принимает.

Пример: Basic Auth на уровне веб-сервера

Иногда доступ к разделу ограничивают не формой логина на самой странице, а встроенной аутентификацией веб-сервера - HTTP Basic Auth. Браузер в таком случае показывает системное окошко «Введите логин/пароль», а проверку учётки делает уже не приложение, а, например, Apache или nginx.

Представим, что по адресу /reports/ лежит внутренняя админка и веб-сервер защищает её Basic Auth'ом. Если обратиться к ней без авторизации, мы увидим типичную картину:

user@linux:~$ curl -i https://intranet.example.com/reports/

HTTP/1.1 401 Unauthorized
Date: Mon, 01 Dec 2025 10:00:00 GMT
Server: Apache
WWW-Authenticate: Basic realm="Internal reports"
Content-Length: 16
Content-Type: text/plain; charset=utf-8

Authorization required
  • код 401 говорит, что доступ запрещён без аутентификации;

  • заголовок WWW-Authenticate подсказывает клиенту, что сервер ожидает Basic Auth и как называется защищаемая область (realm).

Теперь отправим тот же запрос, но уже с логином и паролем, например security:Winter2025!:

user@linux:~$ curl -u security:Winter2025! https://intranet.example.com/reports/

<!DOCTYPE html>
<html lang="en">
<head>
  ...SNIP...

На этот раз доступ есть. Если добавить флаг -v, станет видно, какой заголовок на самом деле уходит к серверу:

user@linux:~$ curl -v -u security:Winter2025! https://intranet.example.com/reports/ 2>&1 | sed -n '1,15p'
> GET /reports/ HTTP/1.1
> Host: intranet.example.com
> Authorization: Basic c2VjdXJpdHk6V2ludGVyMjAyNSE=
> User-Agent: curl/8.5.0
> Accept: */*

Строка после Basic - это просто base64 от security:Winter2025!. Никакого шифрования здесь нет, поэтому Basic Auth имеет смысл только поверх HTTPS.

Тот же эффект можно получить, если задать заголовок вручную:

user@linux:~$ curl -H 'Authorization: Basic c2VjdXJpdHk6V2ludGVyMjAyNSE=' https://intranet.example.com/reports/

GET-параметры и работа с API

Теперь посмотрим на типичный сценарий с GET-параметрами. Допустим, после логина в админке есть строка поиска по отчётам. Пользователь вводит текст, а фронтенд в ответ делает запрос к API, например:

https://intranet.example.com/api/reports?query=fraud&limit=5

Всё, что идёт после ?, - это query string (строка запроса). Здесь есть два параметра:

  • query=fraud - текст поиска;

  • limit=5 - максимальное количество результатов.

Если открыть Network во время поиска, мы увидим этот запрос целиком и сможем воспроизвести его вручную:

user@linux:~$ curl 'https://intranet.example.com/api/reports?query=fraud&limit=5' \
  -H 'Authorization: Basic c2VjdXJpdHk6V2ludGVyMjAyNSE='

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

POST

В прошлом разделе мы смотрели на то, как GET используется для получения страниц и выполнения простых запросов к API. Но как только нужно передать логин/пароль, загрузить файл или просто убрать чувствительные параметры из URL, в дело почти всегда вступает POST.

Ключевое отличие простое:

  • GET передаёт параметры в строке запроса (?param=value в URL);

  • POST складывает данные в тело HTTP-запроса.

У этого есть несколько приятных эффектов для разработчика и безопасника:

  • параметры и файлы не светятся в логах веб-сервера и истории браузера как часть URL;

  • тело может быть бинарным (загрузка файлов), кодировать нужно только служебные разделители;

  • ограничение на длину URL (часто ~2000 символов) больше не мешает передавать большие структуры данных.

Ниже разберём живые сценарии: формы логина, сессионные cookie и JSON-запросы к API.

Формы логина (Login Forms)

В отличие от Basic Auth, где браузер сам рисует системное окно ввода логина/пароля и шлёт заголовок Authorization, в реальных приложениях чаще всего используются обычные HTML-формы. Пользователь вводит логин/пароль, жмёт кнопку - фронтенд отправляет POST на эндпоинт авторизации, например /login.php или /auth.

Типичный пример тела запроса, который можно увидеть во вкладке Network -> Request:

username=analyst&password=LabPass123%21

То же самое легко воспроизвести вручную через curl:

user@linux:~$ curl -X POST -d 'username=analyst&password=LabPass123!' https://portal.example.com/login.php

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Analytics Portal</title>
  ...SNIP...

Опции здесь важны:

  • -X POST - явно задаём метод (во многих случаях curl сам поймёт, что нужен POST, как только видит -d, но явное объявление полезно для читаемости);

  • -d - данные формы в формате key=value&key2=value2 (по умолчанию curl отправляет их с Content-Type: application/x-www-form-urlencoded).

Многие формы после успешного логина делают редирект, например с /login.php на /dashboard. Чтобы не ловить только ответ с кодом 302, удобно добавить -L - тогда curl сам пойдёт по цепочке редиректов:

user@linux:~$ curl -L -X POST -d 'username=analyst&password=LabPass123!' https://portal.example.com/login.php

Cookie после авторизации

Почти любое современное веб-приложение после логина выдаёт клиенту сессионную cookie. Именно по ней сервер дальше понимает, что запросы идут от авторизованного пользователя, а не от гостя.

Посмотрим на ответ того же запроса, если добавить -i, чтобы увидеть заголовки:

user@linux:~$ curl -i -X POST -d 'username=analyst&password=LabPass123!' https://portal.example.com/login.php

HTTP/1.1 302 Found
Date: Mon, 01 Dec 2025 12:00:00 GMT
Server: nginx
Set-Cookie: SESSIONID=abf1329e7d904c9fa3c4c1b8f8c21d3a; Path=/; HttpOnly; Secure
Location: /dashboard
Content-Length: 0

Здесь нас интересует строка:

Set-Cookie: SESSIONID=abf1329e7d904c9fa3c4c1b8f8c21d3a; Path=/; HttpOnly; Secure

Сервер говорит: «С этого момента все запросы с cookie SESSIONID=... считаются привязанными к залогиненной сессии». Мы можем взять это значение и использовать его в дальнейших запросах, не проходя логин каждый раз.

Передать cookie curl-у можно двумя способами:

Через -b:

user@linux:~$ curl -b 'SESSIONID=abf1329e7d904c9fa3c4c1b8f8c21d3a' https://portal.example.com/dashboard

Или через явный заголовок:

user@linux:~$ curl -H 'Cookie: SESSIONID=abf1329e7d904c9fa3c4c1b8f8c21d3a' https://portal.example.com/dashboard

В браузере то же самое можно повторить вручную: открыть DevTools, вкладку Storage / Application -> Cookies, подставить своё значение SESSIONID и обновить страницу. Если cookie валидна, вы сразу окажетесь в авторизованной части интерфейса.

Важный момент для безопасности: браузер автоматически отправляет cookie на соответствующий домен при каждом запросе. Это удобно, но создаёт угрозу CSRF (Cross-Site Request Forgery) - злоумышленник может заставить браузер жертвы отправить запрос на уязвимый сайт, и cookie подставится автоматически. Именно поэтому формы защищают CSRF-токенами, а cookie помечают флагом SameSite. Но это тема для отдельной статьи.

POST + JSON: работа с API

Формы - это классика, но всё чаще фронтенд общается с бекендом напрямую через JSON-API. В этом случае тело POST-запроса - уже не form-url-encoded, а структурированный JSON, а заголовок Content-Type меняется на application/json.

Представим, что на нашем портале есть поиск по городам, и фронтенд при каждом вводе символа отправляет запрос к /api/cities:

POST /api/cities HTTP/1.1
Host: portal.example.com
Content-Type: application/json
Cookie: SESSIONID=abf1329e7d904c9fa3c4c1b8f8c21d3a

{"q":"london","limit":5}

Такой запрос легко повторить вручную:

user@linux:~$ curl -X POST \
  -H 'Content-Type: application/json' \
  -H 'Cookie: SESSIONID=abf1329e7d904c9fa3c4c1b8f8c21d3a' \
  -d '{"q":"london","limit":5}' \
  https://portal.example.com/api/cities

["London (UK)","London (CA)","London (NZ)"]

Здесь важны три момента:

  • тело - корректный JSON (кавычки двойные, без лишних запятых);

  • Content-Type: application/json говорит серверу, как парсить тело;

  • cookie по-прежнему отвечает за авторизацию: без неё backend может вернуть 401 или пустой ответ.

В DevTools такой запрос можно посмотреть и скопировать как Copy -> Copy as Fetch, а затем в консоли браузера поиграться с параметрами прямо на лету:

fetch('https://portal.example.com/api/cities', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  credentials: 'include',
  body: JSON.stringify({ q: 'os', limit: 10 }),
}).then(r => r.json()).then(console.log);

Заключение

Мы разобрали одни из основ сетевых протоколов - HTTP и немного затронули HTTPS. Спасибо всем, кто дочитал, и удачи в дальнейшем обучении или практике в кибербезопасности!

Комментарии (0)