Сети и веб
TCP/IP и порты: как сервисы слушают на вашей машине
Порт — это пронумерованная дверь на вашей машине. Разбираем, как TCP и UDP работают с портами, затем запускаем, находим и проверяем локальный сервер на 127.0.0.1.
Разбираем настоящий обмен HTTP построчно: методы, URL, заголовки, классы кодов состояния и cookie, с проверкой через curl на локальном сервере.
HTTP — это протокол, которым браузеры, приложения и API обмениваются сообщениями. RFC 9110 определяет смысл сообщений: методы, коды состояния, поля заголовков и так далее. RFC 9112 определяет, как сообщения HTTP/1.1 выглядят в виде обычного текста в соединении TCP. Обзор MDN короче и написан с точки зрения браузера, его удобно читать вместе с RFC.
Схема простая. Клиент отправляет запрос, сервер возвращает один ответ. Обмен всегда начинает клиент. Обычный HTTP ничего не шифрует и не помнит, кто вы, между запросами. Шифрование добавляет TLS, о котором рассказывает следующая статья. Память между запросами дают cookie и токены, до них мы дойдём ниже.
Большая часть поведения веба, которое вам предстоит изучать, проходит через HTTP. Форма отправляет значение в теле, API доверяет заголовку, редирект уводит пользователя в другое место, кэш хранит не ту копию страницы. Когда позже вы будете изучать трафик через прокси вроде OWASP ZAP, он покажет эти же сообщения. Если вы не умеете читать сырой запрос, вывод прокси мало чем поможет.
Запрос HTTP/1.1 — это обычный текст такого вида:
GET / HTTP/1.1
Host: 127.0.0.1:8000
User-Agent: curl/8.12.1
Accept: */*Первая строка называется строкой запроса: метод, цель запроса и версия протокола. Каждая следующая строка содержит поле заголовка, записанное как name: value. Пустая строка завершает секцию заголовков. Если у сообщения есть тело, оно идёт после этой пустой строки. При передаче каждая строка заканчивается на CRLF (возврат каретки и перевод строки), и редакторы это от вас скрывают.
Ответ устроен так же, но наверху стоит строка состояния: версия, код состояния и текстовое пояснение.
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 15
<h1>hello</h1>Метод говорит, что клиент хочет сделать. RFC 9110 определяет GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS и TRACE. PATCH описан в RFC 5789.
При чтении трафика важны два свойства:
Эти свойства описывают обещанный смысл метода. Что произойдёт на самом деле, всё равно решает сервер. Обработчик GET, удаляющий запись, нарушает контракт, и ревьюер безопасности будет искать именно такую ошибку.
Возьмите URL http://127.0.0.1:8000/missing?lang=en#top и разделите его на части:
http, сообщает клиенту, какой протокол использовать.127.0.0.1:8000, хост и необязательный порт./missing.lang=en, после ?.top, после #.Схему и фрагмент клиент на сервер не отправляет. Строка запроса HTTP содержит путь и параметры. Authority попадает в заголовок Host. HTTP/1.1 требует Host, потому что на одном IP-адресе может находиться много сайтов, и по этому заголовку сервер решает, какой сайт должен ответить.
Имена заголовков не зависят от регистра, поэтому Content-Type и content-type означают одно и то же поле (RFC 9110, раздел 5.1). Со значениями иначе: многие из них чувствительны к регистру, так что обратного не предполагайте.
Заголовки запроса, которые встречаются чаще всего:
Host: обязателен в каждом запросе HTTP/1.1. RFC 9112, раздел 3.2, требует, чтобы сервер отвечал 400, если заголовка нет.User-Agent: клиентское ПО.Accept: медиатипы, которые может обработать клиент.Cookie: cookie, которые браузер отправляет обратно на сервер.Authorization: учётные данные, часто bearer-токен.Content-Type и Content-Length: описывают тело, если оно есть.Заголовки ответа, которые встречаются чаще всего:
Content-Type: медиатип тела, например text/html.Content-Length: размер тела в байтах.Date: часы сервера, в GMT.Server: имя программы. Многие серверы его отдают. В продакшене его часто скрывают.Last-Modified: когда сервер в последний раз менял ресурс.Set-Cookie: просит браузер сохранить cookie.Заголовки — это данные, которые контролирует отправитель. Браузер отправляет то, что заложено в его коде, а скрипт или прокси может отправить что угодно. Классический пример — X-Forwarded-For: его добавляет прокси, но отправить его может и клиент. Никогда не доверяйте заголовку только потому, что он выглядит официально.
Код состояния состоит из трёх цифр. Первая цифра задаёт класс, как определено в RFC 9110, раздел 15:
| Класс | Значение | Примеры |
|---|---|---|
| 1xx | Информационный. Запрос получен, обработка продолжается | 100 Continue |
| 2xx | Успех | 200 OK, 201 Created, 204 No Content |
| 3xx | Перенаправление. Клиент должен сделать ещё один шаг | 301 Moved Permanently, 304 Not Modified |
| 4xx | Ошибка клиента. С запросом что-то не так | 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 405 Method Not Allowed |
| 5xx | Ошибка сервера. Сервер не смог обработать корректный запрос | 500, 501 Not Implemented, 502, 503 |
Даже если клиент не знает конкретный код, он может действовать по классу. 4xx означает, что запрос надо изменить. 5xx означает, что тот же запрос может сработать позже.
Два кода часто путают. 401 означает, что в запросе нет действующих учётных данных. 403 означает, что сервер понял запрос и отказывает в нём, даже при наличии учётных данных.
Текстовое пояснение, например Not Found, сервер выбирает сам. RFC 9112, раздел 4, говорит, что клиентам следует его игнорировать. Пишите логику по числу и никогда по словам.
Cookie не входят в RFC 9110. Они определены в RFC 6265. Цикл выглядит так:
Set-Cookie: session=abc123; Path=/; HttpOnly; Secure; SameSite=Lax.Cookie: session=abc123.Сессионная cookie — это идентификатор, который выдаёт сервер. Паролем он не считается и личной тайной пользователя тоже. Тот, у кого есть это значение, обычно может действовать от имени сессии, пока она не истечёт или не будет отозвана. HttpOnly не даёт скриптам страницы прочитать значение. Secure разрешает отправку только через HTTPS. SameSite ограничивает случаи, когда браузер отправляет cookie в межсайтовых запросах. К этим атрибутам мы вернёмся в статьях о веб-безопасности.
Нужны curl и Python 3. В Windows 11 с Git Bash есть curl, в большинстве систем Linux и macOS тоже. Сервером будет встроенный в Python http.server. Запускайте его только на своей машине с привязкой к 127.0.0.1 и никогда не открывайте в сеть.
Вывод ниже получен при запуске этих команд на Windows 11 с Git Bash, curl 8.12.1 и Python 3.12.10. На этой машине python3 не было в пути Git Bash, поэтому сервер запускался по полному пути к исполняемому файлу Python 3.12. Используйте python3, py -3 или python, смотря что работает в вашей системе. Даты, время и строки версий у вас будут другими. Индикатор прогресса curl из вывода ниже удалён, а длинные HTML-тела сокращены.
В первом терминале:
mkdir httplab && cd httplab && echo '<h1>hello</h1>' > index.html
python3 -m http.server 8000 --bind 127.0.0.1Оставьте этот терминал открытым. Сервер пишет по одной строке лога на запрос, так что каждый запрос, отправленный ниже, появится здесь.
Во втором терминале:
curl -v http://127.0.0.1:8000/Вывод:
* Trying 127.0.0.1:8000...
* Connected to 127.0.0.1 (127.0.0.1) port 8000
* using HTTP/1.x
> GET / HTTP/1.1
> Host: 127.0.0.1:8000
> User-Agent: curl/8.12.1
> Accept: */*
>
* Request completely sent off
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: SimpleHTTP/0.6 Python/3.12.10
< Date: Sun, 08 Nov 2026 08:16:48 GMT
< Content-type: text/html
< Content-Length: 15
< Last-Modified: Sun, 08 Nov 2026 08:13:48 GMT
<
<h1>hello</h1>
* shutting down connection #0Читайте его в таком порядке:
*, содержат заметки curl о соединении.>, показывают, что отправил curl. Curl сам добавил Host, User-Agent и Accept.<, показывают, что вернул сервер.> завершает заголовки запроса. Пустая строка < завершает заголовки ответа.<h1>hello</h1> — это тело. Content-Length: 15 соответствует файлу, потому что echo добавил после тега перевод строки.HTTP 1.0, assume close after body показывает несовпадение. Curl запросил HTTP/1.1, а сервер ответил по HTTP/1.0. Простой сервер Python говорит на HTTP/1.0, поэтому curl закрывает соединение, как только прочитает тело.curl -i http://127.0.0.1:8000/missingФлаг -i печатает заголовки ответа вместе с телом. Вывод с сокращённым HTML-телом:
HTTP/1.0 404 File not found
Server: SimpleHTTP/0.6 Python/3.12.10
Date: Sun, 08 Nov 2026 08:16:49 GMT
Connection: close
Content-Type: text/html;charset=utf-8
Content-Length: 335
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error response</title>
</head>
<body>
<h1>Error response</h1>
<p>Error code: 404</p>
<p>Message: File not found.</p>
<p>Error code explanation: 404 - Nothing matches the given URI.</p>
</body>
</html>Посмотрите на первый терминал. В логе появилось:
127.0.0.1 - - [08/Nov/2026 05:16:49] code 404, message File not found
127.0.0.1 - - [08/Nov/2026 05:16:49] "GET /missing HTTP/1.1" 404 -Код состояния 404 относится к классу ошибок клиента: путь не соответствует никакому ресурсу. Пояснение File not found сформулировал сам сервер. Тело представляет собой HTML-страницу, которую сервер сгенерировал для ошибки. Любой статус может нести любое тело, поэтому читайте и то, и другое.
В строке лога стоит 05:16, а в заголовке Date стоит 08:16. Лог использует местное время, заголовок использует GMT, поэтому они различаются на величину местного смещения.
curl -i -X POST http://127.0.0.1:8000/Вывод с сокращённым HTML-телом:
HTTP/1.0 501 Unsupported method ('POST')
Server: SimpleHTTP/0.6 Python/3.12.10
Date: Sun, 08 Nov 2026 08:16:49 GMT
Connection: close
Content-Type: text/html;charset=utf-8
Content-Length: 357
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error response</title>
</head>
<body>
<h1>Error response</h1>
<p>Error code: 501</p>
<p>Message: Unsupported method ('POST').</p>
<p>Error code explanation: 501 - Server does not support this operation.</p>
</body>
</html>В строке лога написано code 501, message Unsupported method ('POST'). Флаг -X меняет только метод. Curl отправил те же заголовки, что и раньше.
В простом сервере вообще нет обработчика POST, так что 501 Not Implemented здесь честный ответ. RFC 9110, раздел 15.6.2, определяет 501 для сервера, который не поддерживает функцию, нужную для запроса. Настоящее приложение, которое знает ресурс, но отказывает в POST на нём, обычно ответило бы 405 Method Not Allowed. Ответ 405 должен включать заголовок Allow со списком методов, которые принимает ресурс.
curl -I http://127.0.0.1:8000/index.htmlВывод:
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.12.10
Date: Sun, 08 Nov 2026 08:16:50 GMT
Content-type: text/html
Content-Length: 15
Last-Modified: Sun, 08 Nov 2026 08:13:48 GMT
Флаг -I отправляет запрос HEAD. Статус и заголовки совпадают с ответом на GET, но тела нет. Content-Length всё ещё показывает 15, потому что описывает представление, которое вернул бы GET. Это поведение определено в RFC 9110, раздел 9.3.2. В логе стоит "HEAD /index.html HTTP/1.1" 200 -.
HEAD пригодится, когда надо проверить, существует ли файл, какого он размера или когда менялся в последний раз, и при этом не скачивать его.
curl -v -H 'Cookie: session=demo123' http://127.0.0.1:8000/Нужные строки вывода:
> GET / HTTP/1.1
> Host: 127.0.0.1:8000
> User-Agent: curl/8.12.1
> Accept: */*
> Cookie: session=demo123
>
< HTTP/1.0 200 OK
< Server: SimpleHTTP/0.6 Python/3.12.10
< Date: Sun, 08 Nov 2026 08:18:28 GMT
< Content-type: text/html
< Content-Length: 15
< Last-Modified: Sun, 08 Nov 2026 08:13:48 GMT
< Строка тела здесь опущена. Сервер ответил теми же 200, что и раньше, потому что логики для cookie в нём нет и заголовок он игнорирует. Ради этого упражнение и задумано. Браузер отправляет cookie, но что они значат, решает сервер. Сервер, который никогда не устанавливает cookie, не отреагирует на присланную вами.
Когда закончите, нажмите Ctrl+C в первом терминале.
Host, User-Agent, Cookie и X-Forwarded-For приходят со стороны клиента. Проверяйте их на сервере.127.0.0.1.-X изменит заголовки. Он меняет только метод. Заголовки добавляются через -H.Content-Length и Transfer-Encoding.:8000 в URL.Прокси вроде ZAP показывает эти же сообщения, поэтому статья служит основой для следующих материалов о веб-уязвимостях. API NotHoly на api.notholy.com/v1 тоже HTTP API с телами в JSON, работающий через TLS, так что описанные заголовки и коды состояния применимы и при обращении к нему.
Сети и веб
Порт — это пронумерованная дверь на вашей машине. Разбираем, как TCP и UDP работают с портами, затем запускаем, находим и проверяем локальный сервер на 127.0.0.1.
Сети и веб
TLS 1.3 даёт HTTPS конфиденциальность, целостность и аутентификацию сервера. Разбираем рукопожатие, цепочки сертификатов и то, чего замок в адресной строке не доказывает.
Сети и веб
Как DNS превращает имена в адреса: резолверы, авторитетные серверы, кеширование по TTL, записи A, AAAA, CNAME, MX и TXT и безопасный запрос.