Криптографія і транспорт для QA
Зміст
Тестувальник криптографію не пише — і саме тому найдорожчі знахідки тут не математичні, а конфігураційні. «Рядок у базі схожий на хеш», «сайт відкривається по HTTPS», «сертифікат валідний» — три вердикти, які самі собою не доводять нічого. Перший часто виявляється , який розкодовує сама браузера; другий не каже ні про версію протоколу, ні про (cipher suite); третій завжди означає «валідний щодо конкретного набору якорів довіри».
Механіка самого каналу — , , прапорці клієнтів — живе у главі HTTPS, TLS і безпека; власна вага цієї глави — версії, набори шифрів, валідація сертифіката й межа «спокій проти руху». Пароль як предмет вимог і лишається у главі Автентифікація: атаки й тестування — тут лише механіка хешування.
Хешування, шифрування, кодування
Три операції плутають тому, що всі «змінюють вигляд даних». Розрізняє їх не сила, а ключ і оборотність.
(hash function) бере вхід змінної довжини й дає вихід фіксованої — (digest); ключа не потребує, і головна її властивість — однобічність: маючи вихід, відтворити вхід має бути непрактично. Шифрування працює з ключем і має обернену операцію: у той самий ключ виконує операцію та обернену до неї, в асиметричному є пара, де публічний ключ може знати будь-хто, а приватний контролює лише власник. Base64 — схема кодування «двійкове → текст», щоб пронести двійкові дані каналом, який тримає лише ASCII: ключа немає, розкодовує сам браузер функцією atob(), обсяг зростає приблизно на третину, а URL-safe варіант опускає доповнення й замінює + і / на - та _.
| Операція | Ключ | Оборотність | Роль |
|---|---|---|---|
| Хешування | немає | однобічна за задумом | підписи, MAC, зберігання паролів |
| Шифрування | є | обернена операція існує | конфіденційність |
| Кодування | немає | розкодовує будь-хто | транспорт двійкових даних |
Не кожен хеш криптографічний: для цього потрібні чотири властивості разом — швидкість, однобічність, чутливість до будь-якої зміни входу й стійкість до колізій. Найуживаніша родина — SHA-2, і члени її звуться «SHA-» плюс довжина дайджесту в бітах: SHA-256, SHA-512. SHA-1 більше не вважається безпечним, MD5 названо небезпечним. У браузері це пробується руками: SubtleCrypto.digest() рахує дайджест і приймає SHA-1 попри попередження про його , а автентичності голий хеш не дає — для (message authentication code, MAC) є окремий SubtleCrypto.sign(). І готовий чек: Web Crypto API доступний лише в захищеному контексті.
// Playwright: на http-сторінці буде 'undefined'
const subtle = await page.evaluate(() => typeof crypto.subtle);
expect(subtle).toBe('object');
Найпряміша перевірка коштує пʼять секунд: якщо рядок розкодовується atob() у консолі без жодного ключа — це кодування, а не шифрування. І дзеркальне правило: розділ «Custom Algorithms» шпаргалки OWASP складається з однієї відповіді — «Don't do this.»
«MD5 зламано» — і саме тут плутають, що саме
Алгоритми дайджесту проєктують так, щоб дати три різні стійкості: до колізій, до прообразу й до другого прообразу. Ламаються вони окремо.
Зламані в MD5 саме колізії, і практично: колізія знаходиться приблизно за хвилину на звичайному ноутбуці, а за іншим результатом — за десять секунд або менше, і успішно застосували до сертифікатів X.509. Стійкість до прообразу при цьому тримається: складність 2^123.4 названа все ще доволі високою. Тобто «MD5 зламано» означає «знаходять два різні входи з однаковим хешем», а не «з хеша дістають вхід».
Звідси вердикт із межею: вживати MD5 там, де потрібна стійкість до колізій, нерозважливо, але там, де контрольна сума стоїть у протоколі виключно проти випадкових помилок, це лишається прийнятним. Контрольна сума артефакту збірки — не знахідка; MD5 у підписі чи сертифікаті — знахідка. Заміни названо поіменно: HMAC-SHA256 або AES-CMAC; сам HMAC-MD5 практично вразливим не визнано, тож із наявних протоколів його не виривають, але в нову схему включати не можна. І готове питання до архітектора: застосунок, що вживає MD5 хоч для чого, зобовʼязаний явно назвати очікувані від цього властивості безпеки.
Межа документа важлива: він про колізії й HMAC, а не про . Підстава «швидкий хеш не годиться саме для паролів» — інший аргумент з іншого джерела, і його дім — глава Автентифікація: атаки й тестування. Виводити «MD5 непридатний для паролів» із колізійної атаки не можна.
Зберігання паролів: сіль, memory-hard і три важелі
Точка старту однозначна: паролі не зберігають оборотним шифруванням — для них беруть захищені алгоритми (password hashing).
(salt) — випадкові дані, які додають до пароля перед хешуванням, тож хеш рахується від пари «пароль плюс сіль». Ламає вона наперед пораховані таблиці, зокрема райдужні, — і саме тому, що пошук по таблиці дешевий. Деталь, яку часто перевертають: сіль не є секретом і зберігається поруч із солоним хешем у тій самій базі. З боку алгоритму це окремий вхід: рекомендовано 16 байтів, унікальних для кожного пароля; 128 бітів достатньо для всіх застосувань і лише за браком місця скорочуються до 64.
Memory-hard означає, що платить не тільки часом. Argon2 цілить у найвищу швидкість заповнення памʼяті, водночас захищаючись від атак із компромісом «час проти памʼяті» — нарощування обчислювальних блоків перебір не здешевлює. Варіантів три: Argon2d ходить по памʼяті залежно від даних, Argon2i — незалежно й тому кращий для паролів, — гібрид, перша половина першого проходу як Argon2i, решта як Argon2d. Нормативно реалізація зобовʼязана підтримувати Argon2id, і правило вибору записане прямо: не знаєте різниці або вважаєте атаки по сторонніх каналах реальною — беріть Argon2id. Важелів у конфігурації три: p — ступінь паралелізму, m — обсяг памʼяті в кібібайтах (не менше 8*p), t — кількість проходів, якою час підкручують незалежно від памʼяті; підбирають емпірично — максимальне t, за якого час ще вкладається в бюджет.
Числа, які не можна звести в одне. RFC 9106 називає першою рекомендацією Argon2id з t=1, p=4, m=2^21 (2 GiB памʼяті), 128-бітною сіллю й 256-бітним тегом і зве це дефолтом для всіх середовищ; другою — t=3, p=4, m=2^16 (64 MiB) для середовищ із дефіцитом памʼяті. Шпаргалка OWASP Password Storage вимагає мінімуму: Argon2id, 19 MiB, дві ітерації, один ступінь паралелізму. Різниця на порядок — і це не помилка: RFC дає рівномірно безпечний дефолт під власне залізо, шпаргалка — нижню планку. Обидва джерела канонічні у своїй темі, тож чесна відповідь називає обидва набори з атрибуцією, а не одне число.
Категорія A04 з перелічує прийнятне зі свого боку: адаптивні солоні хеш-функції з — Argon2, yescrypt, scrypt або PBKDF2-HMAC-SHA-512; шпаргалка Password Storage додає bcrypt, але лише для легасі-систем. Що з цього перевіряє QA без доступу до криптографії: однакові хеші в дампі означають відсутність солі; «ми зберігаємо зашифрований пароль» — знахідка, а не синонім хешування; а якщо автентифікація відповідає миттєво при заявленому Argon2id з двома гігабайтами памʼяті, щось одне з двох неправда.
Транспорт: версії протоколу й набори шифрів
«Увімкнули TLS» — не відповідь: заборони формулюються поверсійно, і кожну ухвалював окремий документ.
| Що | Модальність джерела | Документ |
|---|---|---|
| TLS 1.0 і TLS 1.1 | вживати заборонено; узгодження з будь-якої версії не дозволяється | RFC 8996 |
| SSL 3.0 | вживати заборонено; узгодження з будь-якої версії не дозволяється | RFC 7568 |
| SSL 2.0 | клієнт не заявляє версію нижчу за 0x0300; сервер такою версією не відповідає й обриває зʼєднання | RFC 6176 |
Підстави названі, і це не «воно старе». У TLS 1.0/1.1 цілісність рукостискання й підписи автентифікації тримаються на SHA-1, (downgrade attack) коштує 2^77 операцій — значно нижче за прийнятний сучасний запас, — а сильніший хеш у цих версіях не вибирається, тож єдиний шлях оновлення — новіша версія; окремо названо й відсутність (authenticated encryption with associated data), якого до TLS 1.2 не було взагалі. У SSL 3.0 недетерміноване доповнення в конструкції CBC тривіально дозволяє відновити відкритий текст, а потоковий шифр не рятує: у широкому вжитку лише RC4 із серйозними зміщеннями. У SSL 2.0 повідомлення рукостискання не захищені взагалі, тож легко змушує клієнта вибрати слабший набір шифрів.
Дві тонкощі, які губить будь-який переказ. Перша: заборонено узгодити — не те саме, що заборонено розпізнати; сервер приймає будь-яке значення виду 03,XX як номер версії на рівні записів, але узгоджувати SSLv3 йому заборонено, і старий клієнт дістає алерт protocol_version і розрив, а не тиху деградацію. Друга: у TLS 1.3 версія узгоджується списком у розширенні supported_versions, а ServerHello.legacy_version сервер зобовʼязаний виставити в 0x0303 — поле в дампі показує 1.2, коли узгоджено 1.3. Від зниження TLS 1.3 захищається маркером в останніх 8 байтах свого випадкового значення: клієнт зобовʼязаний перевірити його й при збігу обірвати рукостискання алертом illegal_parameter, а з боку конфігурації те саме закриває TLS_FALLBACK_SCSV.
Чинна конфігурація за шпаргалкою OWASP: за замовчуванням TLS 1.3, TLS 1.2 — лише для сумісності; TLS 1.0 і 1.1 формально застарілі за RFC 8996 (березень 2021), заборонені PCI DSS і прибрані з основних браузерів; SSLv2 і SSLv3 вимкнені завжди. Старого клієнта, якщо він справді потрібен бізнесу, ізолюють на окремому без доступу до чутливих даних — основний не послаблюють.
Версія — половина відповіді: наборів шифрів багато, і вони дають різний рівень захисту. Вмикають автентифіковані режими — для TLS 1.3 стандартні AEAD-набори (AES-GCM або ChaCha20-Poly1305), для TLS 1.2 теж AEAD і без CBC: вони дають цілісність і автентичність на додачу до конфіденційності, і GCM та CCM беруть у першу чергу. Вимикають поіменно: null-шифри, анонімні TLS_*_anon_*, експортні TLS_*_EXPORT_*, транспорт RSA TLS_RSA_* і узгодження Діффі — Гелмана TLS_DH_* та TLS_ECDH_*, які не дають прямої секретності (perfect forward secrecy). Дві заборони TLS 1.3 діють для будь-якої версії й із будь-якої причини: не пропонувати й не узгоджувати ні RC4, ні шифри, стійкість яких нижча за 112 бітів. У самому TLS 1.3 вбудована у версію — статичні набори RSA і Діффі — Гелмана з нього прибрані. Окремо вимикають стиснення TLS: вразливість CRIME дозволяє відновити чутливу інформацію, зокрема сесійні cookie.
Перевіряють це інструментом, а не оком — онлайн (SSL Labs Server Test) або офлайн (testssl.sh), — і знахідкою є будь-який заборонений набір у виводі. Типова : «підняли сумісність зі старим клієнтом», після чого слабкі набори повертаються тихо.
Валідація сертифіката: пʼять різних причин однієї помилки
(certification path validation) доводить звʼязок між іменем субʼєкта й його відкритим ключем. Ланцюг потрібен тому, що користувач відкритого ключа початково має лише обмежену кількість надійних ключів центрів сертифікації, і дійсний шлях починається з сертифіката, виданого якорем довіри (trust anchor). Вибір якоря — питання політики, тож «сертифікат валідний» завжди означає «валідний щодо конкретного набору якорів»; звідси й різна поведінка того самого стенду в браузері та в CI-агенті.
Кожен сертифікат у ланцюзі мусить задовольняти чотири умови: підпис перевіряється робочим відкритим ключем, поточний час входить у період дії, сертифікат не відкликано, імʼя видавця збігається з очікуваним. Плюс відсікач: клієнт зобовʼязаний відхилити сертифікат із непідтримуваним критичним розширенням — це , а не попередження.
Період дії — це зобовʼязання центру сертифікації підтримувати інформацію про статус сертифіката, а не «термін придатності файлу», і межі його включні. Відкликання перевіряють по списку відкликаних сертифікатів (certificate revocation list, CRL) — він підписаний центром, має мітку часу й лежить у відкритому репозиторії, — і тут же межа механізму: часова роздільність відкликання обмежена періодом випуску CRL, саме тому відкликаний сертифікат ще якийсь час працює.
Імʼя звіряється з двох боків: клієнт має (reference identifier), сервер показує показаний (presented identifier). Дивитися треба в розширення subjectAltName, поле dNSName; рядки, схожі на доменні імена, у Common Name не включають і не перевіряють, а сучасні версії Chrome CN просто ігнорують. Порівняння нечутливе до регістру, але кожен лейбл має збігтися. Для дві дослівні вимоги: символ підстановки рівно один і він займає крайній лівий лейбл цілком; покриває він рівно один лейбл, тож *.staging.example.com не покриває a.b.staging.example.com, а ще й ручається за будь-який однолейбловий хост у домені — це порушення принципу .
При розбіжності автоматичний клієнт має обірвати спробу звʼязку з помилкою поганого сертифіката й записати її в лог; налаштування, яке цю поведінку вимикає, дозволено мати, але за замовчуванням увімкненим воно бути не може — «ігнорувати помилки сертифіката» в тестовому є саме таким свідомим налаштуванням. Звідси проста дисципліна: помилку сертифіката розкладають, а не переказують — підпис, строк, відкликання, видавець, імʼя — пʼять причин із пʼятьма лагодженнями. Самопідписаний сертифікат на стенді — це відсутній , і лікується він внутрішнім коренем у сховищі довіри агента, а не вимиканням перевірки.
Дані в спокої й дані в русі, і ключі до обох
Набір алгоритмів обирають за задачею: надійно зберігати дані — під захист даних у спокої (data at rest), передавати й приймати — під захист даних у русі (data in transit). «Увімкнули HTTPS» відповідає лише за рух, «зашифрували диск» — лише за спокій.
У спокої все починається не з алгоритму, а з моделі загроз — від кого саме ви захищаєте ці дані. Звідси й шар: шифрування на рівні заліза захищає від фізичної крадіжки сервера й не дає нічого проти віддаленої компрометації. Найдешевший захист узагалі не криптографічний: найкраще чутливу інформацію не зберігати. Числа з тієї самої шпаргалки: AES із ключем щонайменше 128 бітів (в ідеалі 256) і безпечним режимом, еліптичні криві на кшталт Curve25519 для асиметричного, а RSA — не коротше за 2048 бітів і обовʼязково з випадковим доповненням (воно ж OAEP).
У русі транспорт дає конфіденційність, цілісність і автентифікацію — причому особу клієнта не перевіряють, поки не задіяні клієнтські сертифікати. TLS має бути на всіх сторінках: сторінка, що його не примушує, дає нагоду перехопити сесійні токени або вкинути шкідливий JavaScript у відповідь. Для API правило суворіше за редирект: суто API-ендпоінти мають вимикати HTTP узагалі, а якщо це неможливо — відмовляти запитам по нешифрованому HTTP, а не перенаправляти їх, бо до моменту редиректу запит із токеном уже пройшов відкритим каналом.
// API-ендпоінт по http має відмовити, а не привести редиректом на https
const res = await request.get('http://api.example.com/orders', { maxRedirects: 0 });
expect([301, 302, 307, 308]).not.toContain(res.status());
Ключ до обох задач — це не значення, а життєвий цикл: генерація, розподіл, знищення. Один ключ має одне призначення, бо обмеження вжитку обмежує шкоду від компрометації, і ключ шифрують ключем не слабшим за нього. Звідси конвертна схема: (DEK) шифрує дані, (KEK) шифрує DEK, і KEK зберігають окремо — зашифрований DEK можна тримати поруч із даними, але скористатися ним вийде лише в того, хто дістане ще й KEK з іншої системи.
Де ключ лежати не може: у коді — найпряміша перевірка з усіх, робиться грепом по репозиторію; у змінних оточення — їх можна випадково розкрити через функції на кшталт phpinfo() або через файл /proc/self/environ; у відкритому вигляді — ніколи; поруч із даними, які він захищає — якщо дані в базі, ключі кладуть у файлову систему. Місце для них — криптографічне сховище: окремий апаратний модуль або ізольований криптографічний сервіс. Тут і межа з гігієною секретів у CI, яка живе в розділі про Git і CI/CD: вимоги до криптографічного ключа суворіші за звичну гігієну секретів, і змінна оточення сховищем для нього не є.
Ще три речі для ревʼю: ротація існує до інциденту або не існує взагалі; план відновлення після компрометації має бути записаний і легко доступним; а ключі, вектори ініціалізації, ідентифікатори сесій, й токени скидання пароля мають походити з (cryptographically secure pseudo-random number generator), а не зі звичайного генератора. Готова знахідка: UUID першої версії складається з мітки часу й MAC-адреси, тобто випадковим не є, а четверта випадкова, але чи бере вона CSPRNG — залежить від реалізації, тож на випадковість UUID покладатися не варто.
Cryptographic Failures у OWASP Top 10
У редакції 2025 року категорія опустилася на дві позиції, до четвертої, і покриває не лише «слабку криптографію»: сюди входять брак криптографії, недостатньо сильна криптографія, витік криптографічних ключів і повʼязані помилки. Карта категорій цілком — у главі Безпека очима QA.
Перший крок профілактики не криптографічний: дані, які застосунок обробляє, зберігає й передає, треба класифікувати й розмітити; паролі, номери платіжних карток, медичні записи, персональні дані й бізнес-таємниці потребують посиленого захисту, особливо коли підпадають під GDPR або PCI DSS. Далі: усі мають шифруватися на транспортному рівні, усі чутливі мають бути зашифровані, а в русі — тільки протоколи TLS 1.2 і вище, з прямою секретністю, без CBC-шифрів і з підтримкою квантово-стійкого обміну ключами. І рідкісний випадок, коли джерело дає дату: до постквантової криптографії радять готуватися вже зараз, щоб високоризикові системи були в безпеці не пізніше кінця 2030 року — таку дату можна класти прямо в .
Два сценарії атаки показують обидва боки глави. Дані в русі: сайт не примушує TLS на всіх сторінках, атакувальник у ненадійній мережі знижує зʼєднання з HTTPS до HTTP і краде сесійну cookie — сесійний бік у главі Сесії та токени. Дані в спокої: база зберігає паролі несолоними або простими хешами, і всі несолоні хеші розкриваються райдужною таблицею.
Типові помилки
«Дані зашифровані — ось же нечитабельний рядок». Виглядає як шифрування, а насправді часто Base64: розкодовується atob() без ключа, отже ключа немає.
«MD5 зламано, отже з хеша дістануть пароль». Виглядає як логічний висновок, а насправді зламано колізії, тоді як стійкість до прообразу тримається.
«Пароль зберігається у вигляді хешу». Виглядає як відповідь, а насправді питання в тому, чи є сіль і фактор роботи. Сюди ж «сіль треба заховати» — перевернуте правило: сіль секретом не є, ховають ключі.
«Сайт по HTTPS — транспорт закрито». Виглядає як закритий пункт, а насправді це три різні перевірки: усі сторінки, версія й набір шифрів. І дамп тут бреше: сервер, що узгодив TLS 1.3, виставляє в ServerHello версію 1.2.
«Сертифікат валідний — браузер не лається». Виглядає як вердикт, а насправді валідність рахується щодо конкретного набору якорів довіри. Сюди ж «є wildcard, отже всі піддомени покриті»: підстановка покриває рівно один лейбл.
«Диск зашифровано, дані захищені». Виглядає як , а насправді це захист від фізичної крадіжки сервера, який нічого не дає проти віддаленої компрометації.
Підсумок
- Ключ і оборотність розводять три операції. Хешування — без ключа й однобічне, шифрування — з ключем і оборотне, кодування — без ключа й розкодовується будь-ким.
- «MD5 зламано» — про колізії. Стійкість до прообразу тримається, контрольна сума проти випадкових помилок лишається прийнятною, підпис і сертифікат — ні.
- Пароль не шифрують, а хешують із сіллю й фактором роботи. Сіль унікальна на пароль і не є секретом; числа RFC 9106 і мінімум OWASP різняться на порядок, і зводити їх в одне не можна.
- Транспорт перевіряється трьома питаннями: яка версія узгоджується, який набір шифрів узгоджується і що показує сертифікат.
- Помилку сертифіката розкладають на пʼять причин — підпис, строк, відкликання, видавець, імʼя — а вимкнення перевірки лагодженням не є.
- Спокій і рух — окремі задачі, і ключ до обох є життєвим циклом, а не значенням у змінній: не в коді, не у змінній оточення, не поруч із даними.
Можливі питання
«Чим хешування відрізняється від шифрування й кодування?» Дивляться, чи назвете ви критерій, а не приклади: ключ і оборотність, плюс перевірка — Base64 розкодовує сам браузер.
«Чому не можна вживати MD5?» Питання-фільтр на точність. Очікують «зламано колізії», а не «з хеша дістають вхід», і бонусом — межу: контрольна сума проти випадкових помилок прийнятна, підпис і сертифікат — ні.
«Як правильно зберігати паролі й навіщо сіль?» Слухають три речі разом: не шифрування, а хешування; унікальна сіль на кожен пароль; повільний алгоритм із фактором роботи.
«Які версії TLS прийнятні і що ви перевірите, крім версії?» Хочуть підстави: SHA-1 і вартість атаки зниження 2^77 для TLS 1.0/1.1, конструкція CBC для SSL 3.0. Далі — набори шифрів: увімкнені автентифіковані режими, вимкнені null-, анонімні, експортні, RC4 і набори без прямої секретності.
«Що означає “сертифікат валідний” і чому не можна вимкнути перевірку в автотестах?» Тут перевіряють обережність формулювань: пʼять перевірок, валідність відносна до набору якорів довіри, а налаштування дозволено мати, але не за замовчуванням.
«Де зберігати ключі шифрування?» Питання на клас вимог: не в коді, не у змінній оточення, не поруч із даними, ротація готується до інциденту. Слабку відповідь видає «поклали в секрети CI» без різниці між секретом і криптографічним ключем.
«Що входить у Cryptographic Failures із OWASP Top 10?» Хочуть почути, що це не лише «слабкий шифр»: брак криптографії, недостатньо сильна криптографія й витік ключів — той самий .
Джерела
Хешування, шифрування, кодування
- MDN Glossary — Hash function — дайджест, чотири властивості криптографічного хешу, SHA-2, статус SHA-1 і MD5.
- MDN Glossary — Base64 — кодування «двійкове → текст»,
atob(), обсяг, URL-safe варіант. - MDN — SubtleCrypto: digest() method — попередження про SHA-1, MAC через
sign(). - MDN — Web Crypto API — лише захищений контекст.
- OWASP Cheat Sheet — Key Management — симетричний і асиметричний ключі, безключовість хешу.
- OWASP Cheat Sheet — Cryptographic Storage — заборона власних алгоритмів.
«MD5 зламано» — і саме тут плутають, що саме
- RFC 6151 — Updated Security Considerations for the MD5 Message-Digest and the HMAC-MD5 Algorithms — три стійкості, практичні колізії й атака на X.509, складність прообразу, межа «контрольна сума проти помилок», заміни, HMAC-MD5.
- MDN Glossary — Hash function — MD5 і SHA-1 як небезпечні криптографічні хеші.
Зберігання паролів: сіль, memory-hard і три важелі
- RFC 9106 — Argon2 Memory-Hard Function for Password Hashing and Proof-of-Work Applications — memory-hard, три варіанти й нормативність Argon2id, параметри
m,t,p, довжина солі, дві рекомендовані конфігурації. - MDN Glossary — Salt — означення солі, райдужні таблиці, чому сіль не секрет.
- OWASP Cheat Sheet — Cryptographic Storage — заборона оборотного шифрування паролів.
- OWASP Cheat Sheet — Password Storage — мінімальна конфігурація Argon2id, bcrypt у легасі-системах.
- OWASP Top 10:2025 — Cryptographic Failures — перелік прийнятних адаптивних солоних хеш-функцій.
Транспорт: версії протоколу й набори шифрів
- RFC 8996 — Deprecating TLS 1.0 and TLS 1.1 — заборона TLS 1.0/1.1: SHA-1, 2^77, відсутність AEAD до TLS 1.2.
- RFC 7568 — Deprecating Secure Sockets Layer Version 3.0 — заборона SSLv3, доповнення CBC, зміщення RC4, «розпізнати проти узгодити».
- RFC 6176 — Prohibiting Secure Sockets Layer (SSL) Version 2.0 — заборона SSL 2.0, незахищене рукостискання.
- RFC 8446 — TLS 1.3 —
supported_versions,legacy_version, захист від зниження, заборона RC4 і слабких шифрів, вилучення статичних RSA і Діффі — Гелмана. - OWASP Cheat Sheet — Transport Layer Security — чинна конфігурація версій, ізоляція старих клієнтів, набори на вимкнення, стиснення й CRIME, перевірка конфігурації.
- OWASP Cheat Sheet — Cryptographic Storage — перевага режимів GCM і CCM.
Валідація сертифіката: пʼять різних причин однієї помилки
- RFC 5280 — Internet X.509 PKI Certificate and CRL Profile — що доводить шлях сертифікації, якір довіри як питання політики, чотири умови, критичні розширення, період дії та включні межі, CRL і роздільність відкликання.
- RFC 9525 — Service Identity in TLS — очікуваний і , SAN замість CN, правила wildcard, поведінка при розбіжності.
- OWASP Cheat Sheet — Transport Layer Security — ігнорування CN у Chrome, ризик wildcard.
Дані в спокої й дані в русі, і ключі до обох
- OWASP Cheat Sheet — Key Management — вибір алгоритмів під спокій або рух, життєвий цикл ключа, одне призначення, криптографічне сховище, план відновлення.
- OWASP Cheat Sheet — Cryptographic Storage — модель загроз перед алгоритмом, межа шифрування на рівні заліза, AES і RSA числами, OAEP, DEK і KEK, ключі не в коді й не у змінних оточення, ротація, CSPRNG і ненадійність UUID.
- OWASP Cheat Sheet — Transport Layer Security — властивості транспорту, TLS на всіх сторінках, відмова замість редиректу для API.
Cryptographic Failures у OWASP Top 10
- OWASP Top 10:2025 — Cryptographic Failures — обсяг категорії й нова позиція, класифікація даних, вимоги до шифрування, дедлайн постквантової криптографії, два сценарії атаки.
Чим хешування відрізняється від шифрування й кодування?
Сильна відповідь дає критерій, а не приклади: три операції розводять наявність ключа й можливість повернути дані назад. ключа не бере й задумана однобічною — з (digest) фіксованої довжини вхід відтворити має бути непрактично; звідси її ролі: підписи, MAC, . Шифрування без ключа не існує, і зворотний шлях у ньому передбачений задумом: у симетричній схемі ключ той самий на обидві операції, в асиметричній — пара, де публічну половину можна роздавати, а приватну тримає лише власник. Кодування на кшталт не має ні ключа, ні секрету взагалі: це спосіб протягнути двійкові дані каналом, що приймає тільки ASCII, і розгорнути його може будь-хто. Практичний наслідок для тестувальника: фраза «дані зашифровані» без відповіді на питання «яким ключем і хто ним володіє» не гарантує нічого.
У відповіді API прилетів нечитабельний рядок. Як зрозуміти, шифр це чи кодування?
Перевірка займає кілька секунд і не потребує ні доступу до коду, ні знання криптографії: відкрийте браузера й згодуйте рядок atob(). Якщо на виході зʼявився осмислений текст, ключа тут не було — отже, це кодування, а слово «зашифровано» в описі фічі помилкове. Непрямі ознаки Base64 видно ще до перевірки: алфавіт обмежений латиницею, цифрами, +, / і хвостовими =, а сам рядок приблизно на третину довший за вихідні дані; у URL-safe варіанті замість + і / стоятимуть - та _, а доповнення може бути відкинуте. Формулювання для тікета звучить так: дані передаються у відкритому вигляді, просто в іншому поданні. Це повноцінна знахідка, якщо йдеться про персональні дані, токен або внутрішній ідентифікатор.
Які властивості роблять хеш криптографічним і які алгоритми вже не можна брати?
Не будь-яка функція, що дає «рядок фіксованої довжини», годиться для безпеки. Криптографічний хеш має чотири властивості одночасно: рахуватися швидко, не піддаватися оберненню, змінювати вихід до невпізнання від найдрібнішої правки входу й не давати колізій — двох різних входів з одним дайджестом. Робоча родина сьогодні — SHA-2, де число в назві означає довжину дайджесту в бітах. SHA-1 із безпечних вибув, MD5 названо небезпечним. Практична деталь, на якій ловлять: браузерний SubtleCrypto.digest() спокійно порахує SHA-1 — API попереджає, але не забороняє, тож «у нас працює» тут не аргумент. І ще одна межа: сам по собі хеш авторства не доводить, для цього потрібен (message authentication code, MAC) — окремий виклик SubtleCrypto.sign().
Що саме мають на увазі, коли кажуть «MD5 зламано»?
Дайджест-функції тримають три різні стійкості — до колізій, до прообразу й до другого прообразу, — і падають вони не разом. У MD5 упали колізії, причому не теоретично: пару різних входів з однаковим хешем знаходять приблизно за хвилину на звичайному ноутбуці, за іншим результатом — за секунди, і цей трюк уже застосовували до сертифікатів X.509. Стійкість до прообразу при цьому нікуди не поділася: RFC 6151 оцінює складність у 2^123.4 і називає її досі доволі високою. Тому відповідь «з MD5-хеша дістануть вихідний пароль» читається як нерозуміння: зламали здатність підібрати пару входів, а не здатність розгорнути хеш назад. Це питання-фільтр саме на точність формулювань, і бонусні бали дістає той, хто одразу проводить межу застосування.
У проєкті знайшли MD5. Це завжди дефект?
Ні, і саме тут відрізняють інженера від людини, яка читала заголовки новин. Якщо контрольна сума потрібна протоколу винятково для того, щоб зловити випадкове псування даних, MD5 у цій ролі лишається допустимою — RFC 6151 це прямо визнає. Якщо ж від хешу очікують стійкості до навмисно підібраної колізії — підпис, сертифікат, перевірка цілісності проти зловмисника, — це знахідка. Хеш файлу зі збірки я тікетом не оформлю; MD5 в алгоритмі підпису сертифіката оформлю негайно. Заміни джерело називає поіменно: HMAC-SHA256 або AES-CMAC. Окремий нюанс для точності: сам HMAC-MD5 практично вразливим не оголошували, тож із наявних протоколів його не виривають, але тягнути в нову схему не можна. І найкорисніше питання до архітектора: які саме властивості безпеки ви очікуєте від цього MD5 — воно змушує назвати вимогу вголос.
Як правильно зберігати паролі й навіщо потрібна сіль?
Стартова умова однозначна: оборотне шифрування для паролів виключене — хто дістав ключ, той дістав усю базу; потрібна спеціалізована функція . Друга умова — (salt): випадкові байти, які приклеюються до пароля до обчислення, тож у базу лягає дайджест від пари, а не від самого пароля. Роль у солі вузька й конкретна — вона знищує сенс наперед порахованих таблиць, зокрема райдужних, бо словник під кожну можливу сіль побудувати неможливо. Сіль має бути своя на кожен обліковий запис: рекомендують 16 байтів, 128 бітів вистачає для всіх застосувань, і лише за жорсткої економії місця беруть 64. Річ, яку постійно перевертають: сіль не є секретом і спокійно лежить у тому самому рядку бази поруч із солоним хешем — ховають ключі, а не сіль. Третя умова — : алгоритм має бути навмисно повільним і налаштовуваним.
Що таке memory-hard функція і який варіант Argon2 брати?
Класичний повільний хеш змушує платити процесорним часом, а memory-hard — ще й памʼяттю, і саме це ламає економіку перебору на відеокартах та спеціалізованому залізі. Argon2 цілиться в максимальну швидкість заповнення памʼяті й одночасно закриває компроміс «час проти памʼяті»: докласти обчислювальних блоків, щоб памʼяті знадобилося менше, не вийде. Варіантів три. Argon2d ходить по памʼяті залежно від самих даних; Argon2i — незалежно, через що краще підходить паролям; склеює обидва — першу половину першого проходу поводиться як Argon2i, далі як Argon2d. Нормативно реалізація зобовʼязана вміти Argon2id, а правило вибору сформульоване просто: не бачите різниці або вважаєте по сторонніх каналах реальною — беріть Argon2id.
Які параметри Argon2 налаштовують і які числа називають джерела?
Важелів три: p — скільки потоків рахують паралельно, m — скільки кібібайтів памʼяті займе обчислення (не менше ніж 8*p), t — кількість проходів, якою час тягнуть угору окремо від памʼяті. Підбирають їх емпірично: беруть найбільше t, за якого відповідь ще вкладається в бюджет автентифікації. Далі починається місце, де кандидати гублять точність, бо канонічні числа різні. RFC 9106 першою рекомендацією дає Argon2id з t=1, p=4, m=2^21 — це 2 GiB памʼяті, — 128-бітною сіллю й 256-бітним тегом, а другою, для середовищ із дефіцитом памʼяті, — t=3, p=4, m=2^16 (64 MiB). Шпаргалка OWASP Password Storage вимагає іншого: щонайменше 19 MiB, дві ітерації й один ступінь паралелізму. Різниця на порядок не є суперечністю: RFC описує рівномірно безпечний дефолт під власне залізо, шпаргалка фіксує нижню планку прийнятного. Правильна відповідь називає обидва набори з атрибуцією, а не зводить їх в одне число.
Що QA перевірить у зберіганні паролів, не маючи доступу до криптографічного коду?
Три речі видно збоку. Перша — дамп або тестова таблиця: якщо два акаунти з однаковим паролем мають однаковий рядок хешу, солі немає, і це знахідка без жодного реверс-інжинірингу. Друга — формулювання в документації та в розмові з розробником: «ми зберігаємо зашифрований пароль» описує оборотну операцію, а це дефект дизайну, а не синонім хешування. Третя — : якщо в специфікації записаний Argon2id із двома гігабайтами памʼяті, а логін під навантаженням відповідає миттєво, то або конфіг не той, що заявлений, або заявлене не відповідає дійсності. Жодна з перевірок не вимагає знання математики — достатньо розуміти, які сліди залишає правильна конфігурація.
Які версії TLS сьогодні прийнятні і на якій підставі старі заборонили?
Дефолтом ставлять TLS 1.3, TLS 1.2 лишають лише заради сумісності, усе нижче вимкнене. Важливо, що це не «воно старе»: кожну заборону ухвалював окремий документ із названою причиною. RFC 8996 закриває TLS 1.0 і 1.1 — цілісність й підписи автентифікації там тримаються на SHA-1, вартість атаки зниження (downgrade attack) оцінена в 2^77 операцій, що нижче за прийнятний сучасний запас, а вибрати сильніший хеш усередині цих версій неможливо, тож єдиний шлях — перейти на новішу; до TLS 1.2 не було ще й . RFC 7568 закриває SSL 3.0 через недетерміноване доповнення в конструкції CBC, яке дозволяє відновити відкритий текст, а єдиний поширений потоковий шифр там — RC4 із серйозними зміщеннями. RFC 6176 закриває SSL 2.0, де повідомлення рукостискання не захищені взагалі, тож спокійно змушує сторони вибрати слабший набір. Якщо древній клієнт справді потрібен бізнесу, його виносять на окремий без чутливих даних, а основний не послаблюють.
Крім версії протоколу, що ще ви перевірите в транспорті?
(cipher suite) — на одній і тій самій версії можна домовитися про дуже різний рівень захисту. Вмикають автентифіковані режими: у TLS 1.3 це стандартні AEAD-набори з AES-GCM або ChaCha20-Poly1305, у TLS 1.2 — теж AEAD і без CBC, з пріоритетом GCM та CCM; вони додають цілісність і автентичність поверх конфіденційності. На вимкнення джерело дає закритий перелік: TLS_*_anon_* (сервер узагалі не автентифікується), TLS_*_EXPORT_* (спадок навмисно ослабленої криптографії), null-шифри (шифрування немає як такого), а також TLS_RSA_*, TLS_DH_* і TLS_ECDH_* — ці три через відсутність прямої секретності (perfect forward secrecy). Дві заборони діють незалежно від версії й від будь-яких аргументів: RC4 і все, чия стійкість нижча за 112 бітів, не пропонують і не узгоджують. Окремо гасять стиснення TLS — через CRIME воно дозволяє відновити чутливу інформацію, включно з сесійними cookie. Перевіряють це інструментом, а не оком: SSL Labs онлайн або testssl.sh локально, і будь-який заборонений набір у виводі є знахідкою. Типова приходить під приводом «підняли сумісність зі старим клієнтом».
У дампі ServerHello стоїть версія 1.2, а адміністратор клянеться, що це TLS 1.3. Хто помиляється?
Ніхто. У TLS 1.3 узгодження версії винесене з того поля, куди звикли дивитися: клієнт перелічує підтримувані версії в розширенні supported_versions, а сервер зобовʼязаний записати в ServerHello.legacy_version значення 0x0303, тобто «1.2». Механізм узгодження з TLS 1.2 при цьому оголошено застарілим: за наявності розширення сервер не має права брати версію з ClientHello.legacy_version і зобовʼязаний користуватися тільки supported_versions. Тому висновок «у нас TLS 1.2» з дампа робити не можна — дивитися треба саме на розширення, а не на legacy-поле. На практиці простіше не сперечатися над байтами, а прогнати testssl.sh чи Server Test і взяти узгоджену версію з їхнього звіту. Питання типове й перевіряє одну річ: людина розуміє формат чи впізнає числа.
Що таке атака зниження версії і як від неї захищається TLS 1.3?
Ідея атаки в тому, щоб посередник підмінив або обрізав список можливостей і змусив дві цілком сучасні сторони домовитися про старіший, слабший варіант — далі ламають уже його, а не актуальний протокол. TLS 1.3 вбудував проти цього маркер: сервер, який насправді вміє 1.3, але узгоджує нижчу версію, зобовʼязаний записати спеціальне значення в останні вісім байтів свого випадкового поля, а клієнт зобовʼязаний його перевірити й при збігу обірвати рукостискання алертом illegal_parameter. З боку конфігурації ту саму дірку закриває TLS_FALLBACK_SCSV. Розрізняти при цьому треба дві різні речі. Узгодити TLS 1.2 з клієнтом, який не вміє 1.3, — це штатне узгодження версії, і шпаргалка OWASP його прямо дозволяє заради сумісності. А от відкат — повторна спроба рукостискання на нижчій версії після невдалої першої — це вже поверхня для атаки: RFC 7568 забороняє відкат на SSLv3 окремою вимогою, решту закривають вимкнені старі версії й TLS_FALLBACK_SCSV. Знахідкою є саме відкат, а не сама наявність TLS 1.2 у переліку.
Що насправді означає «сертифікат валідний»?
Означає «прийнятий відносно конкретного набору якорів довіри», і без цього уточнення фраза неповна. (certification path validation) доводить, що конкретне імʼя субʼєкта справді повʼязане з конкретним відкритим ключем; ланцюг узагалі існує тому, що клієнт початково довіряє лише невеликому переліку ключів центрів сертифікації, і дійсний шлях мусить дійти до сертифіката, виданого одним із них. Який саме перелік вважати довіреним — рішення політики, а не математики. Звідси береться найчастіше непорозуміння на стендах: браузер із системним сховищем відкриває сайт, а CI-агент у з мінімальним набором коренів на тому самому URL падає — і жоден із них не «глючить». Тому в баг-репорті коректно писати не «сертифікат невалідний», а «шлях до якоря довіри цього клієнта не будується».
Помилка сертифіката прилетіла в тест. На які причини її розкладають?
Розкладати обовʼязково, бо за одним червоним екраном стоять різні поламки з різними лагодженнями. Кожна ланка ланцюга мусить пройти чотири перевірки: підпис звіряється відкритим ключем видавця, поточний час потрапляє в період дії (межі якого включні), сертифікат не значиться відкликаним, імʼя видавця збігається з очікуваним. Пʼятою перевіркою поверх ланцюга йде звірка ідентифікатора: клієнт має (reference identifier), сервер показує свій (presented identifier), і вони мають зійтися. Є ще жорсткий відсікач: критичне розширення, якого клієнт не розуміє, зобовʼязує його відхилити сертифікат — це , а не попередження. Практичний висновок: у тікеті пишуть, яка саме з пʼяти умов не виконалася, бо прострочений сертифікат, невідомий корінь і невірне імʼя лікуються трьома різними діями. Окрема деталь для повноти: відкликаний сертифікат ще якийсь час приймається, бо роздільність відкликання обмежена періодом випуску списку відкликаних сертифікатів (certificate revocation list, CRL).
Що покриває сертифікат на *.staging.example.com і чому wildcard не безкоштовний?
Підстановка діє рівно на один лейбл, тож api.staging.example.com покривається, а a.b.staging.example.com — ні. Правило суворе й складається з двох частин: символ підстановки в імені може бути тільки один, і він мусить займати найлівіший лейбл повністю. Порівняння імен нечутливе до регістру, але всі лейбли повинні збігтися — «майже те саме імʼя» не працює. Дивитися при цьому треба в розширення subjectAltName, поле dNSName: рядки, схожі на доменні імена, у Common Name не включають і не перевіряють, а сучасні версії Chrome це поле просто ігнорують, тож «у CN же написано правильно» — не аргумент. Ціна у тому, що один сертифікат і один приватний ключ автоматично ручаються за будь-який однолейбловий хост усередині домену, включно з тим, який завтра підніме інша команда, — прямий конфлікт із принципом .
Чому не можна просто ввімкнути «ігнорувати помилки сертифіката» в автотестах?
Тому що це вимкнення тієї самої перевірки, яку продукт мусить проходити в проді, і разом із нею зникає цілий клас регресій: прострочений сертифікат, підмінене імʼя, зламаний ланцюг після переїзду. Стандарт формулює межу акуратно: автоматичний клієнт при розбіжності зобовʼязаний обірвати спробу звʼязку з помилкою поганого сертифіката й записати її в лог, а налаштування, яке цю поведінку скасовує, мати дозволено — але вмикати його за замовчуванням не можна. Тобто у — свідоме й обмежене в часі рішення, а не рядок у базовому конфігу. Типовий привід увімкнути його — самопідписаний сертифікат на стенді, а це насправді відсутній , і лікується він внутрішнім коренем у сховищі довіри агента. Ціна помилки помітна не одразу: під загальним прапорцем мовчки проковтне справжню поламку сертифіката, і зелений прогін уже нічого не доводитиме.
Чим задача «дані в спокої» відрізняється від «даних у русі» і чому «ми ввімкнули HTTPS» не закриває питання?
Це два різні набори алгоритмів під дві різні задачі: надійно зберігати й надійно передавати. HTTPS відповідає лише за другу, шифрування диска — лише за першу, і жодне з двох не покриває обидві. Починають не з алгоритму, а з моделі загроз: шифрування на рівні заліза рятує від виносу сервера з дата-центру й нічого не дає проти зловмисника, який отримав віддалений доступ до працюючої системи. Найдешевший захист узагалі не криптографічний — не зберігати те, чого зберігати не обовʼязково. Числа для спокою: AES із ключем від 128 бітів (в ідеалі 256) у безпечному режимі, криві на кшталт Curve25519 для асиметрики, RSA не коротше за 2048 бітів і обовʼязково з випадковим доповненням OAEP. Для руху окремо памʼятають, що TLS має стояти на всіх сторінках, а суто API-ендпоінт мусить нешифрований HTTP відхиляти, а не перенаправляти: до моменту редиректу запит із токеном уже пролетів відкритим каналом.
Де не можна зберігати ключ шифрування?
Список коротких «ні» такий: не в коді репозиторію — це, до речі, найпростіша перевірка з усіх, робиться грепом; не у змінних оточення, бо їх видає назовні і phpinfo(), і читання /proc/self/environ; ніколи у відкритому вигляді; і не поруч із даними, які цей ключ захищає — якщо дані в базі, ключам місце у файловій системі. Правильне місце — криптографічне сховище: апаратний модуль або ізольований криптографічний сервіс. Звідси конвертна схема: (DEK) шифрує самі дані, (KEK) шифрує DEK і зберігається окремо, тож зашифрований DEK можна тримати поряд із даними — без KEK з іншої системи він марний. Загальні правила поруч: ключ шифрують не слабшим ключем, і кожен ключ має рівно одне призначення, бо це обмежує шкоду від компрометації. Тут же межа, на якій валяться відповіді: «поклали в секрети CI» — це гігієна секретів, а вимоги до криптографічного ключа суворіші, і змінна оточення сховищем для нього не рахується. Останнє, що дивляться в ревʼю: ротація готується до інциденту, а не після нього, план відновлення має бути записаний, а самі ключі, вектори ініціалізації, ідентифікатори сесій, й токени скидання пароля мусять походити з .
Три ситуації, у яких тестувальник закриває криптографічний тікет, не написавши жодного рядка криптографії: перевірка твердження «дані зашифровані», аудит транспорту перед релізом за звітом сканера й розбір помилки сертифіката в CI на конкретну причину. Скрізь — що дивитися, який висновок з цього робиться і що писати в тікет.
Кейс 1. «У нас це поле зашифроване» — перевірка за пʼять хвилин
Прийшов тікет на : у відповіді профілю приїжджає поле contactRef, аналітик питає, чи не витікають там персональні дані. Розробник відповідає, що поле зашифроване. Перевіряти таке твердження треба самому, і починається перевірка не з коду, а з .
{
"userId": 8412,
"contactRef": "b2xla3NhbmRyLmtAZXhhbXBsZS5jb206KzM4MDY3MTIzNDU2Nw=="
}
Хвіст із ==, алфавіт лише з латиниці й цифр, довжина помітно більша за очікуваний вміст — усе це вже підозріле. Далі один рядок у консолі браузера:
atob('b2xla3NhbmRyLmtAZXhhbXBsZS5jb206KzM4MDY3MTIzNDU2Nw==');
// 'oleksandr.k@example.com:+380671234567'
Ключа ніхто не питав — отже, шифрування тут немає, є кодування. Пошта й телефон їдуть у відповіді відкрито, просто в іншому поданні. Таблиця, за якою висновок робиться однаково щоразу:
| Що ви бачите | Діагноз | Формулювання для тікета |
|---|---|---|
atob() дає осмислений текст | Base64, ключа немає | Персональні дані передаються відкрито в закодованому вигляді |
atob() дає бінарне сміття, довжина кратна блоку | Схоже на шифротекст | Потрібне питання до розробника: який алгоритм, режим і де ключ |
| Рядок фіксованої довжини на будь-якому вході (32, 40, 64 hex-символи) | Дайджест | Уточнити алгоритм і чи є сіль, якщо це пароль |
| Рядок із частин, розділених крапками | JWT: кожна частина закодована Base64URL | Дивитися корисне навантаження — у підписаному токені воно читається без ключа |
Помічник, який ту саму перевірку робить прямо в API-тесті — щоб контракт не поповз мовчки після наступного релізу:
import { test, expect } from '@playwright/test';
// осмислений ASCII після Base64-декоду = дані відкриті, ключа не було
function decodesToPlainText(value: string): boolean {
if (!/^[A-Za-z0-9+/_-]+={0,2}$/.test(value)) return false;
const decoded = Buffer.from(value, 'base64').toString('utf8');
return decoded.length > 0 && /^[\x20-\x7E\s]+$/.test(decoded);
}
test('чутливе поле не їде у відповіді у відкритому вигляді', async ({ request }) => {
const res = await request.get('https://api.example.com/v1/profile/8412');
const body = await res.json();
expect(
decodesToPlainText(body.contactRef),
'contactRef розкодовується без ключа — це кодування, а не шифрування',
).toBe(false);
});
Що дивитися і чому:
- — це транспорт, а не захист. Тест вище не намагається зламати шифр: він перевіряє рівно те, що можна перевірити збоку — чи розгортаються дані без жодного ключа. Пройшов — питання до архітектури лишається відкритим; не пройшов — знахідка доведена одним рядком.
- від кодування відрізняє довжина. Хеш видає фіксовану довжину на будь-якому вході, Base64 — довжину, пропорційну вхідним даним. Якщо в базі паролів усі рядки однакової довжини — це хеш, і наступне питання вже про і .
- Однакові рядки на однакових паролях — окрема знахідка. Заведіть двох тестових користувачів з одним і тим самим паролем і порівняйте значення в дампі: збіг означає, що солі немає, і райдужна таблиця розкриє всю базу разом.
crypto.subtleнаhttpне існує. Якщо фронтенд щось «шифрує на клієнті», а стенд піднятий без TLS, код мовчки не працюватиме: Web Crypto доступний лише в захищеному контексті, іtypeof crypto.subtleповерне'undefined'.
Кейс 2. Аудит транспорту перед релізом: читаємо звіт сканера
Задача перед релізом звучить як «перевірити, що HTTPS налаштований правильно». Вручну це не перевіряється — беруть testssl.sh або Server Test і читають вивід по трьох осях: які версії пропонуються, які набори шифрів увімкнені, які відомі вразливості спрацювали. Нижче — скорочений і причесаний вигляд такого звіту: точні формулювання рядків залежать від інструмента та його версії, важить не текст рядка, а вердикт, який з нього випливає.
Testing protocols
SSLv2 not offered (OK)
SSLv3 not offered (OK)
TLS 1 offered (deprecated)
TLS 1.1 offered (deprecated)
TLS 1.2 offered (OK)
TLS 1.3 offered (OK): final
Testing cipher categories
NULL ciphers (no encryption) not offered (OK)
Anonymous NULL ciphers (no authentication) not offered (OK)
Export ciphers not offered (OK)
LOW: 64 Bit + DES, RC[2,4] (w/o export) offered (NOT ok)
Obsoleted CBC ciphers offered
Forward Secrecy strong encryption (AEAD) offered (OK)
Testing vulnerabilities
CRIME, TLS VULNERABLE (NOT ok)
| Рядок звіту | Вердикт | Підстава |
|---|---|---|
TLS 1 offered, TLS 1.1 offered | Знахідка | Узгодження цих версій заборонене RFC 8996: SHA-1 у рукостисканні, атака зниження за 2^77 |
SSLv2 / SSLv3 not offered | Норма | Саме те, що вимагають RFC 6176 і RFC 7568 |
LOW ... RC[2,4] offered | Знахідка | RC4 не пропонують і не узгоджують за будь-якої версії й з будь-якої причини |
Obsoleted CBC ciphers offered | Знахідка для TLS 1.2 | Для 1.2 конфігурують AEAD без CBC, з пріоритетом GCM і CCM |
CRIME VULNERABLE | Знахідка | Стиснення TLS дозволяє відновити чутливі дані, зокрема сесійну cookie |
| Набори без прямої секретності в переліку | Знахідка | TLS_RSA_*, TLS_DH_* і TLS_ECDH_* вимикають поіменно |
Друга половина аудиту стосується не шифрів, а того, чи взагалі можна дістатися до сервісу відкритим каналом. Для API правило суворіше, ніж «поставте редирект»:
import { test, expect } from '@playwright/test';
test('API-ендпоінт по http відмовляє, а не веде редиректом на https', async ({ request }) => {
const res = await request.get('http://api.example.com/orders', { maxRedirects: 0 });
// редирект тут не рішення: запит із токеном уже пройшов відкритим каналом
expect([301, 302, 307, 308]).not.toContain(res.status());
});
Що дивитися і чому:
- Версія — лише третина відповіді. «Увімкнули TLS» не каже нічого ні про набори шифрів, ні про сертифікат. Три питання аудиту: що узгоджується як версія, що узгоджується як набір, що показує сертифікат.
- Дамп про версію бреше. Якщо ви дивитеся не звіт сканера, а байти, памʼятайте: сервер, що узгодив TLS 1.3, зобовʼязаний виставити в
ServerHello.legacy_versionзначення0x0303. Реальну версію шукайте в розширенніsupported_versions. - Регресія приходить під приводом сумісності. Формулювання «нам треба підтримати старий клієнт» повертає слабкі набори тихо й без окремого тікета. Правильний хід — окремий для такого клієнта, без доступу до чутливих даних, а основний не чіпають.
- Редирект з HTTP на HTTPS рятує пізно. Браузерну сторінку він ще виправдовує, API — ні: перший запит із заголовком
Authorizationуже пішов відкритим каналом, і перенаправлення відбувається після витоку, а не замість нього. - Звіт сканера кладіть у тікет цілим блоком. Рядок
offered (NOT ok)— це відтворюваний доказ, з яким не сперечаються, на відміну від переказу «сканер щось знайшов».
Кейс 3. У CI ERR_CERT_AUTHORITY_INVALID, у браузері все зелено
Класика стенду: тестувальник відкриває https://staging.example.com у себе — замок на місці, помилок немає. Та сама адреса в CI валить усю на першому ж goto. Перше, що хочеться зробити, — виставити ignoreHTTPSErrors: true у конфізі. Це і є хибний хід, бо він не лагодить причину, а прибирає перевірку разом із цілим класом майбутніх регресій.
Причину дістають однією командою, а не здогадами:
openssl s_client -connect staging.example.com:443 \
-servername staging.example.com -showcerts </dev/null
depth=0 CN = staging.example.com
verify error:num=20:unable to get local issuer certificate
verify error:num=21:unable to verify the first certificate
...
Verify return code: 21 (unable to verify the first certificate)
Це не «сертифікат зламаний» — це «клієнт не може добудувати шлях до жодного зі своїх якорів довіри». Браузер його добудовує, бо системне сховище на робочій машині містить внутрішній корінь компанії; мінімальний CI цього кореня не має. Розкладка помилки на пʼять причин, кожна зі своїм лагодженням:
| Що не зійшлося | Як виглядає | Що робити |
|---|---|---|
| Підпис / шлях до якоря довіри | ERR_CERT_AUTHORITY_INVALID, openssl num=20 або num=21 | Додати внутрішній корінь у сховище довіри агента |
| Період дії | ERR_CERT_DATE_INVALID, openssl num=10 | Перевипустити сертифікат; заодно перевірити годинник контейнера |
| Відкликання | ERR_CERT_REVOKED | Зʼясувати, чому відкликали; новий сертифікат, а не обхід перевірки |
| Видавець у ланцюзі | ланцюг обривається, проміжний сертифікат не віддається | Долити проміжний сертифікат у конфіг сервера |
| Показане імʼя | ERR_CERT_COMMON_NAME_INVALID | Виправити subjectAltName або адресу, за якою ходить тест |
Правильне лагодження для нашого випадку — не , а корінь у сховищі образу; для Node-частини (request- Playwright, API-тести) той самий корінь підключається змінною NODE_EXTRA_CA_CERTS:
# CI: внутрішній корінь потрапляє і в системне сховище, і в Node
- run: |
cp ./ci/certs/internal-root.crt /usr/local/share/ca-certificates/
update-ca-certificates
- run: npx playwright test
env:
NODE_EXTRA_CA_CERTS: /usr/local/share/ca-certificates/internal-root.crt
Що дивитися і чому:
- Прапорець «ігнорувати помилки» дозволено мати, але не за замовчуванням. Стандарт вимагає, щоб автоматичний клієнт при розбіжності рвав звʼязок і писав у лог; вимикач цієї поведінки — свідомий тимчасовий виняток на конкретний прогін, а не рядок у базовому конфізі, який ніхто ніколи не прибере.
- Різниця «браузер бачить, CI не бачить» — не . Валідність рахується щодо набору якорів довіри конкретного клієнта, тож два різні вердикти на одному URL є нормальною поведінкою двох різних сховищ. Питання не «чому CI глючить», а «якого кореня йому бракує».
- часто буває справжньою причиною. Сертифікат на
*.staging.example.comпокриваєapi.staging.example.comі не покриваєa.b.staging.example.com: підстановка бере рівно один лейбл. Якщо тест переїхав на глибший піддомен, помилка імені виглядатиме як «сертифікат раптом зламався». Common Nameне аргумент. ПеревіряютьsubjectAltName, полеdNSName; сучасний Chrome CN узагалі ігнорує, тож «але в CN правильне імʼя» пояснює рівно нічого.- У тікеті називайте конкретну умову. «Сертифікат невалідний» — це п'ять різних дефектів в одному реченні; «на стенді не віддається проміжний сертифікат, ланцюг обривається на глибині 1» — це задача, яку адміністратор закриє за десять хвилин.
Хешування, шифрування, кодування
- Знаю критерій, який розводить три операції — ключ і оборотність, — і замість суперечок про «нечитабельний рядок» доводжу кодування через
atob()у . - Знаю чотири властивості криптографічного хешу, статус SHA-2, SHA-1 і MD5, те, що голий хеш не заміняє MAC, і що Web Crypto API живе лише в захищеному контексті.
«MD5 зламано» — про що це насправді
- Розрізняю три стійкості й памʼятаю, що в MD5 упали колізії, а не прообраз.
- Знаю межу застосування: контрольна сума проти випадкового псування прийнятна, MD5 у підписі чи сертифікаті — знахідка; заміни називаються HMAC-SHA256 та AES-CMAC.
Зберігання паролів
- Знаю, що пароль хешують спеціалізованою функцією з , а не шифрують оборотно.
- Можу пояснити роль солі, її унікальність на кожен пароль і те, що секретом вона не є.
- Розумію, що таке memory-hard, чим різняться Argon2d, Argon2i та і чому за замовчуванням беруть саме Argon2id.
- Знаю три важелі конфігурації (
m,t,p) і не зводжу в одне число дефолти RFC 9106 та мінімум OWASP. - Знаю, що видно збоку без доступу до коду: однакові хеші на однакових паролях означають відсутність солі, а миттєвий логін при заявленому Argon2id з 2 GiB означає, що щось одне з двох неправда — або конфігурація, або заявлене.
Версії TLS і набори шифрів
- Можу назвати заборонені версії з документом і підставою: TLS 1.0/1.1 (RFC 8996, SHA-1 та 2^77), SSL 3.0 (RFC 7568, доповнення CBC), SSL 2.0 (RFC 6176, незахищене ).
- Памʼятаю дві особливості TLS 1.3: у
ServerHello.legacy_versionстоїть0x0303, а справжня версія живе в розширенніsupported_versions; від зниження боронить маркер в останніх восьми байтах випадкового значення, алертillegal_parameterіTLS_FALLBACK_SCSVз боку конфігурації. - Знаю, що вмикають (: AES-GCM, ChaCha20-Poly1305) і що гасять поіменно: null-шифри, анонімні, експортні,
TLS_RSA_*, статичніTLS_DH_*/TLS_ECDH_*, RC4, усе слабше за 112 бітів і стиснення TLS через CRIME.
Сертифікати
- Можу розкласти помилку сертифіката на пʼять причин: підпис, строк, відкликання, видавець, імʼя — і знаю, що лагодяться вони по-різному.
- Розумію, що «валідний» рахується щодо набору якорів довіри конкретного клієнта, тож розбіжність браузера й CI-агента буває цілком законною.
- Знаю правила (один символ підстановки, крайній лівий лейбл цілком, рівно на один лейбл) і те, що імʼя звіряють у
subjectAltName, а не вCommon Name. - Розумію, чому «ігнорувати помилки сертифіката» — тимчасовий свідомий виняток, а самопідписаний сертифікат на стенді лікується внутрішнім коренем у сховищі довіри агента.
Дані в спокої, дані в русі та ключі
- Розводжу дві задачі: HTTPS закриває лише рух, шифрування диска — лише спокій, і залізне шифрування нічого не дає проти віддаленої компрометації.
- Можу пояснити, чому суто API- має нешифрований HTTP відхиляти, а не перенаправляти: до моменту редиректу запит із токеном уже пролетів відкритим каналом.
- Знаю конвертну схему DEK/KEK і перелік місць, де ключам не місце: код, змінні оточення, відкритий вигляд, сусідство з даними, які вони захищають.
- Памʼятаю, що ключі, вектори ініціалізації, ідентифікатори сесій і токени скидання пароля беруть із , а UUID надійним джерелом випадковості не вважають.
Квіз
Перед стартом
- Питань: 15
- Поріг «зараховано»: ≥70% правильних відповідей.
- Результат впливає на прогрес; завалені питання підуть у чергу повторення.
- Квіз впливає на компліт теми: тема стає «пройдено», лише коли прочитано теорію І квіз складено на ≥70%.
Питання
Що розводить хешування, шифрування й кодування?