vyvchy
    Теми розділу

    03 · Веб і мережі для AQA

    HTTPS, TLS і безпека

    Зміст

    Запит із браузера летить через пристрої, яких ви не контролюєте: домашній роутер, провайдера, магістральні вузли, балансувальники на боці сервера. Кожен фізично здатен прочитати чи підмінити те, що передається, — і HTTPS існує рівно для того, щоб зробити це недосяжним на практиці. Для AQA це не абстракція: сертифікат стенда, у , кукі з HttpOnly, яку не видно з JavaScript, сувора політика, що блокує ваш власний допоміжний скрипт, — усе це щодня вирішує, зелений прогін чи «падає без причини».

    Ця глава — канон механіки на сайті: інші глави посилаються сюди по те, як улаштований захищений канал і політики браузера навколо нього. Глибші перевірки — набори шифрів і конфігурація транспорту, повна валідація сертифіката, склад , типи й пейлоади XSS — предмет розділу про безпеку. Мета одна: за хвилину сказати, який саме шар зупинив запит — TLS, політика браузера чи застосунок — і чому.

    HTTPS — це HTTP усередині TLS

    HTTPS — це звичайний HTTP, загорнутий у криптографічний шар TLS (Transport Layer Security): спершу встановлюється захищений канал, і вже всередині нього ходить знайомий HTTP-трафік. Семантика при цьому не змінюється — ті самі методи, заголовки й коди статусів, які розбирає глава «HTTP: методи, структура, заголовки».

    Головна мета TLS — дати захищений канал між двома сторонами, і від транспорту він вимагає лише надійного впорядкованого потоку даних; на практиці це TCP. Сам протокол має дві частини: (handshake) автентифікує сторони й узгоджує параметри та ключі, а record-протокол цими параметрами захищає трафік.

    Три гарантії й асиметрія лише на старті

    TLS дає рівно три гарантії, і плутати їх не варто — вони закривають різні . Автентифікація: сервер автентифікується завжди, клієнт — опційно. Конфіденційність: після встановлення каналу дані видно лише кінцевим точкам. Цілісність: зміну даних у дорозі буде виявлено.

    Ключова ідея рукостискання — асиметрична криптографія працює лише на старті, щоб сторони узгодили спільний симетричний ключ сесії; далі трафік шифрується швидким . Ефемерний обмін ключами дає (perfect forward secrecy). Свою особу сервер доводить трьома повідомленнями: Certificate, CertificateVerify — підпис усього рукостискання приватним ключем — і Finished. У TLS 1.3 усе після ServerHello уже зашифроване, зокрема й сам сертифікат.

    СерверКлієнтСерверКлієнтПеревіряє сертифікат і підпис рукостисканняОбидві сторони мають спільний ключ сесіїClientHello — параметри, матеріал для обміну ключамиServerHello, Certificate, CertificateVerify, FinishedHTTP-запит під симетричним шифруваннямHTTP-відповідь під симетричним шифруваннямСерверКлієнтСерверКлієнтПеревіряє сертифікат і підпис рукостисканняОбидві сторони мають спільний ключ сесіїClientHello — параметри, матеріал для обміну ключамиServerHello, Certificate, CertificateVerify, FinishedHTTP-запит під симетричним шифруваннямHTTP-відповідь під симетричним шифруванням

    Опційність клієнтської автентифікації має практичний наслідок: за замовчуванням сервер не знає, хто до нього прийшов, доки користувач не увійде вже всередині каналу. Саме цю дірку закриває (mutual TLS, mTLS), де сторони автентифікують одна одну, — і в тестах він вимагає окремо налаштованого клієнтського сертифіката.

    Наша практика (не канон). Специфікація каже лише «provide forward secrecy». Явна назва ECDHE і звичне формулювання «витік приватного ключа сервера пізніше не розшифрує раніше записаний трафік» — наші робочі формули, у джерелі їх немає.

    Версії: «заборонено» і «застаріло» — різні слова

    Тут кандидати найчастіше говорять з памʼяті, а точна відповідь коротка. Чинна версія за специфікацією одна — TLS 1.3. RFC 8446 обсолетить попередницю і в шапці, і в тексті: «This document supersedes and obsoletes previous versions of TLS, including version 1.2». Тобто називати TLS 1.2 «актуальною версією» з джерел не випливає.

    Але TLS 1.2 і не заборонений — на відміну від решти. Заборони адресні, і кожна має власний документ IETF:

    ВерсіяСтатусДокумент
    TLS 1.3чиннаRFC 8446
    TLS 1.2обсолетнута, але не забороненаRFC 8446
    TLS 1.1 і TLS 1.0MUST NOT be usedRFC 8996
    SSL 3.0MUST NOT be used, узгодження забороненеRFC 7568
    SSL 2.0клієнту заборонено надсилати CLIENT-HELLO цього форматуRFC 6176

    Звідси й правильна підстава практичної межі «TLS 1.2 і вище»: не «актуальність», а відсутність заборони. І тонкість, яку легко втратити при переказі: заборонено узгоджувати старий протокол, а не розпізнавати його байти — сервер MAY і далі приймати повідомлення у форматі SSL 2.0, «Note that this does not contradict the prohibition against actually negotiating the use of SSL 2.0».

    Наша практика (не канон). Слово «допустима» щодо TLS 1.2 — наше: специфікації оперують обсолетністю й заборонами, а категорії «допустимість» у них немає.

    Що посередник бачить, а що ні

    TLS шифрує тіло й заголовки HTTP, але не приховує сам факт зʼєднання. бачить IP-адресу сервера і — через SNI (Server Name Indication) у рукостисканні — доменне імʼя, до якого ви йдете. Конкретний шлях, параметри запиту, кукі й тіло лишаються закритими. Тобто «на який сайт ви пішли» здебільшого видно, а «що саме ви там відкрили» — ні.

    Звідси випливає пастка. Секрет у query-рядку не захищений навіть по HTTPS — не тому, що канал слабкий, а тому, що повний URL осідає поза каналом: у логах серверів і , в історії браузера, у заголовку Referer і в аналітиці. Місце секрету — заголовок Authorization зі схемою Bearer або тіло POST.

    Наша практика (не канон). Того, що посередник бачить SNI у відкритому вигляді, RFC 6066 прямо не каже — це наш висновок із того, що імʼя передається в ClientHello до встановлення захисту.

    Сертифікат стенда — і чому прогін падає вранці

    Сертифікат — файл формату X.509, який повʼязує доменне імʼя з відкритим ключем сервера. Браузер перевіряє ланцюг підписів до довіреного кореня, збіг домену з полем SAN (Subject Alternative Name), строк дії та відкликання. Якщо хоч одна перевірка не проходить — зʼєднання не встановлюється, і жодного застосунку тест не побачить.

    На dev і staging сертифікат часто самопідписаний — для внутрішнього стенда це звична практика. Обходити помилку кліком «Advanced → Proceed» у прогоні незручно й ненадійно, тому інструменти дають прапорці: ignoreHTTPSErrors у Playwright, acceptInsecureCerts у WebDriver, -k у curl.

    // тільки для проєкту, де self-signed справді очікуваний
    export default defineConfig({
      projects: [
        {
          name: 'staging',
          use: { baseURL: 'https://staging.internal', ignoreHTTPSErrors: true },
        },
      ],
    });

    Компроміс усвідомлений: вимкнена перевірка робить тест сліпим до реальних помилок конфігурації сертифіката. Тому вмикають точково, а не глобально «щоб не заважало». Кращий варіант — додати кореневий сертифікат внутрішнього CA у сховище довіри агента CI: тоді ланцюг стає валідним по-справжньому.

    Наша практика (не канон). Прострочений уночі сертифікат стенда — реальна причина масового «падіння» тестів уранці; це наше спостереження, а не норма документа. Наслідок практичний: перш ніж лізти в код, відкрийте середовище руками й гляньте на значок біля адреси.

    HSTS: схема переписується ще до мережі

    Strict-Transport-Security — заголовок, яким сервер каже браузеру ходити на цей домен лише захищено. Перше: у HTTP-відповіді він не працює. Специфікація прямо забороняє його там надсилати, а браузер такий заголовок ігнорує — отже, перевіряти треба на HTTPS-відповіді, і заголовок у відкритій відповіді є порушенням, а не зайвою обережністю.

    # так він виглядає у відповіді по HTTPS: час життя політики і поширення на піддомени
    HTTP/1.1 200 OK
    Strict-Transport-Security: max-age=31536000; includeSubDomains

    Далі найважливіше для автотестів. Коли браузер збирається завантажити будь-який http-URI відомого HSTS-хоста, він MUST замінити схему на https, а явний порт 80 — на 443, і це відбувається до першого мережевого запиту. Тому базовий URL із жорстко зашитим http:// на такому домені мовчки перепишеться: перевіряти треба фактичну схему у відповіді, а не ту, що ви набрали. Сам факт редиректу httphttps перевіряється інакше — автопрямування вимикають і проміжний код разом із Location (постійні редиректи це 301 і 308).

    Ще дві межі, які економлять години. Політика переживає зникнення заголовка: хост лишається відомим HSTS-хостом до кінця max-age, тож «заголовок прибрали» видно в заголовках, а не в поведінці. І на такому хості зникає «клікнути далі» повз помилку сертифіката — браузер не пропонує продовжити, а специфікація вимагає обірвати зʼєднання за будь-якої помилки транспорту. Директива preload при цьому незворотна — «can have PERMANENT CONSEQUENCES».

    Змішаний контент: «елемент не зʼявився без причини»

    Змішаний контент (mixed content) — це коли сторінка завантажена по HTTPS, а частину ресурсів тягне по HTTP. Значок обіцяє захист, а частина трафіку йде відкрито: такий ресурс можна прочитати або підмінити, і найнебезпечніші тут скрипти — вони змінюють будь-що на сторінці.

    Чинний поділ — на оновлюваний (upgradable) і блокований (blockable) вміст. Оновлюваний запит браузер сам переписує зі схеми http на https; якщо сервер там ресурсу не віддає, запит просто зазнає невдачі — відкоту на HTTP немає. Блокованим є все, що не оновлюване: <script src>, стилі через <link href>, <iframe src>, а також fetch() і XMLHttpRequest. Старі назви «активний» і «пасивний» джерело відносить до ранішої редакції специфікації — у баг-репорті надійніше писати «блокований» і «оновлюваний».

    Пастка

    Межа проходить не лише по типу ресурсу, а й по способу задання джерела. До оновлюваних належить <img>, чиє джерело задано атрибутом src (включно з SVG-документами), але той самий <img> зі srcset чи всередині <picture> стоїть у переліку блокованих. Плюс окреме правило поверх усього: запити, які інакше були б підвищені, блокуються, якщо хостом у URL є IP-адреса, а не доменне імʼя.

    Слід у консолі є завжди: браузер попереджає і про підвищення, і про блокування. Тому діагностика «елемент не зʼявився» починається з консолі, а не з нарощування :

    page.on('console', (msg) => {
      if (msg.type() === 'error') console.log('BROWSER:', msg.text());
    });

    Три межі, на яких плутаються найчастіше. Локальний стенд блокувань не відтворює: file:-URL і loopback-адреси (http://127.0.0.1/, http://localhost/) вважаються захищеними , тож перевіряти треба на адресі з доменним імʼям. Навігація з HTTPS-сторінки на HTTP-сторінку змішаним контентом не вважається — створюється новий контекст. І симптом не завжди виглядає як «немає картинки»: заблокованим може бути fetch, і тоді сторінка лишається без даних. Масово перевести підресурси на HTTPS вміє директива CSP upgrade-insecure-requests.

    Той самий механізм пояснює річ, яку часто заводять як баг реалтайму: зі сторінки на https не відкрити ws://. Схем WebSocket дві, і поверх TLS працює саме wss; деталі — глава «WebSockets і реалтайм».

    Наша практика (не канон). Ланцюжок «заблокований скрипт → тест не бачить елемент → флак» і типовий корінь — зашитий http:// у конфізі після переїзду стенда на HTTPS — це наші висновки; джерело описує лише блокувальну поведінку.

    Походження: схема — це безпекова межа

    Походження (origin) — це трійка «схема + хост + порт». Дві адреси належать одному походженню тоді й лише тоді, коли збігаються всі три складові, тож інша схема — це вже інше походження. Типовий порт схеми https — 443, і в адресі його опускають: https://app.example.com і https://app.example.com:443 — те саме походження.

    Навіщо це в главі про транспорт. Браузер групує адреси в походження як домени захисту: доступ до обʼєктів дозволено тоді й лише тоді, коли адреси належать одному походженню. Звідси формальне означення XSS: документ несе повні повноваження свого походження, будь-який підвантажений у нього скрипт дістає доступ до всіх його ресурсів — а застосунок, який необережно домішує недовірений вміст, зливає йому ці повноваження. Практичний наслідок: переїзд стенда з http на https — не косметика, а зміна межі безпеки. Саму й її послаблення розбирає глава «CORS і політика одного походження».

    CSP: хто вирішує, що сторінці можна виконувати

    CSP (Content Security Policy) — механізм, яким розробник керує тим, які ресурси конкретна сторінка може завантажувати й виконувати. Призначення — зменшити вразливостей через вмісту, насамперед XSS. При цьому специфікація застерігає: CSP не задумана як перша лінія захисту — це механізм , який зменшує шкоду від успішної інʼєкції, але не замінює й .

    Каналів доставки два, і це головна практична деталь. Основний — заголовок відповіді. Але документ може доставити політику й розміткою: «A Document may deliver a policy via one or more HTML meta elements whose http-equiv attributes are an ASCII case-insensitive match for the string "Content-Security-Policy"». Канали не рівносильні: у meta частина можливостей не працює, а сама політика не діє назад — «policies in meta elements are not applied to content which precedes them», тож скрипт вище за неї вона не зупинить.

    Для автотестів це має цілком приземлений бік: сувора політика вміє блокувати те, що робить сам тест. Звична інʼєкція допоміжного скрипта чи вставка inline-обробника в DOM під політикою без 'unsafe-inline' і 'unsafe-eval' не спрацює, а в консолі зʼявиться повідомлення про порушення; для таких випадків у Playwright є опція bypassCSP. Склад директив і — предмет розділу про безпеку; де саме шукати слід порушення, розбирає глава «DevTools: вкладка Network і дебаг»: у мережевій панелі буває зелено, а повідомлення про порушення лежить у консолі.

    Наша практика (не канон). Порада шукати політику в обох місцях — наша: джерело описує канали, а не діагностику. Наші й два висновки поруч: page.evaluate під суворою політикою здебільшого проходить, бо код їде через протокол налагодження, а page.waitForFunction без 'unsafe-eval' може падати; і корисно мати зворотну перевірку — що заголовок узагалі присутній, а список джерел не «поплив» після релізу.

    XSS — рівно стільки, щоб зрозуміти CSP

    XSS (Cross-Site Scripting) — різновид інʼєкційних атак, коли шкідливий скрипт потрапляє на загалом безпечний і довірений сайт, а застосунок доставляє його іншому користувачеві. Корінь один: дані з недовіреного джерела потрапляють у згенерований вивід без валідації чи екранування, і браузер жертви виконує їх як код — відрізнити внесений інʼєкцією скрипт від легітимного він не може.

    Оскільки скрипт вважається «своїм», він отримує доступ до кукі, сесійних токенів та іншої чутливої інформації, збереженої браузером для цього сайту, і може переписати вміст сторінки. Найтяжчий наслідок — розкриття сесійної кукі, а отже, та облікового запису жертви. Захист — кодування виводу, санітизація вводу й фреймворки, які екранують вставки за замовчуванням; CSP додається другим рубежем. Типи, пейлоади й сінки — предмет розділу про .

    Кукі під кутом транспорту

    Без TLS вміст заголовків Cookie і Set-Cookie передається у відкритому вигляді — звідси й вимога атрибута Secure разом із HTTPS: із ним кукі йде лише захищеним каналом, по відкритому HTTP не піде (виняток — localhost). HttpOnly обмежує кукі лише HTTP-запитами: скриптові API браузера її не бачать, і це базовий захист сесійного токена від крадіжки через XSS.

    Прямий наслідок для тестів: кукі з HttpOnly не прочитати ні через document.cookie, ні через page.evaluate — це не баг тесту, а задум. Читати й підставляти таку кукі треба на рівні :

    const session = (await context.cookies()).find((c) => c.name === 'session');

    Те саме стосується ізоляції: очищення document.cookie не прибере HttpOnly-кукі. Решта атрибутів (зокрема SameSite) і сховища браузера — глава «Кукі, сесії та сховище браузера»; CSRF — глава «Автентифікація та авторизація».

    Перехоплення трафіку в тестах — легальний MITM

    Посередник бачить увесь трафік, що крізь нього йде, тож може не лише передавати його, а й інспектувати та змінювати. Але звичайний проксі не бачить і не змінює TLS-потік: запит CONNECT лише просить відкрити трубу між клієнтом і сервером. Щоб зазирнути всередину, інструмент прикидається сервером для клієнта й клієнтом для сервера — саме тому перехоплення HTTPS вимагає власного сертифіката, а браузер приймає його лише тоді, коли корінь цього сертифіката лежить у сховищі довіри машини. Прийом той самий, що й внутрішній CA на агенті CI, тільки мета інша.

    Дзеркальний бік корисно тримати в голові при діагностиці: вузол, якого клієнт не обирав, — корпоративний фаєрвол чи публічний Wi-Fi — протокольно «indistinguishable (at a protocol level) from an on-path attacker», і поведінка запиту може змінитися без жодної зміни в застосунку. Той самий принцип вбудований у фреймворки автотестів, тільки перехоплення живе всередині браузера; механіку розбирає глава «Перехоплення й мокання мережі».

    Типові помилки

    • Виглядає як «TLS ховає все», а насправді посередник бачить IP-адресу сервера і доменне імʼя через SNI; закриті лише шлях, параметри, кукі й тіло.
    • Виглядає як «TLS 1.2 актуальний», а насправді RFC 8446 його обсолетнув, а практична межа «1.2 і вище» стоїть на відсутності заборони.
    • Виглядає як «тест перевіряє редирект httphttps», а насправді на HSTS-хості браузер переписує схему ще до мережевого запиту: http-запит на сервер не піде, тож і редиректу у відповіді не буде.
    • Виглядає як «картинки завжди підвищуються до HTTPS», а насправді підвищується <img> із src, а той самий <img> зі srcset або в <picture> блокується; запит до хоста-IP не підвищується взагалі — його блокують.
    • Виглядає як «CSP немає — у заголовках порожньо», а насправді політику могли доставити елементом meta в розмітці.
    • Виглядає як баг тесту («не читається кукі сесії»), а насправді це HttpOnly працює як задумано: така кукі читається лише на рівні контексту браузера.

    Підсумок

    1. HTTPS не змінює HTTP — він кладе його всередину TLS, а TLS вимагає від транспорту лише надійного впорядкованого потоку.
    2. Асиметрія працює лише на старті, щоб узгодити симетричний ключ сесії; ефемерний обмін дає пряму секретність, сервер автентифікується завжди, клієнт — опційно.
    3. Заборони на версії адресні: TLS 1.0/1.1, SSL 3.0 і SSL 2.0 заборонені поіменними документами IETF, а TLS 1.2 обсолетнутий, але не заборонений.
    4. Схема — безпекова межа: вона входить у походження, тож http і https для браузера різні світи; звідси ростуть змішаний контент, HSTS і поведінка кукі.
    5. Більшість «падінь без причини» на цьому шарі має слід у консолі або в заголовках: заблокований ресурс, порушення політики, переписана схема, прострочений сертифікат.

    Можливі питання

    • «Чим HTTPS відрізняється від HTTP?» Перевіряють, чи ви бачите шар, а не лише замочок: це той самий HTTP усередині TLS, і TLS дає три гарантії — автентифікацію сервера, конфіденційність, цілісність.
    • «Що з HTTPS-трафіку все одно бачить посередник?» Питання на точність. Очікують IP і доменне імʼя через SNI — і те, що шлях, параметри, кукі й тіло закриті; бонус — чому секрет у query-рядку небезпечний навіть по HTTPS.
    • «Навіщо асиметрія, якщо трафік шифрується симетрично?» Дивляться на роль рукостискання: асиметрія узгоджує ключ сесії й автентифікує сервера, далі працює швидкий симетричний алгоритм.
    • «Тест не бачить елемент, у консолі — попередження про змішаний контент. Що сталося?» Міні-кейс на порядок дій: спершу причина в консолі, потім різниця «оновлюваний проти блокованого», і лише тоді код чи таймаути.
    • «Що таке CSP і як сувора політика впливає на автотести?» Очікують механізм і наслідок: політика керує тим, що сторінці можна вантажити й виконувати, доставляється заголовком або елементом meta — і вміє заблокувати вставку допоміжного скрипта самим тестом.

    Джерела

    HTTPS — це HTTP усередині TLS

    • RFC 6797 — HTTP Strict Transport Security (HSTS) — HTTPS як HTTP, загорнутий у криптографічний шар TLS.
    • RFC 8446 — TLS 1.3 — мета TLS, вимога надійного впорядкованого потоку від транспорту, поділ на рукостискання й record-протокол.

    Три гарантії й асиметрія лише на старті

    • RFC 8446 — TLS 1.3 — три гарантії каналу, фази рукостискання, Certificate/CertificateVerify/Finished, шифрування всього після ServerHello, роль асиметрії на старті й пряма секретність.
    • OWASP Cheat Sheet — Transport Layer Security — взаємний TLS як те, що закриває невизначеність з особою клієнта.

    Версії: «заборонено» і «застаріло» — різні слова

    Що посередник бачить, а що ні

    Сертифікат стенда — і чому прогін падає вранці

    HSTS: схема переписується ще до мережі

    • RFC 6797 — HTTP Strict Transport Security (HSTS) — заборона віддавати заголовок по відкритому транспорту, нормативна заміна схеми й порту, час життя політики, обрив зʼєднання при помилці транспорту.
    • MDN — Strict-Transport-Security — браузер ігнорує заголовок з HTTP-відповіді й не пропонує продовжити повз помилку сертифіката.
    • OWASP — HTTP Strict Transport Security Cheat Sheet — незворотність preload.
    • MDN — Redirections in HTTP — постійні редиректи 301/308 і перевірка самого факту перенаправлення з вимкненим автопрямуванням.

    Змішаний контент: «елемент не зʼявився без причини»

    • MDN — Mixed content — поділ на оновлюваний і блокований вміст, правила для <img> і хоста-IP, попередження в консолі, локальні ресурси як захищені походження, межа з навігацією, upgrade-insecure-requests.
    • Playwright — class Page — подія console як спосіб побачити ці попередження з тесту.
    • RFC 6455 — The WebSocket Protocol — схеми ws і wss, друга з яких іде поверх TLS.

    Походження: схема — це безпекова межа

    • MDN — Same-origin policy — походження як трійка «схема / хост / порт» і умова збігу всіх трьох.
    • RFC 6454 — The Web Origin Concept — походження як домен захисту й означення XSS через повноваження походження.
    • WHATWG URL Standard — типові порти «особливих» схем, які в серіалізації опускають.

    CSP: хто вирішує, що сторінці можна виконувати

    XSS — рівно стільки, щоб зрозуміти CSP

    • OWASP — Cross Site Scripting (XSS) — означення, корінь вразливості, доступ внесеного інʼєкцією скрипта до кукі й токенів, розкриття сесійної кукі як найтяжчий наслідок.
    • MDN — Cross-site scripting (XSS) — кодування виводу, санітизація й фреймворки як основний захист.
    • W3C — Content Security Policy Level 3 — CSP як другий рубіж саме проти інʼєкції вмісту.

    Кукі під кутом транспорту

    Перехоплення трафіку в тестах — легальний MITM

    • mitmproxy — How mitmproxy works — межа звичайного проксі на TLS-потоці, підміна ролей обох сторін і потреба у власному сертифікаті.
    • RFC 9110 — HTTP Semantics — перехоплювальний вузол, якого клієнт не обирає, і його нерозрізнюваність з на шляху.
    • Playwright — Network — той самий принцип перехоплення всередині браузера.

    Пояснення

    «Поясни» працює з власним API-ключем Anthropic: запит іде з вашого браузера прямо до Anthropic.

    Свій API-ключ (BYOK)

    Вставте власний ключ Anthropic — пояснення працюватиме на реальній моделі. Ключ зберігається лише у вашому браузері (localStorage), нікуди не надсилається, крім api.anthropic.com, і не логується.