HTTPS, TLS і безпека
Зміст
Кожен клік у браузері — це мережевий запит, який летить через невідому кількість чужих пристроїв: домашній роутер, провайдера, магістральні вузли, балансувальники на боці сервера. Будь-хто на цьому шляху фізично може прочитати або підмінити те, що передається. HTTPS існує саме для того, щоб зробити це нездійсненним на практиці. Для AQA це не абстрактна теорія: сертифікати на staging, mixed content у консолі, HttpOnly-кукі, які не читаються з JavaScript, CSP, що блокує ваш ін’єктований скрипт — усе це щодня впливає на те, працює автотест чи «падає без причини».
HTTP vs HTTPS
HTTP (HyperText Transfer Protocol) передає дані у відкритому вигляді. Запит і відповідь — це звичайний текст, який будь-який посередник читає як листівку. HTTPS — це той самий HTTP, але «загорнутий» у криптографічний шар TLS (Transport Layer Security). Літера S означає Secure: спершу встановлюється захищений канал, і вже всередині нього ходить звичний HTTP-трафік.
| Ознака | HTTP | HTTPS |
|---|---|---|
| Порт за замовчуванням | 80 | 443 |
| Шифрування | немає | є (TLS) |
| Цілісність даних | не гарантується | контролюється |
| Автентифікація сервера | немає | через сертифікат |
| Позначка в браузері | «Not secure» | нейтральний значок |
| URL-схема | http:// | https:// |
Важливий нюанс: TLS шифрує тіло і заголовки HTTP, але не приховує сам факт з’єднання. Посередник усе одно бачить IP-адресу сервера і — через SNI (Server Name Indication) під час рукостискання — доменне ім’я, до якого ви йдете. Він не бачить конкретний шлях (/account/settings), параметри, кукі й тіло. Тобто «що ви відкрили на сайті» приховано, а «на який сайт ви пішли» — здебільшого ні. Розширення ECH (Encrypted Client Hello) шифрує й це доменне ім’я в рукостисканні. Свіжі версії основних браузерів (Chrome, Firefox, Safari) вмикають його за замовчуванням за наявності DNS-over-HTTPS, але працює воно лише там, де ECH підтримує сам сервер чи CDN, — а такого покриття поки що небагато, тож на практиці домен здебільшого все ще видно.
Нюанс. Наскільки ECH реально працює у твоєму середовищі — залежить від браузера, CDN і DNS-резолвера; підтримка поступово розширюється, тож перевіряй на цільовій конфігурації.
Сучасні браузери позначають http:// як небезпечний, а багато сайтів автоматично перенаправляють з HTTP на HTTPS редиректом (301/308) та вмикають заголовок Strict-Transport-Security (HSTS). Він змушує браузер надалі ходити на цей домен тільки через HTTPS, навіть якщо користувач набрав http:// вручну.
# 1) відкритий HTTP-запит → редирект на HTTPS
GET / HTTP/1.1
Host: app.example.com
HTTP/1.1 301 Moved Permanently
Location: https://app.example.com/
# 2) уже по HTTPS сервер віддає HSTS
HTTP/1.1 200 OK
Strict-Transport-Security: max-age=31536000; includeSubDomains
Важлива деталь: заголовок HSTS браузер приймає лише з HTTPS-відповіді — надісланий у відкритій HTTP-відповіді, він ігнорується. Тому HSTS має сенс віддавати вже на захищеному з’єднанні, після редиректу. А коли політику збережено, наступний http→https перехід браузер робить внутрішньо, ще до будь-якого мережевого запиту (у DevTools Chrome це видно як запис «307 Internal Redirect», який насправді не йде на сервер).
Для тестів це означає ось що: якщо ви жорстко зашили http:// у базовий URL, а домен уже під HSTS, браузер сам перепише схему на https:// ще до першого запиту в мережу, і ваші перевірки редиректу можуть поводитися не так, як ви очікуєте. Перевіряйте фактичну схему в отриманій відповіді, а не в тому, що набрали.
Що саме захищає TLS
TLS дає три різні гарантії, і плутати їх не варто — вони закривають різні атаки.
Конфіденційність (confidentiality). Дані шифруються, тож посередник бачить лише беззмістовний набір байтів. Реалізується симетричним шифруванням (наприклад, AES): обидві сторони використовують один спільний ключ сесії.
Цілісність (integrity). Отримувач може виявити будь-яку підміну чи пошкодження даних у дорозі. Якщо зловмисник змінить хоч один байт, перевірка не збіжиться і з’єднання розірветься. Забезпечується автентифікованим шифруванням (AEAD, Authenticated Encryption with Associated Data).
Автентифікація сервера (server authentication). Клієнт переконується, що розмовляє саме з app.example.com, а не з тим, хто прикинувся ним. Це роль сертифіката, і саме тут ховається основна складність.
Ключова ідея рукостискання (handshake): асиметрична криптографія (пара «відкритий/приватний ключ») працює лише на старті, щоб сторони узгодили спільний симетричний ключ сесії, а далі весь трафік шифрується швидким симетричним алгоритмом. У сучасному TLS 1.3 ключ сесії виводиться через ефемерний обмін Діффі — Гелмана (ECDHE), а приватний ключ із сертифіката використовується для підпису рукостискання, тобто для автентифікації, а не для передавання самого ключа. Наслідок — пряма секретність (forward secrecy): навіть якщо приватний ключ сервера пізніше витече, раніше записаний трафік із нього розшифрувати не вдасться. Актуальні версії — TLS 1.2 і TLS 1.3 (RFC 8446). Старіші SSL 2.0/3.0 та TLS 1.0/1.1 вважаються небезпечними й вимкнені.
У тестах. Перелік cipher suites на конкретному сервері — питання його конфігурації: перевіряй
openssl s_clientабо зовнішнім сканером, а не по памʼяті.
Зверніть увагу: за замовчуванням TLS автентифікує лише сервер. Клієнт лишається анонімним, доки не увійде через логін/токен уже всередині захищеного каналу. Взаємну автентифікацію (mutual TLS, mTLS), де клієнт теж пред’являє сертифікат, застосовують у сценаріях «сервіс до сервісу», і в тестах вона зазвичай потребує окремого налаштування клієнтського сертифіката.
Сертифікат і хто його видає
Сертифікат (certificate) — це файл формату X.509, який пов’язує доменне ім’я з відкритим ключем сервера. Спрощено він містить:
- домен(и), для яких дійсний — поле SAN (Subject Alternative Name);
- відкритий ключ сервера;
- хто видав сертифікат (issuer);
- строк дії (від / до);
- цифровий підпис центру сертифікації.
Видає сертифікат центр сертифікації (Certificate Authority, CA) — організація, якій браузери й операційні системи довіряють «за замовчуванням». Приклади: Let’s Encrypt (безкоштовний, автоматичний), DigiCert, Sectigo.
Довіра будується ланцюжком (chain of trust):
Root CA → Intermediate CA → Сертифікат сервера (leaf)
(у сховищі довіри ОС/браузера) (для app.example.com)
Кореневі сертифікати (root) вшиті у сховище довіри (trust store) вашої операційної системи та браузера. Сервер зазвичай пред’являє свій leaf-сертифікат разом із проміжними. Браузер перевіряє підписи вгору по ланцюгу до кореня, якому довіряє, а також:
- чи збігається домен із SAN (сучасні браузери дивляться саме на SAN, а не на застаріле поле Common Name);
- чи не вичерпався строк дії;
- чи не відкликаний сертифікат (через OCSP або CRL).
Перевірка відкликання на практиці ненадійна: OCSP-запит може заблокуватися чи «мовчати», тому браузери додатково возять власні куровані списки відкликаних сертифікатів (наприклад, CRLSets у Chrome, OneCRL у Firefox) і покладаються радше на них. Ба більше, галузь відходить від OCSP: Let’s Encrypt повністю вимкнув свій OCSP-сервіс у серпні 2025 року, лишивши тільки CRL — зокрема через приватність, адже кожен OCSP-запит розкриває оператору CA, які сайти й з якої IP-адреси відвідує користувач.
Якщо будь-яка перевірка не проходить, браузер показує помилку. Найчастіші в роботі AQA:
| Помилка (Chrome) | Причина |
|---|---|
NET::ERR_CERT_DATE_INVALID | сертифікат прострочений або дата на машині невірна |
NET::ERR_CERT_COMMON_NAME_INVALID | домен не збігається з SAN |
NET::ERR_CERT_AUTHORITY_INVALID | підписаний невідомим CA (часто self-signed) |
Окремо про прострочений сертифікат: це реальна причина масового «падіння» тестів вранці, коли сертифікат staging протух уночі. Перш ніж лізти в код, варто відкрити середовище руками й глянути на значок біля адреси.
Self-signed сертифікати на dev/staging
Self-signed сертифікат — це сертифікат, який підписав сам себе, без участі довіреного CA. На проді так не роблять, а от на dev/staging це нормально: випускати повноцінний публічний сертифікат для внутрішнього стенду часто недоцільно. Браузер такому сертифікату не довіряє й показує попередження (NET::ERR_CERT_AUTHORITY_INVALID).
Ручний обхід — натиснути «Advanced → Proceed». Але в автотестах так не вийде, тому інструменти дають прапорці, які кажуть «не перевіряй сертифікат».
Playwright — опція контексту ignoreHTTPSErrors:
const context = await browser.newContext({
ignoreHTTPSErrors: true,
});
Selenium — можливість acceptInsecureCerts:
const options = new chrome.Options();
options.setAcceptInsecureCerts(true);
curl у скриптах і CI — прапорець -k / --insecure:
curl -k https://staging.internal/api/health
Компроміс тут важливий, і його треба усвідомлювати. Вимикаючи перевірку, ви робите тест сліпим до реального MITM і до помилок конфігурації сертифіката. Тому:
- вмикайте
ignoreHTTPSErrorsтільки для середовищ, де це справді очікувано (dev/staging), а не глобально «щоб не заважало»; - на проді, навпаки, корисно мати окремий тест, який перевіряє, що сертифікат валідний і не близький до прострочення;
- кращий за прапорець варіант там, де це можливо, — додати кореневий сертифікат вашого внутрішнього CA у сховище довіри агента CI. Тоді ланцюг стане валідним «по-справжньому», і перевірку вимикати не доведеться.
Mixed content
Mixed content — це коли сторінка завантажена по HTTPS, але тягне підресурси по HTTP. Проблема очевидна: значок обіцяє захист, а частина трафіку йде відкрито, тож зловмисник може підмінити той HTTP-скрипт і зламати всю сторінку.
Браузери розрізняють два типи:
- пасивний (passive) mixed content — картинки, відео, аудіо. Такі ресурси не керують сторінкою, тож ризик нижчий. Сучасні браузери автоматично підвищують ці запити з
httpдоhttps, а якщо по HTTPS ресурс недоступний — блокують його, без відкату на HTTP (історично їх могли просто завантажити з попередженням у консолі); - активний (active) mixed content — скрипти, стилі, iframe, XHR/fetch. Ризик високий, бо ці ресурси керують сторінкою. Такий контент браузери за замовчуванням блокують.
Реальне попередження в консолі Chrome виглядає приблизно так:
Mixed Content: The page at 'https://app.example.com/' was loaded over HTTPS,
but requested an insecure script 'http://cdn.example.com/app.js'.
This request has been blocked; the content must be served over HTTPS.
Для AQA це часте джерело плаваючих (flaky) падінь: тест «раптом» не бачить елемент, бо скрипт, який його малює, заблоковано як active mixed content. У консолі при цьому буде явне попередження. Тому при діагностиці «елемент не з’явився» варто відразу заглядати в консоль браузера — Playwright дає це через подію console:
page.on('console', (msg) => {
if (msg.type() === 'error') console.log('BROWSER:', msg.text());
});
Часто корінь проблеми — жорстко зашитий http:// у конфізі середовища або протокол-залежне посилання, яке зламалося після переїзду стенду на HTTPS.
XSS — Cross-Site Scripting
XSS — це впровадження чужого JavaScript у сторінку, яку бачать інші користувачі. Корінь проблеми один: дані, введені користувачем, потрапляють у HTML без належного екранування, і браузер виконує їх як код. Наслідки — крадіжка сесії, дії від імені жертви, підміна вмісту.
Оглядово три типи:
| Тип | Де живе payload | Приклад сценарію |
|---|---|---|
| Stored (persistent) | зберігається на сервері (БД) | коментар зі <script> показується всім читачам |
| Reflected | у параметрі запиту, вертається у відповіді | шкідливе посилання з ?q=<script> |
| DOM-based | у клієнтському JS, сервер не задіяний | innerHTML = location.hash |
Захист — екранування виводу (output encoding), санітизація вводу, сучасні фреймворки (React, Angular), які за замовчуванням екранують вставки, і CSP як другий рубіж. Небезпечні місця — прямі присвоєння innerHTML, document.write, dangerouslySetInnerHTML.
Для тестувальника XSS — це не лише про безпеку, а й про негативні перевірки: варто мати кейси, де у поле вводять <img src=x onerror=alert(1)> чи "><script>… і перевіряють, що на сторінці цей рядок показується як текст, а не виконується. Простий детектор у тесті: після сабміту поля переконатися, що в DOM не з’явився новий виконаний <script>, а рядок відрендерився екранованим (<script>).
CSRF — Cross-Site Request Forgery
CSRF експлуатує те, що браузер автоматично додає кукі до кожного запиту на домен. Ідея атаки: користувач залогінений у bank.example, заходить на сайт зловмисника, а той тихо шле форму чи запит на bank.example/transfer. Браузер прикріплює кукі сесії, і сервер думає, що це легітимна дія користувача.
Ключова відмінність від XSS: при CSRF зловмисник не читає відповідь і не краде дані напряму — він змушує браузер жертви виконати дію її ж правами.
Захист:
- CSRF-токен — унікальне непередбачуване значення, яке сервер кладе у форму й перевіряє при сабміті; чужий сайт його не знає;
- атрибут кукі
SameSite(Lax/Strict) — браузер не надсилає кукі при міжсайтових запитах; - перевірка заголовків
Origin/Refererна боці сервера.
У тестах CSRF-токени — часте джерело болю в API-перевірках в обхід UI: сервер відповість 403, бо ви не передали свіжий токен. Правильний шлях — спершу отримати токен зі сторінки чи спеціального ендпоінта, а потім підставити його в запит, а не хардкодити.
SQL-injection
SQL-injection — це впровадження SQL-коду через дані, які потрапляють у запит до бази без параметризації. Класичний приклад — конкатенація рядка:
// небезпечно
db.query("SELECT * FROM users WHERE name = '" + input + "'");
Якщо у input передати ' OR '1'='1, умова стане завжди істинною і запит поверне всіх користувачів. Ще гірше — '; DROP TABLE users; --.
Захист один і надійний — параметризовані запити (prepared statements), де дані передаються окремо від тексту запиту й ніколи не інтерпретуються як код:
// безпечно
db.query("SELECT * FROM users WHERE name = ?", [input]);
AQA сюди дотичний через негативні кейси: у поля вводу підставляють характерні payload’и (' OR 1=1 --, лапки, крапка з комою) і перевіряють, що застосунок повертає коректну помилку валідації або порожній результат, а не «раптом» видає чужі дані чи 500. Робити це варто виключно на своїх тестових середовищах і ніколи — на чужих чи продакшн-системах без дозволу.
CSP — Content-Security-Policy
CSP — це заголовок відповіді, який каже браузеру, з яких джерел дозволено завантажувати ресурси й виконувати код. Це головний рубіж проти XSS: навіть якщо зловмисник впровадив скрипт, CSP не дасть браузеру його виконати, бо джерело не в білому списку.
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:
Основні директиви:
| Директива | Що обмежує |
|---|---|
default-src | значення за замовчуванням для решти |
script-src | звідки можна вантажити/виконувати JS |
style-src | джерела стилів |
img-src | джерела зображень |
connect-src | куди дозволено fetch/XHR/WebSocket |
frame-src | що можна вбудовувати в iframe |
Є також режим тільки-звіт — заголовок Content-Security-Policy-Report-Only, який нічого не блокує, а лише надсилає звіти про порушення. Його зручно вмикати перед бойовим запуском політики, щоб побачити, що вона зламала б, не ламаючи насправді.
Для автотестів CSP має цілком практичний бік. Якщо ви звикли інжектити допоміжний скрипт або додавати inline-обробники в DOM самої сторінки — сувора CSP (без 'unsafe-inline', без 'unsafe-eval') це заблокує, і в консолі з’явиться повідомлення про порушення. При цьому сам page.evaluate у Playwright здебільшого проходить: код їде через протокол налагодження (Chrome DevTools Protocol) і виконується в контексті сторінки, а не вставляється як <script>, тож директива script-src його зазвичай не зачіпає. Виняток — дуже сувора CSP без 'unsafe-eval': окремі механізми на кшталт page.waitForFunction спираються на eval-подібні конструкції й під такою політикою можуть падати. А от addScriptTag чи спроба вставити inline-обробник у розмітку сторінки під суворою CSP не спрацюють — саме для таких випадків Playwright має окрему опцію bypassCSP. І навпаки: корисно мати перевірку, що CSP взагалі присутній і що список джерел не «поплив» після релізу (наприклад, ніхто не дописав 'unsafe-inline' заради швидкого фіксу).
Кукі: HttpOnly і Secure
Кукі керують сесіями, тож їхні атрибути безпеки — критичні. Три ключові:
| Атрибут | Що робить | Проти чого захищає |
|---|---|---|
HttpOnly | кукі недоступне з JavaScript (document.cookie його не бачить) | крадіжка сесії через XSS |
Secure | кукі надсилається тільки по HTTPS | перехоплення по відкритому HTTP |
SameSite | обмежує надсилання при міжсайтових запитах | CSRF |
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Lax; Path=/
Логіка проста: HttpOnly розриває головний вектор XSS — навіть якщо чужий скрипт виконався, він не прочитає токен сесії. Secure гарантує, що кукі не «витече» випадковим HTTP-запитом. SameSite=Lax обмежує надсилання кукі при більшості міжсайтових переходів; у Chromium-браузерах (Chrome, Edge) це ще й типова поведінка для кукі без явно заданого атрибута, тоді як Firefox і Safari до такого дефолту так і не перейшли — тож покладатися на «дефолт Lax» не варто, атрибут краще задавати явно. SameSite=None дозволяє крос-сайт, але тоді браузер вимагає ще й Secure.
Прямий наслідок для AQA: токен сесії з HttpOnly ви не прочитаєте через document.cookie в тесті. Це не баг тесту — це так задумано. Читати й підставляти такі кукі треба через API контексту браузера, а не через JS сторінки:
const cookies = await context.cookies();
const session = cookies.find((c) => c.name === 'session');
Це ж стосується ізоляції стану між тестами: очищення document.cookie не прибере HttpOnly-кукі. Надійніше стартувати з чистого контексту або керувати станом через storageState / context.clearCookies().
Чому секретам не місце у фронтенді й у query
Фронтенд-код повністю видимий користувачеві. Будь-хто відкриває DevTools, вкладку Sources, і читає весь ваш JavaScript, включно з тим, що «заховане» у змінну чи згорнуте мініфікатором. Тому будь-який ключ, зашитий у клієнтський код — API-секрет, приватний токен, пароль сервісу — це вже не секрет. Фронтенду можна давати лише публічні ідентифікатори (наприклад, публічний ключ, який і призначений бути відкритим).
Окремо про query-рядок. Здавалося б, по HTTPS URL зашифрований — то чому не покласти токен у ?token=…? Тому що повний URL із параметрами зазвичай:
- пишеться в логи сервера, проксі та балансувальників;
- зберігається в історії браузера;
- може «протекти» у заголовку
Refererпри переході на інший сайт; - потрапляє в аналітику й системи моніторингу.
# погано: токен видно всюди, де логуються URL
GET /api/data?token=SECRET123 HTTP/1.1
# краще: секрет у заголовку, який не логується за замовчуванням
GET /api/data HTTP/1.1
Authorization: Bearer SECRET123
Тому секрети передають у заголовках (найчастіше Authorization) або в тілі POST, а не в query. Для тестів це теж робоче правило: у CI токени тримають у секретах середовища й підставляють через змінні оточення, а не хардкодять у репозиторій. Логи автотестів корисно фільтрувати, щоб токен не осів у публічному звіті білда.
MITM і як HTTPS йому протидіє
MITM (Man-in-the-Middle) — атака, коли зловмисник вклинюється між клієнтом і сервером: перехоплює, читає, а то й підміняє трафік, лишаючись непоміченим для обох сторін. Класичний сценарій — підставна Wi-Fi-точка в кафе, яка вдає легітимну мережу.
HTTPS протидіє двома механізмами одночасно, і важливо, що потрібні обидва:
- шифрування — перехоплений трафік нечитабельний, тож пасивне підслуховування марне;
- автентифікація сервера — щоб не просто підслухати, а видати себе за сервер, зловмиснику потрібен валідний сертифікат на потрібний домен, підписаний довіреним CA. Отримати такий без контролю над доменом він не може, а self-signed чи чужий сертифікат браузер відхилить із попередженням.
Саме тому попередження про сертифікат, яке хочеться «просто проклацати», — це і є момент, коли HTTPS ловить потенційний MITM. Ігнорувати його наосліп означає власноруч відкрити канал атаки.
Практичний парадокс для AQA: інструменти, якими ми перехоплюємо й аналізуємо трафік — mitmproxy, Charles, Burp Suite — це, по суті, легальний MITM. Вони працюють так: встановлюють свій кореневий сертифікат у сховище довіри вашої машини, і тоді браузер довіряє сертифікатам, які проксі генерує на льоту для кожного домену. Без цього кореневого сертифіката браузер підняв би тривогу — що й доводить, що захист працює.
Логічне продовження — перехоплення мережі в самих автотестах. Playwright дає page.route, яким ви підмінюєте відповіді, мокаєте API, імітуєте помилки й таймаути:
await page.route('**/api/user', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ id: 1, name: 'Test User' }),
});
});
По суті це контрольований MITM усередині вашого тесту: ви навмисне стаєте посередником, щоб ізолювати фронтенд від бекенду, прибрати мережеву нестабільність (одну з головних причин flakiness) і детермінувати дані. Різниця з атакою лише в намірі й у тому, що ви робите це у власному, ізольованому оточенні.
Розуміння цієї механіки замикає всю тему: сертифікати, ланцюг довіри, Secure-кукі, CSP і заборона на секрети у фронтенді — це різні шари однієї відповіді на просте питання «як довіряти каналу, який фізично проходить через чужі руки».
Чим HTTPS відрізняється від HTTP?
HTTPS — це той самий HTTP, але «загорнутий» у криптографічний шар TLS: спершу встановлюється захищений канал, і вже всередині нього ходить звичний HTTP-трафік. HTTP передає запити й відповіді відкритим текстом, і будь-який посередник на шляху — роутер, провайдер, балансувальник — читає їх як листівку. HTTPS додає три речі: шифрування, контроль цілісності й автентифікацію сервера через сертифікат. Порти за замовчуванням різні: 80 для HTTP і 443 для HTTPS. Браузер позначає http:// як «Not secure», а багато сайтів автоматично редиректять на HTTPS. Для автотестів це означає, що жорстко зашитий http:// у базовому URL — потенційне джерело сюрпризів: редиректи, mixed content, HSTS.
Які гарантії дає TLS?
Три різні гарантії, які закривають різні атаки: конфіденційність, цілісність і автентифікацію сервера. Конфіденційність — дані шифруються симетричним алгоритмом (наприклад, AES), і посередник бачить лише беззмістовний набір байтів. Цілісність — підміну навіть одного байта в дорозі буде виявлено, перевірка не збіжиться і з'єднання розірветься; це забезпечує автентифіковане шифрування (AEAD). Автентифікація сервера — сертифікат доводить, що клієнт говорить саме з app.example.com, а не з тим, хто ним прикинувся. Важливий нюанс: за замовчуванням TLS автентифікує лише сервер, клієнт лишається анонімним, доки не увійде через логін чи токен уже всередині захищеного каналу. Плутати ці гарантії — типова помилка на співбесіді: саме по собі шифрування без автентифікації від MITM не рятує.
Що таке сертифікат і хто його видає?
Сертифікат — це файл формату X.509, який пов'язує доменне ім'я з відкритим ключем сервера. Він містить домени, для яких дійсний (поле SAN), відкритий ключ, видавця (issuer), строк дії та цифровий підпис центру сертифікації. Видає його центр сертифікації (Certificate Authority, CA) — організація, якій браузери й ОС довіряють за замовчуванням: Let's Encrypt, DigiCert, Sectigo. Довіра будується ланцюжком: Root CA підписує Intermediate CA, той — leaf-сертифікат сервера, а кореневі сертифікати вшиті у сховище довіри операційної системи та браузера. Браузер перевіряє підписи вгору по ланцюгу, а також збіг домену з SAN, строк дії та чи не відкликано сертифікат. Якщо будь-яка перевірка провалюється — користувач бачить повноекранну помилку замість сайту.
Уранці всі UI-тести на staging масово впали. З чого почнеш діагностику?
Перш ніж лізти в код — відкрити середовище руками й глянути на значок біля адресного рядка: масове падіння вранці — класичний симптом сертифіката, який протух уночі. Chrome у такому разі покаже NET::ERR_CERT_DATE_INVALID. Інші часті помилки сертифіката: NET::ERR_CERT_COMMON_NAME_INVALID — домен не збігається з SAN, NET::ERR_CERT_AUTHORITY_INVALID — підпис невідомого CA, найчастіше self-signed. Логіка проста: якщо впало все й одночасно, причина радше в середовищі, ніж у тестах, і хвилина ручної перевірки економить години розбору логів. На проді з цього ж робиться висновок: корисно мати окремий тест, який перевіряє, що сертифікат валідний і не близький до прострочення.
Що таке self-signed сертифікат і як з ним працювати в автотестах?
Self-signed — це сертифікат, який підписав сам себе, без довіреного CA; браузер йому не довіряє й показує NET::ERR_CERT_AUTHORITY_INVALID. На проді так не роблять, а на dev/staging це нормально — публічний сертифікат для внутрішнього стенду часто недоцільний. Вручну попередження обходять через «Advanced → Proceed», але автотесту потрібні прапорці: у Playwright — опція контексту ignoreHTTPSErrors: true, у Selenium — acceptInsecureCerts, у curl — -k/--insecure. Компроміс треба усвідомлювати: вимкнена перевірка робить тест сліпим до реального MITM і помилок конфігурації сертифіката. Тому прапорець вмикають лише для середовищ, де це очікувано, а не глобально «щоб не заважало». Кращий варіант, де можливо, — додати кореневий сертифікат внутрішнього CA у сховище довіри агента CI: ланцюг стане валідним по-справжньому, і перевірку вимикати не доведеться.
Що таке HSTS і як він може вплинути на автотести?
HSTS (Strict-Transport-Security) — заголовок, який змушує браузер надалі ходити на домен тільки через HTTPS, навіть якщо користувач набрав http:// вручну. Браузер приймає цей заголовок лише з HTTPS-відповіді — надісланий по відкритому HTTP, він ігнорується, тому його віддають уже після редиректу на захищене з'єднання. Коли політику збережено, наступний перехід http→https браузер робить внутрішньо, ще до будь-якого мережевого запиту — у DevTools Chrome це видно як «307 Internal Redirect», який насправді не йде на сервер. Для тестів наслідок конкретний: якщо в базовому URL зашито http://, а домен уже під HSTS, браузер сам перепише схему ще до першого запиту, і перевірки редиректу поводитимуться не так, як очікувалося. Тому перевіряти треба фактичну схему в отриманій відповіді, а не те, що набрали.
Що таке mixed content і чому через нього тест «не бачить елемент»?
Mixed content — це коли сторінка завантажена по HTTPS, але тягне підресурси по HTTP: значок обіцяє захист, а частина трафіку йде відкрито й може бути підмінена. Браузери розрізняють пасивний (картинки, відео, аудіо — їх автоматично підвищують до https, а без HTTPS-версії блокують) і активний (скрипти, стилі, iframe, fetch/XHR — блокується за замовчуванням, бо ці ресурси керують сторінкою). Для AQA це часте джерело flaky-падінь: тест не бачить елемент, бо скрипт, який його малює, заблоковано як active mixed content, — а в консолі при цьому лежить явне попередження «Mixed Content: … This request has been blocked». Тому при діагностиці «елемент не з'явився» варто одразу читати консоль браузера — у Playwright її слухають через подію console. Корінь проблеми зазвичай — жорстко зашитий http:// у конфізі середовища або протокол-залежне посилання, яке зламалося після переїзду стенда на HTTPS.
Які атрибути безпеки кукі ти знаєш і що кожен дає?
Три ключові: HttpOnly, Secure і SameSite. HttpOnly робить кукі недоступним із JavaScript — document.cookie його не бачить, що розриває головний вектор XSS: навіть якщо чужий скрипт виконався, токен сесії він не прочитає. Secure дозволяє надсилати кукі тільки по HTTPS, тож воно не витече випадковим відкритим запитом. SameSite (Lax/Strict) обмежує надсилання кукі при міжсайтових запитах і захищає від CSRF; SameSite=None дозволяє крос-сайт, але браузер тоді вимагає ще й Secure. Нюанс із дефолтом: Chromium-браузери трактують кукі без атрибута як Lax, а Firefox і Safari до такого дефолту не перейшли, тому атрибут краще задавати явно. Для автотестів прямий наслідок: HttpOnly-кукі не читається через document.cookie — це не баг, читати й підставляти його треба через API контексту браузера (context.cookies()), а чистити стан — через context.clearCookies() чи storageState, бо очищення document.cookie таке кукі не прибере.
Що таке XSS і як його тестувати?
XSS (cross-site scripting) — впровадження чужого JavaScript у сторінку, яку бачать інші користувачі; корінь один — дані користувача потрапляють у HTML без екранування, і браузер виконує їх як код. Три типи: stored — payload зберігається на сервері й показується всім (коментар зі <script>), reflected — payload у параметрі запиту вертається у відповіді (шкідливе посилання), DOM-based — уразливість у клієнтському JS без участі сервера (типу innerHTML = location.hash). Наслідки — крадіжка сесії, дії від імені жертви, підміна вмісту. Захист: екранування виводу, санітизація вводу, фреймворки на кшталт React/Angular, які екранують за замовчуванням, і CSP як другий рубіж; небезпечні місця — innerHTML, document.write, dangerouslySetInnerHTML. Для тестувальника це негативні кейси: ввести у поле <img src=x onerror=alert(1)> чи "><script>… і перевірити, що рядок відрендерився як текст (<script> у розмітці), а не виконався — тобто в DOM не з'явився новий виконаний скрипт.
Чим CSRF відрізняється від XSS і як від нього захищаються?
CSRF (cross-site request forgery) експлуатує те, що браузер автоматично додає кукі до кожного запиту на домен: жертва залогінена в bank.example, відкриває сайт зловмисника, а той тихо шле форму на bank.example/transfer — браузер прикріплює кукі сесії, і сервер вважає дію легітимною. Ключова відмінність від XSS: зловмисник не читає відповідь і не краде дані напряму — він змушує браузер жертви виконати дію її ж правами. Захисти: CSRF-токен (непередбачуване значення, яке сервер кладе у форму й перевіряє при сабміті — чужий сайт його не знає), атрибут кукі SameSite і перевірка заголовків Origin/Referer на сервері. Для AQA це часте джерело болю в API-тестах в обхід UI: сервер відповідає 403, бо не передано свіжий токен. Правильний шлях — спершу отримати токен зі сторінки чи спеціального ендпоінта й підставити в запит, а не хардкодити.
Що таке SQL-ін'єкція і який захист вважається надійним?
SQL-injection — впровадження SQL-коду через дані, які потрапляють у запит до бази без параметризації, класично — через конкатенацію рядка. Якщо в input передати ' OR '1'='1, умова стане завжди істинною і запит поверне всіх користувачів; ще гірше — '; DROP TABLE users; --. Надійний захист один — параметризовані запити (prepared statements), де дані передаються окремо від тексту запиту й ніколи не інтерпретуються як код. AQA дотичний через негативні кейси: підставити у поля характерні payload'и (' OR 1=1 --, лапки, крапку з комою) і перевірити, що застосунок повертає коректну помилку валідації або порожній результат, а не чужі дані чи 500. Важливе застереження: робити це можна виключно на своїх тестових середовищах і ніколи — на чужих чи продакшн-системах без дозволу.
Чому секрети не можна класти у фронтенд-код і в query-рядок, навіть по HTTPS?
Фронтенд-код повністю видимий користувачеві: будь-хто відкриває DevTools → Sources і читає весь JavaScript, включно з «захованим» у змінну чи мініфікованим, — тому будь-який ключ у клієнтському коді вже не секрет. Фронтенду можна давати лише публічні ідентифікатори, які й призначені бути відкритими. Query-рядок по HTTPS справді зашифрований у дорозі, але повний URL із параметрами пишеться в логи сервера, проксі та балансувальників, зберігається в історії браузера, може протекти в заголовку Referer при переході на інший сайт і потрапляє в аналітику. Тому секрети передають у заголовках (найчастіше Authorization: Bearer …) або в тілі POST. Для автотестів правило те саме: токени в CI живуть у секретах середовища й підставляються через змінні оточення, а не хардкодяться в репозиторій, а логи тестів варто фільтрувати, щоб токен не осів у публічному звіті білда.
Що таке CSP і як суворий CSP впливає на автотести?
CSP (Content-Security-Policy) — заголовок відповіді, який каже браузеру, з яких джерел дозволено вантажити ресурси й виконувати код; це головний рубіж проти XSS — навіть впроваджений скрипт не виконається, якщо його джерело не в білому списку. Основні директиви: default-src (дефолт для решти), script-src, style-src, img-src, connect-src (fetch/XHR/WebSocket), frame-src. Є режим тільки-звіт — Content-Security-Policy-Report-Only, який нічого не блокує, а лише шле звіти про порушення: зручно ввімкнути перед бойовим запуском політики. Для автотестів практика така: сувора CSP без 'unsafe-inline' заблокує інжект допоміжного <script> чи inline-обробника, і в консолі з'явиться порушення. При цьому page.evaluate у Playwright здебільшого проходить — код їде через Chrome DevTools Protocol, а не вставляється тегом, тож script-src його зазвичай не зачіпає; виняток — дуже сувора політика без 'unsafe-eval', під якою можуть падати eval-залежні механізми на кшталт page.waitForFunction. Для addScriptTag та inline-обробників Playwright має окрему опцію bypassCSP, а на проді корисно мати зворотну перевірку — що CSP присутній і список джерел не «поплив» після релізу.
Як працює TLS-рукостискання і що таке forward secrecy?
Ключова ідея: асиметрична криптографія працює лише на старті, щоб сторони узгодили спільний симетричний ключ сесії, а далі весь трафік шифрується швидким симетричним алгоритмом. У TLS 1.3 ключ сесії виводиться через ефемерний обмін Діффі — Гелмана (ECDHE), а приватний ключ із сертифіката використовується для підпису рукостискання — тобто для автентифікації сервера, а не для передавання самого ключа. Клієнт шле ClientHello з підтримуваними шифрами і своєю частиною ECDHE, сервер відповідає ServerHello, сертифікатом і підписом; клієнт перевіряє ланцюг довіри й підпис — і обидві сторони виводять спільний ключ. Наслідок такої схеми — пряма секретність (forward secrecy): навіть якщо приватний ключ сервера пізніше витече, раніше записаний трафік ним розшифрувати не вдасться. Актуальні версії — TLS 1.2 і TLS 1.3 (RFC 8446); SSL 2.0/3.0 і TLS 1.0/1.1 вважаються небезпечними й вимкнені. Окремий випадок — mutual TLS (mTLS), де сертифікат пред'являє і клієнт: застосовується у зв'язках «сервіс до сервісу» і в тестах зазвичай потребує окремого налаштування клієнтського сертифіката.
Що з HTTPS-трафіку все одно бачить посередник?
TLS шифрує тіло й заголовки HTTP, але не приховує сам факт з'єднання: посередник бачить IP-адресу сервера і — через SNI (Server Name Indication) у рукостисканні — доменне ім'я, до якого ви йдете. Він не бачить конкретний шлях, параметри запиту, кукі й тіло — тобто «що ви відкрили на сайті» приховано, а «на який сайт пішли» здебільшого ні. Це і є коректна відповідь сеньйорського рівня: HTTPS захищає вміст, а не метадані з'єднання. Розширення ECH (Encrypted Client Hello) шифрує й доменне ім'я в рукостисканні; свіжі версії Chrome, Firefox і Safari вмикають його за замовчуванням за наявності DNS-over-HTTPS, але працює воно лише там, де ECH підтримує сам сервер чи CDN, — покриття поки невелике, тож на практиці домен здебільшого все ще видно. Практичний висновок: чи працює ECH у твоєму середовищі, залежить від браузера, CDN і DNS-резолвера — перевіряти треба на цільовій конфігурації, а не покладатися на «має бути».
Як працюють Charles, mitmproxy чи Burp, якщо HTTPS захищає від MITM?
Це, по суті, легальний MITM — і те, що для нього потрібен окремий крок, якраз доводить, що захист працює. HTTPS протидіє MITM двома механізмами одночасно: шифрування робить пасивне підслуховування марним, а автентифікація сервера вимагає від зловмисника валідного сертифіката на потрібний домен від довіреного CA — отримати такий без контролю над доменом він не може, а self-signed браузер відхилить із попередженням. Проксі-інструменти обходять це чесно: встановлюють свій кореневий сертифікат у сховище довіри вашої машини, після чого браузер довіряє сертифікатам, які проксі генерує на льоту для кожного домену. Без цього кроку браузер підняв би тривогу — саме тому попередження про сертифікат, яке хочеться «просто проклацати», і є моментом, коли HTTPS ловить потенційну атаку. В автотестах та сама механіка звужується до page.route у Playwright: ви навмисне стаєте посередником у власному ізольованому оточенні, щоб мокати API, імітувати помилки й таймаути та прибрати мережеву нестабільність — одну з головних причин flakiness. Різниця з атакою лише в намірі й у тому, що це ваше контрольоване середовище.
Три кейси, де TLS і безпека вирішують, зелений тест чи «падає без причини»: обхід self-signed сертифіката на staging плюс охоронний тест валідності серта на проді, діагностика flaky-падіння через заблокований active mixed content / CSP, і читання HttpOnly-кукі сесії з обходом CSRF-токена в API-тесті. Скрізь — що дивитися і чому.
Кейс 1. Self-signed на staging: обійти, але не осліпнути на проді
На внутрішньому стенді сертифікат підписаний сам собою, тож браузер кидає NET::ERR_CERT_AUTHORITY_INVALID, і тест не доходить навіть до логіну. Руками рятує «Advanced → Proceed», але в автотесті так не клікнеш — вмикаєш прапорець «не перевіряй серт»:
// playwright.config.ts — тільки для dev/staging-проєкту
export default defineConfig({
projects: [
{
name: 'staging',
use: { baseURL: 'https://staging.internal', ignoreHTTPSErrors: true },
},
],
});
Компроміс: із ignoreHTTPSErrors: true тест став сліпим до реального MITM і до кривої конфігурації серта. Тому на проді роблять дзеркальний, охоронний тест — що сертифікат валідний і не близький до прострочення. Найпростіше — openssl у CI:
# лишається < 15 днів до прострочення → нехай CI впаде
echo | openssl s_client -connect app.example.com:443 -servername app.example.com 2>/dev/null \
| openssl x509 -noout -checkend 1296000
Що дивитися і чому:
ignoreHTTPSErrors— точково, не глобально. Вмикай лише в проєкті/середовищі, де self-signed справді очікуваний. Глобальне «щоб не заважало» ховає справжні помилки серта на проді.-servernameвs_clientобовʼязковий. Без нього не передається SNI, і сервер за одним IP може віддати не той серт (дефолтний), — перевіриш чужий ланцюг і зробиш хибний висновок.-checkend <секунди>— це і є охоронна асерція. Команда виходить із ненульовим кодом, якщо серт спливе раніше, ніж за вказаний інтервал. Прострочений уночі серт staging — реальна причина масового «падіння» тестів вранці; такий тест ловить це до релізу, а не після.- Ще краще за прапорець — довіра по-справжньому. Якщо додати кореневий серт внутрішнього CA у trust store агента CI, ланцюг стає валідним, і
ignoreHTTPSErrorsне потрібен узагалі.
Кейс 2. «Елемент не зʼявився» — а це заблокований скрипт (mixed content / CSP)
Тест raptom не бачить кнопку, локатор таймаутить, скрін порожній. Перша інтуїція — «повільна сторінка, додам wait». Насправді скрипт, який малює той елемент, браузер заблокував: або як active mixed content (http://-скрипт на HTTPS-сторінці), або як порушення CSP. І там, і там доказ лежить у консолі, а не в DOM. Тому в діагностиці спершу слухай console, а не нарощуй таймаути:
import { test, expect } from '@playwright/test';
test('форма рендериться, а не глушиться політикою', async ({ page }) => {
const problems: string[] = [];
page.on('console', (msg) => {
const t = msg.text();
if (/Mixed Content|Content Security Policy|Refused to/i.test(t)) problems.push(t);
});
await page.goto('https://app.example.com/checkout');
// спершу ПРИЧИНА (заблоковані ресурси), і лише потім наслідок (кнопка) —
// інакше toBeVisible впаде першим голим таймаутом і причину ти не побачиш
expect(problems, problems.join('\n')).toHaveLength(0);
await expect(page.getByRole('button', { name: 'Pay' })).toBeVisible();
});
Типові повідомлення, які ловить фільтр:
Mixed Content: The page at 'https://app.example.com/' was loaded over HTTPS,
but requested an insecure script 'http://cdn.example.com/app.js'.
This request has been blocked; the content must be served over HTTPS.
Refused to execute inline script because it violates the following
Content Security Policy directive: "script-src 'self'".
Що дивитися і чому:
- Active vs passive — різна поведінка. Скрипти, стилі, iframe,
fetch/XHR браузер блокує за замовчуванням (керують сторінкою). Картинки/відео/аудіо він тихо підвищує зhttpдоhttps; тому «зникла кнопка» — це майже завжди active-контент, а не картинка. - Корінь — зашитий
http://. Часто винен протокол-залежний URL у конфізі середовища, який зламався після переїзду стенду на HTTPS. Лікуєш конфіг, а не тест. page.evaluateпід CSP здебільшого живе,addScriptTag— ні. Кодevaluateїде через CDP і виконується в контексті сторінки, тожscript-srcйого зазвичай не чіпає. А спроба вставити<script>чи inline-обробник під суворою CSP не спрацює — для таких випадків у Playwright єbypassCSP: true. Виняток — дуже сувора CSP без'unsafe-eval':page.waitForFunctionспирається на eval-подібні конструкції й може падати.- Корисний і зворотний тест. Асерти, що CSP взагалі присутній і список джерел не «поплив» після релізу — щоб ніхто не дописав
'unsafe-inline'заради швидкого фіксу.
Кейс 3. HttpOnly-кукі й CSRF-токен: чому document.cookie тут безсилий
Треба перевірити стан сесії або піти в API в обхід UI. Спокуса — прочитати токен через document.cookie. Але серверна кукі з HttpOnly для JS сторінки невидима — і це не баг тесту, так задумано (саме HttpOnly розриває головний вектор XSS). Читати таку кукі треба через API контексту браузера:
import { test, expect } from '@playwright/test';
test('кукі сесії HttpOnly і Secure, читається лише через контекст', async ({ page, context }) => {
await page.goto('https://app.example.com/login');
// ... виконуємо логін ...
// document.cookie НЕ побачить HttpOnly-кукі — поверне порожньо/без session
const viaJs = await page.evaluate(() => document.cookie);
expect(viaJs).not.toContain('session=');
// правильний шлях — API контексту
const session = (await context.cookies()).find((c) => c.name === 'session');
expect(session?.httpOnly).toBe(true);
expect(session?.secure).toBe(true);
});
Далі — типовий біль API-перевірки в обхід UI: сервер відповість 403, бо чужий сайт (і ваш «голий» запит) не знає свіжого CSRF-токена. Хардкодити токен марно — його треба спершу дістати зі сторінки/ендпоінта, а вже потім підставити:
// 1) забираємо свіжий CSRF-токен зі сторінки (мета-тег або прихований інпут)
const csrf = await page.getAttribute('meta[name="csrf-token"]', 'content');
// 2) підставляємо у стан-змінний запит; кукі сесії поїдуть автоматично з контексту
const res = await page.request.post('https://app.example.com/api/transfer', {
headers: { 'X-CSRF-Token': csrf ?? '', 'Content-Type': 'application/json' },
data: { to: 'acc-2', amount: 10 },
});
expect(res.status()).toBe(200);
Що дивитися і чому:
HttpOnlyкукі → тількиcontext.cookies(). Черезdocument.cookieтокен сесії не прочитається ніколи; асертhttpOnly === true/secure === trueзаразом фіксує, що атрибути безпеки на місці.- Ізоляція стану теж впирається в це. Очищення
document.cookieне прибереHttpOnly-кукі. Між тестами стартуй із чистого контексту або керуй станом черезstorageState/context.clearCookies(). 403на API — часто відсутній/протухлий CSRF-токен, а не зламані права. Токен беруть свіжим перед кожним стан-змінним запитом; підстав старий — і сервер його відхилить.SameSiteне задавай «за дефолтом». Chromium вважає кукі без атрибута заLax, а Firefox і Safari — ні. Покладатися на дефолт крихко; у перевірках очікуй саме явно заданий атрибут.
HTTPS, TLS і що воно гарантує
- Можу пояснити різницю HTTP vs HTTPS: HTTPS — той самий HTTP, «загорнутий» у TLS; порт
443замість80, шифрування, цілісність, автентифікація сервера через сертифікат. - Розумію, що TLS шифрує заголовки й тіло, але не приховує факт зʼєднання: посередник бачить IP і — через SNI — домен, але не шлях, параметри, кукі й тіло (домен ховає лише ECH, покриття якого поки мале).
- Можу розвести три гарантії TLS: конфіденційність (симетричне шифрування, AES), цілісність (AEAD виявляє підміну байта), автентифікація сервера (сертифікат) — і що за замовчуванням клієнт лишається анонімним, доки не залогіниться.
- Розумію суть рукостискання: асиметрія працює лише на старті для узгодження симетричного ключа сесії; у TLS 1.3 ключ виводиться через ефемерний ECDHE, звідси forward secrecy — витік приватного ключа не розшифрує раніше записаний трафік; актуальні версії — TLS 1.2/1.3, а SSL 2.0/3.0 і TLS 1.0/1.1 небезпечні й вимкнені.
- Можу пояснити HSTS: браузер приймає заголовок лише з HTTPS-відповіді, далі ходить на домен тільки через
https://; наступний http→https він робить внутрішньо (307 Internal Redirect), тому перевіряю фактичну схему у відповіді, а не в набраному URL.
Сертифікати, ланцюг довіри й MITM
- Можу описати сертифікат X.509: домени (поле SAN), відкритий ключ, issuer, строк дії, підпис CA — і що браузери дивляться на SAN, а не на застаріле Common Name.
- Розумію ланцюг довіри: Root CA → Intermediate → leaf; корені вшиті у trust store ОС/браузера, сервер віддає leaf із проміжними, браузер перевіряє підписи вгору до кореня плюс домен, строк і відкликання (OCSP/CRL).
- Знаю типові помилки сертифіката в Chrome:
NET::ERR_CERT_DATE_INVALID(прострочений/невірна дата),NET::ERR_CERT_COMMON_NAME_INVALID(домен ≠ SAN),NET::ERR_CERT_AUTHORITY_INVALID(невідомий CA, часто self-signed). - Розумію, що прострочений сертифікат staging — реальна причина масового «падіння» тестів уранці, тому спершу відкриваю середовище руками й дивлюсь на значок, а не лізу в код.
- Можу пояснити self-signed на dev/staging і обхід у тестах: Playwright
ignoreHTTPSErrors, SeleniumacceptInsecureCerts, curl-k; усвідомлюю компроміс — це робить тест сліпим до MITM, тож краще додати внутрішній кореневий CA у trust store агента CI. - Можу пояснити MITM і чому HTTPS протидіє двома механізмами разом (шифрування + автентифікація сервера), і що mitmproxy/Charles/Burp — легальний MITM, який працює лише зі своїм кореневим сертифікатом у trust store;
page.routeу Playwright — контрольований MITM усередині тесту.
Атаки рівня застосунку
- Можу пояснити XSS як впровадження чужого JS через неекранований ввід і три типи: Stored, Reflected, DOM-based; захист — output encoding, санітизація, фреймворки, CSP; небезпечні місця —
innerHTML,document.write,dangerouslySetInnerHTML. - Розумію, як AQA тестує XSS негативно: ввести
<img src=x onerror=alert(1)>чи"><script>і перевірити, що рядок показано як текст (<script>), а не виконано. - Можу пояснити CSRF і головну відмінність від XSS: зловмисник не читає відповідь, а змушує браузер жертви виконати дію її ж правами, бо кукі шлються автоматично; захист — CSRF-токен,
SameSite, перевіркаOrigin/Referer; знаю, що API-тести в обхід UI ловлять403, бо треба спершу дістати свіжий токен, а не хардкодити. - Можу пояснити SQL-injection через конкатенацію (
' OR '1'='1,'; DROP TABLE) і єдиний надійний захист — параметризовані запити; знаю, що payloadʼи ганяю лише на своїх середовищах.
Політики браузера, кукі й секрети
- Можу пояснити mixed content: пасивний (картинки — автоматично підвищується до HTTPS або блокується) і активний (скрипти, стилі, iframe, fetch — блокується); це часте джерело flaky-падінь, тому при «елемент не зʼявився» відразу дивлюсь консоль.
- Можу пояснити CSP як заголовок-білий-список джерел і другий рубіж проти XSS; знаю ключові директиви (
default-src,script-src,connect-src,frame-src) і режимContent-Security-Policy-Report-Only. - Розумію взаємодію CSP з Playwright:
page.evaluateзазвичай проходить (їде через CDP), але сувора CSP без'unsafe-eval'може ламатиwaitForFunction, аaddScriptTag/inline під суворою політикою потребуютьbypassCSP. - Можу пояснити атрибути кукі:
HttpOnly(недоступне зdocument.cookie, проти крадіжки сесії через XSS),Secure(лише по HTTPS),SameSite(проти CSRF); знаю, щоSameSite=NoneвимагаєSecureі що дефолтLax— лише в Chromium, тож атрибут задаю явно. - Можу пояснити, чому секретам не місце у фронтенді (весь JS видно в DevTools) і в query (
?token=…осідає в логах, історії,Referer, аналітиці) — секрети йдуть у заголовкуAuthorizationчи в тілі POST, а в CI — через змінні оточення.
Що посередник у мережі все одно бачить під час HTTPS-з'єднання (без ECH)?
Питання
Чим HTTPS відрізняється від HTTP «під капотом»?