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

    11 · Security для QA

    Інʼєкції: SQL, NoSQL, command

    Зміст

    Поле пошуку, параметр ?id=42, заголовок User-Agent — для застосунку це дані. Для інтерпретатора, якому вони приїдуть уже склеєними в готовий рядок команди, це може виявитися кодом. На цьому розриві тримається вся тема: розробник думає про ввід як про значення, а база, шел чи каталог розбирають отриманий рядок як текст команди — межі між «моїм синтаксисом» і «чужими даними» там ніхто не проводив.

    Для QA це тема, де зелений функціональний тест не доводить нічого: форма працює, замовлення створюється, статус 200 — і той самий параметр дає читати чужу таблицю. Ця глава — канонічний виклад на сайті, інші глави посилаються сюди. Синтаксис SQL і механіка запитів — матеріал розділу про бази даних; тут інʼєкція як і пейлоад як техніка. Інʼєкція у вивід, який виконує браузер, — окрема глава «XSS: міжсайтовий скриптинг».

    Механізм: код і дані в одному рядку

    (injection flaw) означений через рух даних, а не через технологію: він виникає, коли застосунок надсилає недовірені дані інтерпретаторові. Мішені OWASP перелічує одразу — SQL-запити, LDAP-запити, XPath-запити, команди операційної системи, аргументи програм. Це один клас дефекту, форми якого різняться технологією-мішенню, а не механізмом.

    Суть найточніше формулюють на прикладі SQL: команди вставляють у ввід, призначений для даних, щоб вплинути на виконання заздалегідь визначених команд. Метасимвол у полі даних переносить команду в площину керування (control plane), де її не було, — і працює це тому, що SQL реальної межі між нею та площиною даних (data plane) не проводить. Каталог слабкостей описує те саме з боку коду: продукт будує команду із зовні впливаного входу й не нейтралізує спецелементи, здатні змінити задуману команду (-89).

    Склеювання рядка

    Параметризація

    Ввід користувача

    Як будується команда?

    Синтаксис і дані
    в одному рядку

    Інтерпретатор розбирає
    весь рядок як команду

    Частина вводу
    виконується як код

    Спершу текст команди,
    значення окремо

    Інтерпретатор знає:
    це дані, не код

    Ввід лишається значенням

    Склеювання рядка

    Параметризація

    Ввід користувача

    Як будується команда?

    Синтаксис і дані
    в одному рядку

    Інтерпретатор розбирає
    весь рядок як команду

    Частина вводу
    виконується як код

    Спершу текст команди,
    значення окремо

    Інтерпретатор знає:
    це дані, не код

    Ввід лишається значенням

    Одну межу варто провести одразу: інʼєкція коду (code injection) дозволяє додати власний код, який виконає застосунок, а в власного коду не треба — розширює наявну функцію, яка й так виконує системні команди.

    Інʼєкція в мапі ризиків: A05 і межа термінів

    У редакції :2025 інʼєкція (Injection) — категорія A05: вона опустилася з №3 на №5, але лишається однією з найтестованіших — якусь її форму перевіряли у 100% застосунків вибірки. Траєкторія позиції: №1 у 2013 і 2017, №3 станом на 2021, №5 у 2025; цифру завжди називайте з роком. Категорія обʼєднує XSS (висока частота, низький вплив, понад 30 тисяч ) і (низька частота, високий вплив, понад 14 тисяч CVE) — профілі навмисно різні. теж названі: ввід не валідується й не санітизується, непараметризовані виклики йдуть прямо в інтерпретатор, структура запиту й ворожі дані опиняються в одному рядку.

    Тут же межа термінів, на якій легко посковзнутися. «Інʼєкція» — назва категорії ризику A05; «інʼєкційний дефект» — назва механізму класу. Перше відповідає на питання «куди це класифікувати», друге — «що саме зламано»; зводити їх в одне слово не варто. Інʼєкції в LLM у цей список не входять — вони винесені в окремий перелік OWASP для .

    SQL-інʼєкція: класична, сліпа, часова

    SQL-інʼєкція (SQL injection, ) — вставка часткового або повного SQL-запиту через дані, які клієнт передає застосунку. Умов дві, і друга обовʼязкова: непередбачені дані заходять із недовіреного джерела і використовуються для динамічної побудови запиту. Синтаксис програміста змішано з даними користувача в одному рядку, тож без екранування ввід починає читатися як SQL.

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

    За каналом отримання даних розрізняють три класи — inband (тим самим каналом, яким зайшов пейлоад), out-of-band (іншим каналом) і inferential, він же сліпий (передачі даних немає, висновок будується з поведінки бази). Технік експлуатації гайд називає чотири: UNION (обʼєднати два запити, коли дефект у SELECT), булева, на помилках і із ; спільна вимога до всіх — синтаксично коректний запит. Осі дві, і плутати їх не варто: out-of-band — це канал, яким дані повертаються, а не техніка, якою запит ламають.

    Коли вивід мовчить

    (blind SQL injection) ставить базі питання «істина чи хиба» й читає відповідь із реакції застосунку; від звичайної вона відрізняється рівно способом отримання даних. Типовий її контекст — застосунок віддає узагальнені повідомлення про помилку, але вразливий код не полагоджено: загальна сторінка помилки дефекту не усуває, вона ховає індикатор. Різновидів два. Контентний: сторінка для «істини» відрізняється від сторінки для «хиби». Часовий (time-based): база робить паузу на задану кількість часу і лише тоді відповідає — і працює це навіть тоді, коли вміст сторінки не змінюється взагалі.

    БазаЗастосунокТестувальникБазаЗастосунокТестувальникСторінка та сама —вердикт виносить часпараметр із базовим значеннямзапитрядки200 OK, ~120 мспараметр з умовою і паузоюзапит із функцією затримкирядки після паузи200 OK, ~5120 мсБазаЗастосунокТестувальникБазаЗастосунокТестувальникСторінка та сама —вердикт виносить часпараметр із базовим значеннямзапитрядки200 OK, ~120 мспараметр з умовою і паузоюзапит із функцією затримкирядки після паузи200 OK, ~5120 мс

    Дані добуваються посимвольно, окремим запитом на кожну літеру; затримку можна збільшувати, і чекати повної відповіді не обовʼязково. Часові функції є не лише в MySQL — джерело називає pg_sleep() у PostgreSQL і WAIT FOR DELAY у MS SQL. Побічний трофей — : різні функції поточної дати (now(), getdate(), sysdate()) видають конкретну базу. Для QA головне — : вердикт виносить різниця з базовою відповіддю або затримка, а не рядок помилки. Ручний прогін дуже витратний за часом, тому цей клас автоматизують.

    NoSQL-інʼєкція: підсовують оператор, а не значення

    (NoSQL injection) означена як небезпечна побудова обʼєктів запиту або рядків запиту з недовіреного вводу. Ключове слово — обʼєкт: мішень тут не лише текст команди, а й структура, з якої запит збирається. Оцінку шпаргалка OWASP не помʼякшує: інші моделі запиту й інші патерни розгортання створюють більше ризиків, ніж у реляційних базах, — саме більше, а не «інші за складом».

    Вектор, якого в SQL немає, — оператор від клієнта: керовані клієнтом оператори ($where, $regex, $expr) мають бути заборонені, якщо не потрібні строго й не провалідовані, а сирі фрагменти JSON від клієнта не можна виконувати як запит. Єдиний конкретний приклад на сторінці — дозвіл надіслати { "$where": "this.balance > 0" }, і наслідків там два: віддалене виконання коду й важке навантаження на CPU. Другий небезпечний патерн — конкатенація вводу в рядки мови запитів або в shell-команди утиліт бази.

    Захист: обʼєкти запиту драйвера замість побудови рядків, текстовий пошук через безпечні API драйвера з контрольованим вводом, вимкнення функцій, що виконують код на боці сервера. І пряме «не робіть»: покладатися лише на мережеві обмеження для захисту погано написаних запитів не можна. Для QA це означає перевіряти тіло запиту, а не лише поля форми — негативний набір містить і підміну структури JSON; індикатор при цьому може лежати не у відповіді, а в логах, тож підозрілі команди варто моніторити.

    Командна інʼєкція та інші контексти

    Командна інʼєкція (OS command injection) — виконання довільних команд на хостовій операційній системі через вразливий застосунок; з боку дефекту це побудова системної команди із зовні впливаного вводу без нейтралізації спецелементів. Вектори названо поіменно, і поля форми там лише перший пункт: форми, cookie, HTTP-заголовки. Команди виконуються з привілеями вразливого застосунку, а нехтування збільшує шкоду. У Unix команди розділяються крапкою з комою: додали ; і другу команду в кінець значення — вона виконається. Оточення процесу теж частина : system() і exec() користуються оточенням програми, тож відносний шлях до бінарника плюс нечищений $PATH дають вектор навіть без користувацького рядка в самій команді.

    Звідси теза, що знімає найпоширеніше заперечення: кожна командна інʼєкція є водночас (argument injection), а наслідок тягнеться від розкриття інформації до критичного віддаленого виконання коду. «Команду захардкоджено» безпеки саме по собі не доводить.

    Захист згори вниз. Головне — не викликати команди ОС узагалі: вбудовані бібліотечні функції названо дуже хорошою альтернативою. Якщо виклику не уникнути — два шари: параметризація структурним механізмом розділення даних і команди, а поверх — валідація окремо для команди (список дозволених) і окремо для аргументів ( зі списком дозволених символів і межею довжини, без метасимволів і пробілів). Позитивна модель тут не стильова вподоба: законні символи перелічити значно легше за незаконні. Дрібниця з POSIX: перший --, що не є аргументом опції, приймається як роздільник кінця опцій, тож curl -- $url запобігає інʼєкції аргументу. І мовна механіка, яку плутають: у Java Runtime.exec шел не викликає взагалі, тоді як system() у C передає свої аргументи /bin/sh на розбір.

    LDAP і решта мішеней

    (LDAP injection) — той самий механізм у каталозі: інструкції будуються з користувацького вводу й змінюються техніками, схожими на SQL-інʼєкцію; наслідок — права неавторизованим запитам і зміна вмісту дерева. Клас поширений із двох причин, і перша важлива для всієї глави: безпечних параметризованих інтерфейсів LDAP-запиту бракує; друга — на LDAP широко тримається автентифікація. Тому захистів тут два інші: екранувати правильною функцією кодування або взяти фреймворк, який екранує сам. Контекстів вставки два, з різним екрануванням, — фільтр пошуку (префіксна, вона ж польська, нотація) і DN, , чий вичерпний перелік спецсимволів включає провідні та кінцеві пробіли. застосовна, але нормалізація передує валідації. І «у нас bind-автентифікація» вердиктом не є: джерело називає два обходи — анонімне зʼєднання й unauthenticated bind.

    Решта карти коротко: XPath-запити стоять у тому самому переліку мішеней; вразливими бувають і SOAP- та REST-запити, наслідок там — витяг даних або обхід контролю; у скриптових мовах вектор дає eval-подібний виклик; мережеві демони (SMTP, IMAP, FTP) приймають ввід просто в потік обміну.

    Параметризований запит і чотири варіанти OWASP

    (parameterized query) змушує спершу визначити весь код SQL, а кожен параметр передати пізніше. Наслідок сформульовано без помʼякшень: база завжди відрізнятиме код від даних, хоч би що подав користувач, а (prepared statement) не дає змінити намір запиту, навіть якщо у ввід вставили SQL-команди. Механізм — (bind variables): ними базі повідомляють, що входи цього динамічного SQL є «даними», а не можливим кодом. Класичний пейлоад тут нічого не ламає: tom' or '1'='1 шукатиметься як імʼя користувача, що буквально дорівнює всьому цьому рядку.

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

    // node-postgres: текст запиту йде незміненим, значення підставляє сервер
    await client.query('SELECT id, email FROM users WHERE email = $1', [email]);
    
    // А так — ні: рядок склеєно ще до бази
    await client.query(`SELECT id, email FROM users WHERE email = '${email}'`);
    
    // Імʼя таблиці плейсхолдером не передати — беремо його з коду, не з параметра
    const table = ALLOWED_TABLES[name];

    Чотири варіанти й межі кожного

    Шпаргалка запобігання перелічує чотири варіанти, і порядок не декоративний: 1 — підготовлені інструкції з параметризованими запитами; 2 — правильно побудовані ; 3 — валідація за списком дозволеного; 4 — КАТЕГОРИЧНО НЕ РЕКОМЕНДОВАНО: екранування всього користувацького вводу. Сусідня шпаргалка додає: SQL-інʼєкції найкраще запобігати параметризованими запитами — «найкраще», а не «єдиним способом».

    Збережені процедури — місце, де сам OWASP говорить двома голосами. Шпаргалка запобігання: підхід має той самий ефект, що параметризація, доки процедури реалізовані безпечно, тобто без небезпечної динамічної генерації SQL усередині. Сторінка www-community того ж OWASP жорсткіша: процедури запобігають деяким типам атак, але не захищають від багатьох інших, а обмеження обходяться. Гайд тестування додає третій бік: динамічний SQL усередині процедури вимагає санітизації, інакше зловмисний SQL виконається просто в ній. Тож «у нас логіка в збережених процедурах» аргументом не є.

    Валідація за списком дозволеного відповідає за місця, де привʼязати змінну неможливо, і як другорядний захист рекомендована завжди. Але повною вона не буває: спецсимволи багатьом застосункам потрібні законно, а вільні текстові поля вона не закриває. з переліку захистів випадає зовсім — такі списки «поцятковані лазівками».

    Екранування — місце, де два канонічні джерела дають протилежний пріоритет, і глава сторони не обирає. OWASP ставить його останнім із позначкою STRONGLY DISCOURAGED: методологія крихка, і гарантувати, що вона запобіжить усім інʼєкціям у всіх ситуаціях, шпаргалка не може; ручне екранування «може допомогти, але не зробить ваш застосунок захищеним». MITRE у CWE-89 навпаки: правильне , екранування й квотування — «найдієвіше розвʼязання», а валідація дає лише певний ешелонований захист. Предмет у цих формулювань різний: MITRE говорить про механізм (правильне екранування в місці виводу), шпаргалка — про стратегію (ручне екранування всього входу як спосіб будувати застосунок); жодне з джерел цієї відмінності саме не проговорює, тож обидва наводяться з атрибуцією. Як запасний варіант екранування лишається дозволеним і в OWASP — але під конкретний інтерпретатор.

    І дві межі самої ідеї: у LDAP безпечних параметризованих інтерфейсів бракує, а в командах ОС «параметризація» означає структурний механізм розділення, а не привʼязані змінні. Мінімізація привілеїв акаунтів бази зменшує шкоду, а не дефект.

    Тестування: пейлоади, індикатори, автоматизація

    Спершу інвентаризація точок вводу. Потрібен список усіх полів, значення яких можуть потрапити в запит, включно з прихованими полями POST-запитів; заголовки й cookie теж у списку. Мапа ризиків формулює ширше: параметри, заголовки, URL, cookie, JSON, SOAP і XML-входи.

    Перші проби. Найперший тест — одинарна лапка ' або крапка з комою ;: лапка є термінатором рядка в SQL, і якщо її не фільтрують, запит стає некоректним. Далі пробують роздільники коментарів (--, /* */), ключові слова AND і OR, а також рядок там, де очікується число. Канонічний приклад показує суть: завжди істинна умова (OR 1=1) змушує запит повернути значення — і система автентифікує користувача, не знаючи ні логіна, ні пароля; у мапі ризиків це виглядає як підміна параметра id на ' OR '1'='1.

    Індикатори. Дивитися треба не тільки на екран: помилка часто лежить у HTML- або JavaScript-коді відповіді й користувачеві не показується. Просте 500 Server Error чи кастомна сторінка помилки — сигнал переходити до сліпих технік, а не висновок «не вразливе». Дисципліна прогону: одне поле за раз, решта параметрів незмінна. Цілі тесту — знайти точки інʼєкції й оцінити серйозність та рівень доступу, який через них досяжний.

    // Playwright: пейлоад не має змінювати ні код, ні форму відповіді
    const base = await request.get('/api/orders', { params: { id: '42' } });
    const probe = await request.get('/api/orders', { params: { id: "42'" } });
    
    expect(probe.status()).toBe(base.status());
    expect(await probe.text()).not.toMatch(/sql|syntax error|sqlstate/i);

    Автоматика і її сліпа зона. Автоматизований (у безпековій рамці — , Static Application Security Testing) розбирає код, не виконуючи його, і CWE-89 знаходить часто, зменшуючи хибні спрацювання аналізом потоків даних. Але він може не побачити виклики через власні API-функції чи сторонні бібліотеки, що звертаються до SQL опосередковано, — це хибнонегативні результати, тож чистий звіт доказом не є. Добирають динамічними техніками: , тестуванням на стійкість, внесенням несправностей. Асиметрію класу названо прямо: інʼєкційні дефекти легко виявити ревʼю коду й важче — тестуванням, а сканери й фазери допомагають і атакувальнику.

    -сканер: два режими. — окремий додаток, який перевіряє повідомлення (HTTP, WebSocket), пропущені крізь ZAP або надіслані ним; воно жодним чином не змінює відповіді й вважається безпечним. Активне сканування застосовує до цілей відомі атаки, тобто є справжньою атакою: дока попереджає про можливу реальну шкоду функціональності сайту й даним — тільки свій стенд і тільки з дозволом. Прогін у CI, гейти й місце у — тема окремої глави про автоматизацію security-перевірок наприкінці розділу.

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

    • «Помилок ми не показуємо — інʼєкції немає» → виглядає як захист, а насправді узагальнена сторінка помилки лише ховає індикатор: саме такий контекст названо типовим для сліпої інʼєкції.
    • «Спецсимволи фільтруються» → виглядає як закритий дефект, а насправді валідація навіть за списком дозволеного повним захистом не є: спецсимволи багатьом застосункам потрібні законно.
    • «У нас збережені процедури» → виглядає як параметризація, а насправді еквівалентність діє лише без небезпечної динамічної генерації SQL усередині.
    • «Плейсхолдери в коді є» → виглядає як параметризований запит, а насправді частина клієнтських бібліотек склеює рядок ще до відправки: підстановку робить сервер, і перевіряти треба саме там.
    • «Команда захардкоджена, підставляється лише аргумент» → виглядає як безпечний виклик, а насправді кожна командна інʼєкція є водночас інʼєкцією аргументів.
    • «Це NoSQL, тут інʼєкцій не буває» → виглядає як властивість технології, а насправді ризиків там більше, а вектор із оператором від клієнта в SQL узагалі відсутній.
    • «Сканер пройшов чисто» → виглядає як доказ, а насправді статика не бачить викликів через власні обгортки й сторонні бібліотеки, а пасивний прохід проксі нічого до застосунку не досилає — він лише читає наявний трафік.

    Підсумок

    • Інʼєкція — це один механізм, а не десять різних багів. Недовірені дані доходять до інтерпретатора, і те, що задумувалося як значення, потрапляє в площину керування. Змінюється мішень і синтаксис пейлоада — логіка перевірки лишається та сама.
    • Категорія і механізм — різні поняття. «Інʼєкція» — назва категорії A05 у Top 10:2025, де вона стоїть пʼятою й обʼєднує XSS із SQL-інʼєкцією; «інʼєкційний дефект» — назва самого механізму.
    • Справжній захист — структурне розділення даних і команди. Параметризований запит визначає весь код першим і передає значення окремо; збережені процедури, валідація за списком дозволеного й екранування — три інші варіанти OWASP, кожен зі своїм застереженням, а щодо екранування два канонічні джерела дають протилежний пріоритет.
    • Оракулом часто є поведінка, а не текст. Різниця з базовою відповіддю або затримка працюють там, де помилок не видно, тож перевірка будується на порівнянні.
    • Точки вводу ширші за форму, а чистий скан не є доказом. Приховані поля, заголовки, cookie, JSON — усе це вектори; статика має хибнонегативні результати, а активне сканування є справжньою атакою й потребує дозволу.

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

    • «Що таке SQL-інʼєкція і чому вона взагалі можлива?» — перевіряють механізм, а не завчене означення. Сильна відповідь говорить про змішування синтаксису й даних в одному рядку та про відсутність межі між площинами керування й даних.
    • «Чим сліпа інʼєкція відрізняється від звичайної?» — на розуміння оракула. Чекають, що різниця рівно в способі отримання даних і що вердикт виносить різниця відповідей або затримка; плюс бали за пояснення, чому кастомна сторінка помилки дефекту не закриває.
    • «Як ти перевірятимеш поле на інʼєкцію?» — питання про процедуру: інвентаризація точок вводу разом із прихованими полями, заголовками й cookie, перші проби, дисципліна «одне поле за раз», опис індикаторів.
    • «Параметризовані запити чи екранування?» — пастка на однозначну відповідь. Сильний кандидат називає параметризацію основним захистом і знає, що щодо екранування каталог CWE й шпаргалка OWASP розставляють пріоритети по-різному.
    • «У нас MongoDB — інʼєкція нам не загрожує?» — перевіряють, чи привʼязане знання лише до SQL. Правильна відповідь: небезпечно будується сам обʼєкт запиту, а головний вектор — оператор або фрагмент JSON від клієнта.
    • «Чим командна інʼєкція відрізняється від інʼєкції коду?» — на точність меж: власний код тут не потрібен, розширюється наявна функція застосунку, і команда виконується з його привілеями.
    • «Сканер безпеки не знайшов нічого. Реліз?» — питання про межі автоматики: хибнонегативні результати статики й різниця пасивного та активного сканування.

    Джерела

    Механізм: код і дані в одному рядку

    Інʼєкція в мапі ризиків: A05 і межа термінів

    SQL-інʼєкція: класична, сліпа, часова

    NoSQL-інʼєкція: підсовують оператор, а не значення

    • OWASP Cheat Sheet — NoSQL Security — означення через обʼєкти запиту, більші ризики проти реляційних баз, оператори від клієнта, приклад $where, захисти й моніторинг.

    Командна інʼєкція та інші контексти

    Параметризований запит і чотири варіанти OWASP

    Тестування: пейлоади, індикатори, автоматизація

    Пояснення

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

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

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