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

Уявімо пошук товарів. Програма хоче виконати SELECT * FROM products WHERE name = ?. Якщо значення передається як параметр, база отримує окремо шаблон запиту й окремо значення. Якщо ж розробник склеює рядок вручну, введення може змінити структуру WHERE, додати іншу умову або завершити команду.
Користувач контролює значення
Це може бути не лише form field: query string, JSON, cookie, заголовок, імпорт CSV, webhook, параметр сортування або значення з черги. Якщо джерело можна змінити ззовні, вважайте його untrusted.
Значення склеюють із SQL
Проблема з'являється в query = '... ' + input + ' ...', template string, raw query builder або ORM-методі, який приймає готовий SQL. Валідація формату сама по собі не замінює параметризацію.
DB engine бачить не дані, а синтаксис
Спеціальні символи та фрагменти SQL можуть змінити умову, об'єднати результати, вплинути на час відповіді або перетворити read-операцію на write-операцію — залежно від драйвера й прав.
Помилка виходить за межі одного endpoint
Якщо один connection user має надмірні права, компрометація endpoint може зачепити таблиці, звіти, персональні дані, резервні копії або інші tenant-и.
Ментальна модель
Питайте не «чи є тут ORM?», а «чи може зовнішнє значення вплинути на структуру SQL і з якими правами виконується цей запит?».
Цифри нижче потрібно читати чесно. OWASP рахує широку категорію Injection, куди входять SQLi, XSS та інші CWE, а MITRE оцінює слабкість CWE-89 за даними CVE. Це не відсоток усіх атак у світі й не порівняння мов між собою.
274 228
загальних occurrences для категорії Injection у наборі даних OWASP; середня incidence rate — 3,37%. [2]
№2
SQL Injection посіла друге місце у MITRE CWE Top 25 за 2025 рік, score — 28,72. [3]
94%
застосунків у дослідженні OWASP тестувалися на певну форму injection. [2]
Що це означає для команди
SQLi — зріла, добре описана проблема, але її зрілість не робить legacy-код безпечним. Висока відтворюваність — перевага для захисту: патерни легко перевіряти статичним аналізом і тестами.
Почніть із data-flow, а не зі списку фреймворків: знайдіть джерело зовнішнього значення, шлях до data-access layer і фактичний виклик драйвера.
Пошук небезпечних sink-ів
Знайдіть query, execute, raw, text, literal, sequelize.query, knex.raw, jdbc.Statement, SqlCommand та еквіваленти. Перевірте, чи отримують вони склеєний рядок.
Перевірка dynamic SQL
Особливо уважно перегляньте ORDER BY, назви колонок/таблиць, report builder і фільтри. Для них часто не можна використати bind variable — застосуйте allow-list mapping із фіксованих значень. [1]
ORM escape hatches
Перегляньте raw SQL, unsafe operators, custom scopes, string-based filters і міграції. ORM знижує ризик лише там, де команда не обходить його параметризований API.
Не забудьте неочевидні entry points
Адмінка, імпорт, cron, інтеграції, GraphQL resolvers, search endpoints і внутрішні API теж приймають дані, які можуть бути скомпрометовані раніше в ланцюжку.
Перевірте error handling
SQL stack trace, назва таблиці, тип драйвера або різні помилки для різних умов полегшують діагностику атаки. Зовні повертайте безпечну помилку, деталі залишайте в correlation-id логах.
Немає чесного рейтингу «найнебезпечнішої мови»: SQLi можлива майже всюди, де є SQL і небезпечний API. Різниця — у тому, наскільки легко випадково перейти з параметризованого шляху на raw SQL.
| Екосистема | Типова точка ризику | Безпечний default | Що перевірити |
|---|---|---|---|
| PHP: Laravel / Symfony | DB::raw, query string, старий mysqli/PDO-код | Query Builder/Eloquent bindings, PDO prepared statements | raw(), bind parameters, legacy controllers |
| JavaScript / TypeScript: Node.js | pg/mysql2 query з template string, knex.raw, Sequelize raw | driver placeholders, Prisma parameters, query builder | $queryRawUnsafe, raw(), dynamic filters |
| Python: Django / SQLAlchemy | cursor.execute з конкатенацією, text() із рядком | Django QuerySet, SQLAlchemy bound parameters | extra(), raw(), literal SQL, f-strings |
| Java: Spring / Hibernate | Statement, String concatenation, HQL з input | PreparedStatement, JPA parameters, named parameters | createNativeQuery, HQL interpolation |
| .NET: ASP.NET / EF Core | FromSqlRaw або SqlCommand із рядком | LINQ, FromSqlInterpolated, параметри | Raw methods, interpolated SQL, DB role |
| Go / Rust | database/sql або client API з побудовою рядка | QueryContext/Execute з placeholders, sqlx bind | fmt.Sprintf, format!, dynamic identifiers |
Висновок по стеку
Найбільший ризик мають не мови, а команди, які змішують user input із query text, дозволяють raw SQL без review або дають застосунку роль DBA.
Небезпечно: рядок знає забагато
const sql = `SELECT * FROM users WHERE email = '${email}'`;
const result = await db.query(sql);Тут email стає частиною SQL-тексту. Навіть якщо зараз це «лише пошук», той самий патерн легко копіюють у login, export або admin-функцію.
Безпечніше: драйвер відділяє значення
const result = await db.query(
"SELECT * FROM users WHERE email = $1",
[email],
);Текст запиту фіксований, а email переданий як параметр. Формат placeholder відрізняється між драйверами, тому використовуйте документацію саме свого client library.
Для sort field — не bind, а mapping
const columns = { newest: "created_at", price: "price" } as const;
const column = columns[input] ?? columns.newest;
const sql = `SELECT * FROM products ORDER BY ${column}`;Назва колонки не є звичайним значенням для bind parameter. Дозволяйте тільки ключі з allow-list і ніколи не підставляйте raw input напряму.
Жоден інструмент не бачить усю картину. Найкращий результат дає комбінація аналізу коду, тесту endpoint-ів і спостереження за runtime.
CodeQL
SAST для пошуку data-flow від user input до SQL sink у JavaScript/TypeScript, Python, Java, C# та інших підтриманих мовах. Додайте в CI й переглядайте findings разом із кодовласником.
Semgrep
Швидкі pattern/data-flow правила для локальної перевірки й pre-commit. Добре підходить, щоб заборонити конкретні небезпечні виклики на кшталт raw query без параметрів.
Burp Suite / OWASP ZAP
DAST для staging: проксі, повторення запитів, активне й пасивне сканування. Запускайте лише в межах дозволеного scope і не вмикайте руйнівні checks на production.
PortSwigger Web Security Academy
Безпечне навчальне середовище з SQLi labs: error-based, blind, UNION та інші сценарії. Корисно для команди, щоб побачити атаку руками без ризику для клієнтських даних.
Sentry / DB audit logs / WAF
Сигнали експлуатації: сплески 4xx/5xx, незвичні latency, помилки парсингу SQL, масові SELECT, дивні user-agent і доступ до нетипових таблиць. WAF — шар зниження ризику, не patch коду.
OWASP радить не покладатися на один фільтр. Зберіть defense-in-depth baseline і зафіксуйте його в Definition of Done.
Параметризовані запити або безпечні stored procedures; dynamic SQL — лише з allow-list і окремим review. [1]
Не використовуйте escaping як основний захист: OWASP прямо вважає його крихким і радить лише як крайній варіант для legacy. [1]
Окремий DB-користувач для застосунку, мінімальні SELECT/INSERT/UPDATE права, без DBA/owner ролі; для read-only flow — read-only account або view. [1]
Секрети й production data не мають потрапляти в логи. Логуйте подію, endpoint, actor, request-id і policy result, але не повний SQL із персональними параметрами.
Додайте negative tests: quote у звичайному імені, довгий рядок, unicode, неправильний тип, невідомий sort key, cross-tenant ID і boundary values.
Тримайте driver, ORM і database engine оновленими, але не вважайте patch dependency заміною secure query construction.
0–1 година
Зупинити розширення шкоди
Зафіксуйте endpoint, час, request-id і scope. За потреби тимчасово вимкніть функцію, обмежте route або поверніть DB role до read-only. Не видаляйте логи й не запускайте експерименти на production.
Того ж дня
Закрити root cause
Замініть string-built SQL на parameterized API, додайте allow-list для identifiers, перевірте права DB user і створіть regression test, який падає на старій реалізації.
Після patch
Перевірити blast radius
Проаналізуйте DB audit logs, незвичні exports, зміни даних, service accounts і доступ до інших tenant-ів. Зафіксуйте, які дані могли бути прочитані або змінені.
До закриття інциденту
Покращити процес
Додайте SAST/DAST у CI, правило review для raw queries, least-privilege migration і короткий playbook для команди. Інакше наступна SQLi з'явиться в іншому endpoint.
ORM зазвичай параметризує стандартні запити, але не захищає автоматично raw SQL, небезпечні оператори, dynamic identifiers або кастомний query builder. Перевіряйте escape hatches і правила команди.
Це корисна валідація формату, але не універсальний захист. Основним захистом для значень мають бути параметризовані запити; для імен таблиць, колонок і сортування — фіксований allow-list mapping.
Так. GraphQL змінює форму API, але resolver усе одно може передавати аргумент у небезпечний SQL, filter або raw query. Тестуйте data-flow від resolver arguments до DB client.
WAF може блокувати відомі патерни й знизити ймовірність експлуатації, але не виправляє query construction і не гарантує покриття обфускацій та business-specific шляхів. Patch, least privilege і тести все одно потрібні.
Використовуйте локальний sandbox, OWASP Juice Shop, WebGoat або PortSwigger Academy. Не тестуйте сторонній сайт, API клієнта чи production без чітко погодженого scope і правил тесту.
PAS7 Studio може провести code review і security audit для API, адмін-панелі та data-access layer: знайти raw SQL, перевірити tenant boundaries, налаштувати SAST/DAST у CI, зменшити DB-права й підготувати план виправлень із пріоритетами.
Пов'язані статті
Скільки коштує розробка AI асистента у 2026: RAG чатбот, база знань, CRM, Telegram та підтримка
Практичний гід для бізнесу: від чого залежить ціна розробки AI асистента у 2026 році, що входить у RAG чатбот, інтеграції з CRM, Telegram, guardrails, оцінювання, моніторинг і супровід.
AI-assisted attacks і prompt injection у 2026: нова поверхня атак для продуктів з AI
Практичний гайд з безпеки AI-функцій: prompt injection, tool abuse, excessive agency, RAG poisoning, data leakage, output handling, human approval gates і permissions для AI agents.
AI для розробки лендінгів: де він реально прискорює запуск, а де псує конверсію
Дослідження про використання AI у розробці лендінгів: v0, Webflow AI, Builder.io, Framer-подібні AI builders, генерація UX, copy, SEO, персоналізація, A/B тести, ризики шаблонності, безпеки, доступності та технічного боргу.
AI SEO / GEO у 2026: ваші наступні клієнти — не люди, а агенти
Пошук зміщується від кліків до відповідей. Боти та AI-агенти сканують, цитують, рекомендують і дедалі частіше купують. Дізнайтесь, що таке AI SEO / GEO, чому класичного SEO вже недостатньо, і як PAS7 Studio допомагає брендам перемагати у «агентному» вебі.
Професійна розробка для вашого бізнесу
Створюємо сучасні веб-рішення та боти для бізнесу. Дізнайтеся, як ми можемо допомогти вам досягти цілей.