Ця “шпаргалка” із запобігання SQL-ін’єкціям надає практичні стратегії для захисту вебзастосунків від однієї з найкритичніших загроз безпеці. Вона пояснює типи атак, демонструє безпечні шаблони написання коду та наголошує на тестуванні та моніторингу для надійного захисту.
Типи SQL-ін’єкцій
SQL-ін’єкції – це широкий клас недоліків безпеки, які дозволяють зловмиснику додавати запити до бази даних або команди у вхідні дані так, що сервер бази даних їх виконує. Існують різні типи SQLi залежно від того, де та як впроваджується SQL-код:
- Звичайна (“in-band”) SQL-ін’єкція: пряме введення шкідливого SQL у вбудовані запити. Це включає як SQL-ін’єкції на основі помилок (“error-based”), так і “union-based”.
- Сліпа (“blind”) SQL-ін’єкція: отримання даних за допомогою boolean логіки або технік на основі часу, коли не відображаються повідомлення про помилки. Зазвичай спирається на спостереження за поведінкою сторінки, вмістом відповідей або затримками часу.
- Out-of-band SQL-ін’єкція: використання таких методів, як DNS- або HTTP-запити, для отримання даних, коли прямі відповіді недоступні.
- SQL-ін’єкція другого порядку (“second order”): впровадження корисного навантаження, яке запускає SQL-ін’єкцію пізніше в робочому процесі програми. Часто пропускається під час тестування, оскільки виконується не одразу.
Найкращі практики запобігання SQL-ін’єкціям
Уникання створення SQL-запитів за допомогою конкатенації рядків
Безпосереднє включення даних користувача в рядок запиту є небезпечним за своєю суттю. Щоразу, коли вони об’єднуються із запитом до бази даних, дані та логіка програми змішуються, залишаючи зловмиснику простір для контролю поведінки застосунку. Небезпечний конкатенований запит у PHP може виглядати так:
$query = "SELECT * FROM users WHERE userid = " . $_GET['userid'];
Якщо зловмисник може впровадити SQL-код як значення параметра userid, програма вразлива до SQL-ін’єкцій.
Використання параметризованих запитів або підготовлених операторів (statements)
Основне правило для запобігання SQL-ін’єкціям – ніколи не вставляти вхідні дані користувача безпосередньо в SQL-код. Незалежно від того, чи працює команда з MySQL, PostgreSQL, Oracle або Microsoft SQL Server, важливо завжди треба використовувати параметризовані запити або підготовлені оператори. Вони відокремлюють введені користувачем дані від синтаксису SQL, гарантуючи, що навіть спеціальні символи не зможуть змінити логіку запиту.
Ось приклад створення підготовленого оператора на Java:
// Слід переконатися, що conn є дійсним відкритим з'єднанням, і правильно обробляє винятки у коді продакшну
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE userid = ?");
stmt.setInt(1, userId); // $userId містить значення, введене користувачем
ResultSet rs = stmt.executeQuery();
Подібний приклад для PHP з використанням PDO:
// Варто застосувати PDO::ATTR_ERRMODE для правильної обробки помилок
$stmt = $pdo->prepare("SELECT * FROM users WHERE userid = :userid");
$stmt->execute(['userid' => $userId]); // $userId містить значення, введене користувачем
Перевірка вхідних даних
Параметризація зупиняє ін’єкцію, але перевірка вхідних даних також важлива для підвищення стійкості, запобігаючи як спробам атаки, так і помилкам введення даних. Важливо використовувати білі списки, щоб обмежити вхідні дані для відомих закритих параметрів. Також варто очищувати вхідні дані, щоб перевірити, чи цілі числа дійсно є цілими числами, чи рядки відповідають очікуваним шаблонам, а неочікувані значення, такі як NULL або спеціальні символи, обробляються коректно. Наприклад, щоб забезпечити цілочисельні значення в PHP, можна використовувати:
if (!filter_var($userid, FILTER_VALIDATE_INT)) {
throw new Exception("Invalid userid");
}
Це усуває ризик потенційно шкідливих значень рядка у параметрі userid. Проте це лише перевіряє формат даних і не замінює параметризовані запити.
Обережність зі збереженими процедурами
Збережені процедури можуть підвищити безпеку, але лише якщо вони використовують параметризовані вхідні дані та уникають динамічного SQL. Якщо застосовуються будь-які динамічні скрипти всередині процедур, це підвищує ризик ін’єкції. Наприклад, в Oracle можна налаштувати процедуру для отримання користувача за ідентифікатором, а потім викликати процедуру замість виконання запиту з коду програми:
CREATE PROCEDURE GetUser(p_userid IN NUMBER) AS
BEGIN
SELECT * FROM users WHERE userid = p_userid;
END;
Безпечна обробка помилок
Варто уникати надання детальних повідомлень про помилки, які розкривають назви таблиць, стовпців або структуру запиту. Слід повертати загальні повідомлення про помилки користувачам та створювати логи деталей на стороні сервера. Як додаткова перевага, стандартизація обробки помилок та повідомлень може запобігти або перешкодити деяким атакам на основі часу.
Моніторинг та створення логів запитів
Моніторинг допомагає виявляти незвичайні патерни запитів, такі як повторювані логічні (boolean) умови або затримки на основі часу, які можуть свідчити про атаки сліпої SQL-ін’єкції. Також варто шукати неочікувані SQL-команди або невдалі спроби автентифікації в логах, що охоплюють як журналювання на рівні бази даних, так і моніторинг на рівні програми. Як більш загальний запобіжний захід, конфіденційні дані ніколи не повинні записуватися в логи.
Оновлення технологічного стека
Важливо регулярно оновлювати не лише компоненти власного додатка, але й об’єктно-реляційної проєкції (ORM), драйвери баз даних, програмне забезпечення СУБД та операційні системи бекенду. Це однаково стосується як платформ з відкритим кодом, так і комерційних. Підтримка актуальності допомагає мінімізувати вразливості.
Принцип найменших привілеїв
Хоча це не запобігатиме безпосередньо атакам, програма повинна підключатися до сервера бази даних за допомогою облікового запису з низькими привілеями та мати доступ лише до таблиць і стовпців, необхідних для її роботи. Це допомагає мінімізувати вплив і обмежити ескалацію, якщо зловмисник десь знайде та використає вразливість SQLi. Аналогічно, сам сервер бази даних повинен працювати лише з мінімально необхідними дозволами та функціями, щоб обмежити можливості зловмисника.
Обережне використання об’єктно-реляційної проєкції (ORM)
Системи об’єктно-реляційної проєкції (Object-relational mapping, ORM) зазвичай використовуються для взаємодії між об’єктноорієнтованими мовами програмування (найчастіше Java) та реляційними базами даних. Хоча сучасні ORM можуть повністю абстрагуватися від мови запитів та зменшити ризик ін’єкцій, все одно потрібно знати функції параметризації ORM та правильно їх використовувати. Якщо повертатися до сирих SQL-запитів, треба гарантувати їхню безпеку, так само як і без ORM взагалі.
Написання безпечного коду та сканування на вразливості
Застосування всіх цих рекомендацій значною мірою допоможе уникнути SQL-ін’єкцій у додатках, але єдиний спосіб переконатися в цьому – це тестування. Їх слід сканувати на наявність вразливостей безпеки, включаючи SQL-ін’єкції, та тестувати на кількох етапах життєвого циклу розробки ПЗ (SDLC).
Інструменти статичного тестування безпеки додатків (SAST) можуть виявляти деякі з найочевидніших ризиків вже на ранніх етапах написання коду, але переконатися, що запущений додаток не має вразливостей, які можна експлуатувати, – це завдання динамічного тестування безпеки додатків (DAST). За допомогою передових інструментів, таких як Invicti (раніше Netsparker), можна виконувати перевірки безпеки у пайплайні та у продакшні, щоб виявити широкий спектр SQL-ін’єкцій, включаючи out-of-band вразливості. А завдяки proof-based скануванню з доказом експлуатації можна запевнитися в існуванні проблеми.
Висновок
Запобігання SQL-ін’єкціям вимагає ряду заходів безпеки, включно з тестуванням, що дозволяє створювати стійкі вебдодатки. Використання рекомендацій у цій “шпаргалці” підвищить безпеку користувачів, систем і даних від однієї з найвпливовіших атак.
Якщо ви хочете безкоштовно протестувати Invicti (DAST) залиште свою контактну інформацію у формі нижче:
Запит на безкоштовне тестування Invicti
Залиште контакти і ми з вами зв’яжемось







