Зміна SQL-виразу (Injection)

Матеріал представлений на сторінці створений за ресурсом w3schools.com.


Застосування техніки SQL-Injection може привести до втрати даних БД.

Застосування SQL-запитів на веб-сторінках

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

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


Код на стороні серверу


txtUserId = getRequestString("UserId");
txtSQL = "SELECT * FROM Users WHERE UserId = " + txtUserId;

Приклад вище створює пошуковий вираз за допомогою змінної (txtUserId), що добавляється в SQL-запит вибірки даних. Змінна (txtUserId) отримана за допомогою поля вводу (input) HTML-форми на сторінці сайту.

Проте, якщо ввести деякий інший текстовий рядок, то можна виконати потенційну загрозу для існування даних серверу БД.

Зміна SQL-виразу (Injection)

Injection це техніка, що дозволяє зловмиснику внести видозмінений SQL-запит до серверу БД та отримати приховані дані.

SQL-Injection, потенційно небезпечна та може поставити під загрозу роботу додатків.

Injection, 1=1 завжди істина

Давайте розглянемо приклад, що був вище, ще раз.

Давайте припустимо, що первинна мета коду була створити SQL-запит, щоб вибрати користувача з заданим ідентифікатором користувача - id.

Якщо нічого не заважає користувачу ввести "неправильний" запит, то користувач може ввести якийсь "приємний" вираз як:

UserId:

Результат запиту на стороні серверу

SELECT * FROM Users WHERE UserId = 105 or 1=1

SQL-запит має правдиве значення і видасть всі рядки з таблиці Users тому, що WHERE 1=1 завжди буде мати правдиве значення.


- Чи зразок здається небезпечним? Що коли таблиця Users складається з імен та паролей?

Вищеприведений SQL-вираз має на увазі теж саме:

SELECT UserId, Name, Password FROM Users WHERE UserId = 105 or 1=1

Звичайний хакер зможе мати доступ до імен та паролей Бази Даних досить просто, якщо вставить в поле вводу '105 or 1=1'.

Injection, оснований як ""="" завжди істина

Загальна схема верифікації користувача на сайті:

User Name:

Password:

Код на стороні серверу

uName = getRequestString("UserName");
uPass = getRequestString("UserPass");

sql = "SELECT * FROM Users WHERE Name ='" + uName + "' AND Pass ='" + uPass + "'"

Хакер зможе мати доступ до Бази Даних, якщо введе " or " "=" в поле імені та паролю.
Код на стороні серверу буде мати вигляд:

Результат запиту на стороні серверу

SELECT * FROM Users WHERE Name ="" or ""="" AND Pass ="" or ""=""

SQL-вираз правдивий і він виведе всі рядки з таблиці Users, так як WHERE ""="" є правдиве значення.

Injection, що базується на груповому розташуванні SQL-виразів

Більшість Баз Даних підтримує групове розташування SQL-виразів, розділених за допомогою коми.

SELECT * FROM Users; DROP TABLE Suppliers

SQL-вираз вище отримує всі значення даних з таблиці Users та потім видаляє таблицю Suppliers.
Якщо ми будемо мати код, на стороні серверу:

Результат запиту на стороні серверу

txtUserId = getRequestString("UserId");
txtSQL = "SELECT * FROM Users WHERE UserId = " + txtUserId;

І введем слідуючі дані:

User id:

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

SELECT * FROM Users WHERE
UserId = 105; DROP TABLE Suppliers

Захист

Деякі веб-розробники використовують "чорний список" слів чи символів в полі пошуку SQL-запиту, щоб запобігти зміні SQL-виразу, використовуючи техніку Injection

Це не дуже добра ідея. Багато таких слів (як delete чи drop) та символів (коми та значки цитати ), використовуються в загальній мові і мають бути дозволені в полях вводу.

(Фактично було б ідеально використовувати поле вводу для SQL-виразів в базах Даних).

Лише один доречний шлях для безпечного використання веб-сайту,- це використання параметрів SQL.

SQL-параметри це коректно застосовані значення, що додаються в SQL-запит під час виконання запиту.



Зразок PHP INSERT INTO

$stmt = $dbh->prepare("INSERT INTO Customers (CustomerName,Address,City)
VALUES (:nam, :add, :cit)");
$stmt->bindParam(':nam', $txtNam);
$stmt->bindParam(':add', $txtAdd);
$stmt->bindParam(':cit', $txtCit);
$stmt->execute();