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

Веб-основы для тестировщика

Как устроен веб под API

Я открыл страницу и попросил её сходить за данными на соседний сервис. Из браузера пришла ошибка «Failed to fetch», данные не получены. Тот же самый адрес, запрошенный не из браузера, спокойно вернул 200 и тело с балансом. Сервер отдаёт данные всегда - блокирует их браузер.

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

// Формулировки: «что происходит при вводе адреса?», «почему CORS не воспроизводится через curl?», «зачем куке HttpOnly?».

Путь запроса и где он рвётся

Запрос проходит четыре понятных шага. Первый - DNS: имя api.example.com превращается в числовой адрес машины. Второй - TCP: между твоим клиентом и этой машиной устанавливается соединение. Третий - TLS: соединение шифруется, стороны сверяют сертификат. Четвёртый - собственно HTTP-запрос и ответ приложения.

Зачем тестировщику эта лестница: она даёт порядок диагностики. «Не работает» может означать «имя не резолвится», «соединение не устанавливается», «сертификат просрочен» и «приложение вернуло 500» - это четыре разные команды и четыре разных ответственных. Разбирают снизу вверх, и до кода доходят последним.

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

DNS
превращение имени сайта в числовой адрес машины
заголовки
служебные пары «имя: значение» рядом с запросом и ответом

CORS: замер в настоящем браузере

Вернусь к опыту из начала. Источник (origin) - это сочетание протокола, домена и порта. Страница с одного источника, сервис на другом - для браузера это чужой источник, даже если оба на твоей машине и различаются только портом.

CORS (cross-origin resource sharing) - правило браузера: прежде чем отдать странице ответ от чужого источника, он проверяет, разрешил ли тот сервис такое обращение специальным заголовком. Замер: сервис без заголовка - страница получила «Failed to fetch»; тот же сервис с заголовком разрешения - страница получила {"balance":105000}; тот же адрес запросом не из браузера - 200 и те же данные.

// Отсюда вывод, который экономит дни. Воспроизвести ошибку CORS через curl или Postman нельзя. Это программы, которые шлют запросы к серверу напрямую, без страницы: они не браузеры и правило источников не применяют. Поэтому фразу разработчика «у меня в Postman работает» нельзя принимать как аргумент - Postman тут ничего не проверяет. Разбирать надо в браузере, глядя на заголовки ответа и на предварительный запрос OPTIONS, который браузер шлёт перед непростыми обращениями.

источник (origin)
протокол плюс домен плюс порт; граница правил браузера
CORS
cross-origin resource sharing: правило браузера о доступе к чужому источнику

Куки и токены: что видно снаружи

Кука - маленькая пара «имя-значение», которую сервер просит браузер запомнить и присылать обратно. Замер: сервис поставил две куки - session с признаком HttpOnly и theme без него. Скрипт на странице, запросив список кук, увидел только theme=dark. Сессионная кука для него невидима - и ровно это спасает при XSS (cross-site scripting), когда на страницу попадает чужой скрипт: украсть сессию через него не выйдет.

Остальные признаки куки тоже проверяют: Secure (передаётся только по шифрованному соединению), SameSite (не отправлять её при переходах с чужих сайтов - защита от CSRF (cross-site request forgery), когда чужая страница дёргает твой сервис от твоего имени), срок жизни, домен и путь.

// Второй способ носить личность - токен в заголовке. Я собрал такой токен и разобрал его: середина читается обычным декодированием без всякого ключа - там оказалось {"sub":"user-42","role":"user","email":"anna@mail.ru"}. При этом подменить роль на admin не вышло: подпись перестала сходиться. Вывод точный - подделать нельзя, а прочитать может кто угодно, поэтому в JWT (JSON Web Token) не кладут ничего, что нельзя показывать.

HttpOnly
признак куки: скрипту страницы она не видна
JWT
JSON Web Token: подписанный токен; закодирован, но не зашифрован

Как отвечать: «Почему ошибка CORS видна только в браузере?»

Потому что это правило самого браузера, а не запрет сервера. Работает оно так: когда скрипт на странице одного источника просит данные у другого источника, браузер перед тем, как отдать странице ответ, проверяет, разрешил ли тот сервис такое обращение ответным заголовком. Если разрешения нет - браузер сам блокирует чтение и показывает ошибку, хотя сервер запрос вполне мог обработать. Я проверял ровно этот сценарий: страница получила «Failed to fetch», а тот же самый адрес, запрошенный не из браузера, вернул двести и полные данные. Отсюда практический вывод: воспроизвести CORS через curl или Postman невозможно, они не браузеры и правило источников не применяют, у них запрос проходит всегда. Поэтому разбирать надо в браузере: смотреть предварительный запрос OPTIONS и заголовки разрешения в ответе. И когда разработчик говорит «у меня в Postman всё работает» - это не аргумент, Postman в этом месте вообще ничего не проверяет.

Почему это сильный ответ: назван корень (правило браузера, не сервера), объяснён механизм, приведено наблюдение с двумя разными исходами и сделан практический вывод про инструменты.

На чём валят

  • Разбирать CORS через curl: правило накладывает браузер, консольный клиент его не применяет.
  • Класть личные данные в токен «он же закодирован» - середина читается без ключа.
  • Сессионная кука без HttpOnly: чужой скрипт на странице заберёт её вместе с сессией.
  • Проверять выход из аккаунта только по интерфейсу - токен может остаться живым на сервере.
  • Забыть про кэш: «починили, а у пользователя по-старому» - ответ приехал из сохранённой копии.

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

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

  1. #web_fundamentals1 / 5
    Что делает DNS при обращении к сайту?
    A)Шифрует соединение между браузером и сервером для защиты передаваемых данных
    B)Переводит доменное имя (site.ru) в IP-адрес сервера
    C)Хранит на сервере сессии пользователей, чтобы сайт помнил их между запросами
    D)Распределяет входящие запросы между несколькими серверами для баланса нагрузки
    показать ответ и разбор
    +B)Переводит доменное имя (site.ru) в IP-адрес сервера

    // разбор: Компьютеры находят друг друга по IP-адресам, а люди помнят имена. DNS (система доменных имён) — распределённый справочник, который по доменному имени возвращает соответствующий IP. Браузер сначала спрашивает у DNS адрес site.ru, получает, например, 93.184.216.34, и только потом устанавливает TCP-соединение и шлёт HTTP-запрос. Для тестировщика это объясняет класс проблем: сайт «не открывается» из-за DNS (неверная запись, кэш), хотя сам сервер жив.

  2. #web_fundamentals2 / 5
    Как cookie и сессии дают HTTP «память» о пользователе?
    A)Cookie хранит на клиенте пароль пользователя и шлёт его серверу при каждом запросе
    B)Сессии хранятся в DNS-записи домена, откуда сервер и подтягивает данные пользователя
    C)Сервер выдаёт cookie с идентификатором сессии
    D)Сервер запоминает пользователя по его IP-адресу, поэтому cookie для этого не нужны
    показать ответ и разбор
    +C)Сервер выдаёт cookie с идентификатором сессии

    // разбор: HTTP сам по себе не помнит предыдущие запросы. Чтобы связать их в диалог, при логине сервер создаёт сессию и отдаёт клиенту cookie с её идентификатором (Set-Cookie). Браузер автоматически прикладывает эту cookie к каждому последующему запросу к домену, и сервер по id находит сессию — так пользователь остаётся «залогинен». Для тестировщика это точка проверок: живёт ли сессия, истекает ли по таймауту, привязана ли к пользователю, безопасны ли флаги cookie (HttpOnly, Secure).

  3. #web_fundamentals3 / 5
    Почему HTTP называют протоколом без состояния (stateless)?
    A)HTTP не хранит состояние, потому что данные шифруются и стираются после ответа
    B)Протокол помнит все прошлые запросы клиента и накапливает их в памяти сервера
    C)Stateless означает, что сервер отвечает без тела, одним лишь кодом статуса ответа
    D)Каждый запрос независим и не опирается на предыдущие
    показать ответ и разбор
    +D)Каждый запрос независим и не опирается на предыдущие

    // разбор: Stateless означает, что сервер не обязан помнить контекст между запросами: каждый HTTP-запрос самодостаточен и обрабатывается независимо от прошлых. Это упрощает масштабирование — любой запрос может уйти на любой сервер за балансировщиком. «Память» о пользователе и сессии организуют поверх протокола: клиент прикладывает cookie/токен, сервер по нему поднимает состояние из хранилища. Для тестирования важно: нельзя полагаться, что второй запрос «помнит» первый сам по себе — нужное состояние надо передавать явно.

  4. #web_fundamentals4 / 5
    Что даёт использование HTTPS вместо HTTP?
    A)Шифрует трафик между клиентом и сервером (TLS)
    B)Ускоряет загрузку страниц за счёт сжатия трафика между клиентом и сервером
    C)Хранит cookie пользователя на сервере, освобождая память браузера клиента
    D)Позволяет серверу самому инициировать соединение с клиентом в обход браузера
    показать ответ и разбор
    +A)Шифрует трафик между клиентом и сервером (TLS)

    // разбор: HTTPS — это HTTP поверх TLS. Он решает три задачи: конфиденциальность (трафик зашифрован, посредник не прочтёт пароли и данные), целостность (подмену по дороге видно) и аутентичность сервера (сертификат подтверждает, что это настоящий site.ru, а не подделка). По HTTP всё идёт открытым текстом и легко перехватывается в общей сети. Для тестировщика: проверяют, что чувствительные эндпоинты работают по HTTPS, сертификат валиден, а редирект с HTTP на HTTPS настроен.

  5. #web_fundamentals5 / 5
    Что такое CORS и зачем он нужен?
    A)CORS шифрует кросс-доменные запросы, защищая передаваемые между сайтами данные
    B)Правило браузера: скрипт с одного домена не обращается к API другого без явного разрешения сервера (заголовки)
    C)CORS ускоряет запросы к чужому API за счёт кэширования ответов в браузере
    D)CORS применяется на сервере и блокирует запросы ещё до их отправки клиентом
    показать ответ и разбор
    +B)Правило браузера: скрипт с одного домена не обращается к API другого без явного разрешения сервера (заголовки)

    // разбор: CORS (Cross-Origin Resource Sharing) — механизм безопасности браузера. По умолчанию действует same-origin policy: JS-код со страницы domainA не может читать ответы API на domainB. CORS позволяет серверу B явно разрешить это, вернув заголовки (Access-Control-Allow-Origin и др.); при «сложных» запросах браузер сперва шлёт preflight OPTIONS. Важный нюанс для тестировщика: CORS проверяет именно браузер — из Postman или бэкенда запрос пройдёт и без него. Поэтому баг «работает в Postman, но не в браузере» часто про CORS.

дальше

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

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