Як хакнути форму? Sql ін'єкції. Динамічна робота із даними. Аналіз запитів перед додатком

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

Сьогодні ми розберемо що таке SQL ін'єкція, як вона працює, а також як з нею боротися, як на рівні мови програмування, так і за допомогою створення різних перешкод для зловмисника за допомогою веб-сервера. Це також досить ефективно.

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

І тут з'являється можливість SQL ін'єкції. Якщо користувач введе в поле певний запит, він зможе створити свій запит до бази даних. Це дозволить йому робити практично все, що завгодно, вкрасти ваші дані, стерти базу, отримати доступ до паролів користувачів, додати нових користувачів і все, що йому завгодно.

Наприклад, ось так виглядає SQL запит запиту id статті під час пошуку:

SELECT id,title,content FROM posts WHERE title LIKE "%запит_користувача%"

А тепер уявіть, що користувач замість ключових слів із статті запровадить таку комбінацію:

1%"; DROP TABLE posts LIKE "%;

І в результаті вийде такий собі повністю робочий запит, на який ви зовсім не очікували:

SELECT id,title,content FROM posts WHERE title LIKE "% 1%"; DROP TABLE posts LIKE "%%"

Це спрацює з будь-яким запитом, у якому є введення даних користувачів, якщо програміст не дбав про безпеку. Отже, щоб вирішити цю проблему, потрібно просто екранувати всі лапки в запитах від користувача. А тепер перейдемо до засобів захисту.

Захист сайту від SQL ін'єкцій на рівні PHP

Захист від SQL атак може виконуватися різними способами. Перше, на що варто звернути увагу і дуже важливо - це щоб програміст вже під час написання коду займався екрануванням лапок за допомогою таких функцій, як mysql_real_escape_string або mysqli_real_escape_string. Якщо кожна змінна, яка використовується в запитах до бази, буде профільтрована ними, програмістом або на рівні CMS, то жодних проблем не виникне.

Але чому протягом останніх 14 років все ще трапляються атаки на SQL? Все просто. Програмісти ліниві, а робити небезпечні запити до бази так просто, тоді як безпечні - складніші. Принаймні складніше, ніж небезпечні.

Захист на рівні веб-сервера

Не завжди є можливість виправити всі недоробки коду. Наприклад, популярний двигун Drupal має більше 20 000 рядків коду, WordPress – 60 000, а Joomla – 180 000. Було б недоцільно все це переписувати. Але можна вчинити по-іншому. Спочатку ми відфільтруємо всі значення зі змінної REQUEST на самому початку скрипту. Вставте цей код після підключення бази даних:

if (!function_exists("clean")) (
if (get_magic_quotes_gpc()) (
function magicquotes_Stripslashes(&$value, $key) (
$value = stripslashes($value);
}
$gpc = array(&$_COOKIE, &$_REQUEST);
array_walk_recursive($gpc, "magicquotes_Stripslashes");
}
function clean(&$value, $key) (
//Ця функція екранує всі лапки.
$value = mysql_real_escape_string($value);
}
}
$req = array(&$_REQUEST);
array_walk_recursive($req, "clean");

Для PHP 7 потрібно буде використовувати функцію mysqli_real_escape_string, оскільки розширення mysql було видалено з цієї версії мови. Для екранування лапок використовується лише функція clean і все, що нижче за неї. Те, що вище застосовується для сумісності з версіями PHP нижче 5.4. У них було налаштування Magic Quotes, яке при включенні екранувало всі лапки. Щоб наш скрипт все не псував ми спочатку все видаляємо екранування якщо вона увімкнена.

Тепер у вас є додатковий захист на рівні PHP. Залишилося ще подбати про захист на рівні веб-сервера. Якщо ви використовуєте Nginx, то можна додати такі правила до вашої секції server:

set $block_sql_injections 0;
if ($query_string ~ "union.*select.*\(") (
set $block_sql_injections 1;
}
if ($query_string ~ "union.*all.*select.*") (
set $block_sql_injections 1;
}
if ($query_string ~ "concat.*\(") (
set $block_sql_injections 1;
}
if ($block_sql_injections = 1) (
return 403;
}

Тут ми фільтруємо всі запити, які містять слова select, concat разом із лапками. Це явна ознака, що користувач намагається виконати ін'єкцію SQL, а значить його запит потрібно заблокувати.

Також ви можете блокувати підозрілі адреси на рівні веб-сервера Apache, наприклад, вибрати найуживаніші ключові слова SQL. Щоправда, це небезпечно, оскільки можуть бути заблоковані запити звичайних користувачів. Додайте до вашої секції VitualHost такі рядки:

RewriteCond %(QUERY_STRING) [^a-z](declare|char|set|cast|convert|delete|drop|exec|insert|meta|script|select|truncate|update)[^a-z]
RewriteRule (.*) - [F]

Але це ще не повне рішення, тут можна йти далі. Це блокування не захищає від SQL ін'єкцій, які виконуються за допомогою POST або RESTful запитів. Також можна активувати модуль mod_security:

sudo a2enmod mod_security

Тут також є кілька правил, які захищають від ін'єкцій. Але бажано використати ще й комплексніший підхід.

Поділ бази даних

Щоб зробити вашу базу даних безпечнішою, ви можете розділити її на кілька частин. В галузі інформаційної безпеки існує таке поняття, як принцип мінімальних привілеїв. Суть принципу полягає в тому, що програма або користувач повинні мати доступ тільки до того, що їм потрібно і більше. Наприклад, буде розумно зберігати дані про кредитні картки користувача та форуми у різних базах даних. Особливо якщо форми використовують застарілу версію phpBB. Це своєрідний додатковий захист sql injection.

Аналіз запитів перед додатком

Ще один варіант – це використання складніших систем захисту. Це може бути апаратне рішення, яке працює поверх iptables або ipfw або система виявлення вторгнень на сервері HIDS, така, як OSSEC. Але таке рішення набагато складніше, ніж потрібне і не призначене для вирішення нашого завдання. Можна використовувати спеціальні брандмауери веб-застосунків, за допомогою яких, зокрема, виконується захист від SQL ін'єкцій. Це такі вільні рішення, як ModSecurity або IronBee.

Висновки

Немає ідеального рішення або чарівної палички, за допомогою якої виходив би стовідсотковий захист сайту від sql ін'єкцій, хоча PHP прагне бути все більш захищеним. Починаючи з версії 7.0 було видалено підтримку розширення MySQL. Тепер потрібно переходити на MySqli або PDO. І це добре, тому що ці розширення роблять простіше використання даних із підготовленими операторами. Хоча для цього все ще потрібно написати кілька рядків.

Існує безліч способів виконання SQL атак, але до тих пір, поки розробники не будуть писати правильний код, а на веб-серверах не максимально використовуватимуться засоби захисту, ці атаки не зникнуть зі списку ТОП 10 OSWAP. Налаштуйте вашу систему так, щоб захистити свої дані та бази даних.

Схожі записи:


SQL ін'єкція- це один із найдоступніших способів злому сайту.
Суть таких ін'єкцій – впровадження даних (передані через GET, POST запити чи значення Cookie) довільного SQL коду. Якщо сайт вразливий і виконує такі ін'єкції, то, по суті, є можливість творити з БД (найчастіше це MySQL) що завгодно.

Як визначити вразливість, що дозволяє впроваджувати SQL ін'єкції?

Досить легко. Наприклад, є тестовий сайт test.ru. На сайті виводиться список новин з можливістю детального просомтру. Адреса сторінки з детальним описом новини має такий вигляд: test.ru/?detail=1 . Тобто через GET запит змінна detail передає значення 1 (що є ідентифікатором запису в таблиці новин).

Змінюємо GET запит на ?detail=1" або ?detail=1" . Далі пробуємо передавати ці запити серверу, тобто заходимо на test.ru/?detail=1" або на test.ru/?detail=1".

Якщо при заході на дані сторінки з'являється помилка, сайт вразливий на SQL ін'єкції.

Приклад помилки, що виникає під час перевірки вразливості

Можливі SQL ін'єкції (SQL застосування)
1) Найбільш прості - згортання умови WHERE до істинного результату за будь-яких значень параметрів.
2) Приєднання до запиту результатів іншого запиту. Робиться це через оператор UNION.
3) Закоментування частини запиту.

практика. Варіанти злому сайту із вразливістю на SQL впровадження

Отже, у нас є вже згадуваний сайт test.ru. У базі зберігається 4 новини, 3 із яких виводяться. Дозвіл на публікацію новин залежить від парметра public (якщо параметр містить значення 1, то новина публікується).

Список новин, дозволених до публікації

При зверненні до сторінки test.ru/?detail=4 , яка має виводити четверту новину, з'являється помилка – новина не знайдена.
У нашому випадку новина існує, але її заборонено до публікації.

Але оскільки ми вже перевіряли сайт на вразливість і він видавав помилку БД, то пробуємо перебирати можливі варіанти запитів.
В адресному рядку плюс (+) виконує роль пробілу, так що не лякайтеся

Тестую наступні варіанти:
test.ru/?detail=4+OR+1
test.ru/?detail=4+--
test.ru/?detail=4+UNION+SELECT+ *+FROM+news+WHERE+id=4

У результаті успіх посміхнувся і два запити (перший і третій) повернули нам детальний опис четвертої новини

Розбір прикладу зсередини

За отримання детального опису новини відповідає блок коду:
$detail_id=$_GET["detail"];
$zapros="SELECT * FROM `$table_news` WHERE `public`="1" AND `id`=$detail_id ORDER BY `position` DESC";

Мало того, що $detail_id отримує значення без будь-якої обробки, так ще й конструкція `id`=$detail_id написана криво, краще дотримуватися `id`="$detail_id" (тобто порівнюване значення писати в прямих апострофах).

Дивлячись на запит, який отримується при зверненні до сторінки через test.ru/?detail=4+OR+1

SELECT * FROM `news` WHERE `public`="1" AND `id`=4 OR 1 ORDER BY `position` DESC

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

Розбираємо запит, сформований під час звернення через test.ru/?detail=4+UNION+SELECT+*+FROM+news+WHERE+id=4 .

Тут назва таблиці з новинами (у разі це news) бралося логічним перебором.
Отже, виконався запит SELECT * FROM `news` WHERE `public`="1" AND `id`=4 UNION SELECT * FROM news WHERE id=4 ORDER BY `position` DESC . До нульового результату першої частини запиту (до UNION) приєднався результат другої частини (після UNION), яка повернула детальний опис 4-ої новини.

Захист від SQL ін'єкцій (SQL впроваджень)

Захист від злому зводиться до базового правила «довіряй, але перевіряй». Перевіряти потрібно все – числа, рядки, дати, дані у спеціальних форматах.
Числа
Для перевірки змінної на числове значення використовується функція is_numeric(n);, яка поверне true, якщо параметр n - число, і false інакше.
Також можна не перевіряти значення на число, а вручну перевизначити тип. Ось приклад, що перевизначає значення $id, отримане від $_GET["id_news"] значення целочисленного типу (в ціле число):
$id=(int)$_GET["id_news"];
Рядки
Більшість зломів через SQL відбуваються через перебування у рядках «незнешкоджених» лапок, апострофів та інших спеціальних символів. Для такого знешкодження потрібно використовувати функцію addslashes($str);, яка повертає рядок $str з доданим зворотним слешем (\) перед кожним спеціальним символом. Цей процес називається екранізацією.

$a="приклад тексту з апострофом";
echo addslashes($a); //буде виведено: приклад тексту з апострофом \"

Крім цього існують дві функції, створені саме для екранізації рядків, що використовуються у виразах SQL.
Це mysql_escape_string($str); і mysql_real_escape_string($str);

Перша не враховує кодування з'єднання з БД і може бути обійдена, а друга її враховує і абсолютно безпечна. mysql_real_escape_string($str); повертає рядок $str з доданим зворотним слешем до наступних символів: \x00, \n, \r, \, ", " і \x1a .

Магічні лапки

Магічні лапки – ефект автоматичної заміни лапки на зворотний слеш (\) та лапку при операціях введення/виведення. У деяких конфігураціях PHP цей параметр увімкнено, а в деяких немає. Для того, щоб уникнути подвійного екранізування символів і заекранізувати дані по-нормальному через mysql_real_escape_string($str); необхідно прибрати автоматичні проставлені зворотні сліші (якщо магічні лапки включені).

Перевірка включеності магічних лапок для даних, що отримуються з GET, POST або Куков, організується через функцію get_magic_quotes_gpc(); (повертає 1 – якщо магічні лапки включені, 0 – якщо відключені).

Якщо магічні лапки вкючені (тобто зворотні сліші додаються) і таке зустрічається частіше, їх потрібно прибрати. Це робиться через функцію stripslashes($str); (повертає рядок $str без зворотних слішів у лапок і прямих апострофів).

У висновку наводжу код з повною екранізацією рядків для запису в БД

If(get_magic_quotes_gpc()==1)
{
$element_title=stripslashes(trim($_POST["element_title"]));
$element_text=stripslashes(trim($_POST["element_text"]));
$element_date=stripslashes(trim($_POST["element_date"]));
}
else
{
$element_title=trim($_POST["element_title"]);
$element_text=trim($_POST["element_text"]);
$element_date=trim($_POST["element_date"]);
}

$element_title=mysql_real_escape_string($element_title);
$element_text=mysql_real_escape_string($element_text);
$element_date=mysql_real_escape_string($element_date);

Стаття була підготовлена ​​на основі практичних навичок захисту веб-систем. Теорія справа хороша, але практика важливіша і головне вона працює.

Пропонуємо до вашої уваги новий курс від команди The Codeby- "Тестування Веб-Додатків на проникнення з нуля". Загальна теорія, підготовка робочого оточення, пасивний фазинг та фінгерпринт, Активний фазинг, Вразливості, Пост-експлуатація, Інструментальні засоби, Social Engeneering та багато іншого.


Суть SQL-ін'єкцій

Напевно, вже чули жарт із Інтернету: « Чому у всіх уроках малювання одне й те саме: Наприклад, урок з малювання сови. Спочатку півгодини довго в деталях малюємо око сови. А потім — раз — за п'ять хвилин — малюємо частину сови, що залишилася.».

Ось навіть картинка з цього приводу є:

За SQL-інжектами матеріалу море: статті, книги, відеокурси (платні та безкоштовні). При цьому не багато хто з них додає розуміння з цього питання. Особливо якщо ви новачок. Я добре пам'ятаю свої відчуття: ось він гурток, ось він залишок сови.

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

Для дослідів, у нас буде дуже простий і вразливий до SQL-ін'єкції скрипт:

Для доступу до Бобруйскої районної бібліотеки введіть Ваші облікові дані:

Введіть ваше ім'я

Введіть Ваш пароль


query("SET NAMES UTF8"); $mysqli->query("SET CHARACTER SET UTF8"); $mysqli->query("SET character_set_client = UTF8"); $mysqli->query("SET character_set_connection = UTF8"); $mysqli->query("SET character_set_results = UTF8"); ) $name = filter_input(INPUT_GET, "name"); $password = filter_input(INPUT_GET, "password"); if ($result = $mysqli->query("SELECT * FROM `members` WHERE name = "$name" AND password = $password")) ( while ($obj = $result->fetch_object()) ( echo "

Ваше ім'я:$obj->name

Ваш статус:$obj->status

Доступні для Вас книги:$obj->books


"; ) ) else ( printf("Помилка: %sn", $mysqli->error); ) $mysqli->close(); ?>

Ви набагато більше зрозумієте, якщо все робитимете разом зі мною. Тому ось. У ньому два файли: index.phpі db_library.sql. Файл index.php розмістіть у будь-яке місце на сервері – це і є наш вразливий скрипт. А файл db_library.sql потрібно імпортувати, наприклад, за допомогою phpMyAdmin.

У файлі index.php як ім'я користувача бази даних заданий root, а пароль - порожній. Ви можете вписати свої дані, відредагувавши рядок:

$mysqli = new mysqli("localhost", "root", "", "db_library");

За легендою, це форма входу до он-лайн версії Бобруйскої районної бібліотеки. Нам уже дали облікові дані: ім'я користувача - Demo, пароль - 111.

Давайте введемо їх і подивимося:

Наші облікові дані прийняті, на екрани виведено наше ім'я, статус та доступні для нас книги. Ви можете спробувати, з будь-якими іншими даними (якщо змінити ім'я або пароль) ми не зможемо увійти і подивитися доступні для читання книги. Також ми не можемо дізнатися, які книги доступні для інших, оскільки ми не знаємо їхнього імені та пароля.

Підглянемо у вихідний код, щоб зрозуміти, як стався запит до бази даних:

SELECT * FROM `members` WHERE name = "$name" AND password = "$password"

Слово SELECT SQL-запит показує, які дані потрібно отримати. Наприклад, можна було б вказати SELECT name або SELECT name, password. Тоді в першому випадку з таблиці було б отримано тільки ім'я, а в другому - тільки ім'я і пароль. Зірочка каже, що потрібно отримати всі значення. Тобто. SELECT * - це означає отримати всі значення.

FROMкаже, звідки їх потрібно отримати. Після FROM слідує ім'я таблиці, тобто запис FROM `members` каже, отримати з таблиці `members`.

Далі WHEREЯкщо ви вивчали будь-які мови програмування, то це слово найбільше нагадує «Якщо». А далі йдуть умови, ці умови можуть бути дійсними (1) або хибними (0). У нашому випадку

(name = '$name') AND (password = '$password')

означає, що умова буде істинною, якщо передана змінна $name дорівнюватиме значення поля name в таблиці і передана змінна '$password дорівнюватиме значення поля password в таблиці. Якщо хоча б одна умова не виконується (невірне ім'я користувача або пароль), то з таблиці нічого не буде взято., тобто вираз SELECT * FROM `members` : у таблиці `members` взяти значення всіх полів, якщо для них виконується умова - збігаються передане ім'я користувача та пароль з тими, що зустрічаються в таблиці.

Це зрозуміло. Давайте тепер, наприклад, з ім'ям користувача підставимо одиночну лапку:

Адресний рядок:

http://localhost/test/mysql-inj-lab1/index.php?name=Demo’&password=111

Жодних даних не отримано, замість них ми бачимо помилку:

Помилка: Ви помиляєтеся в SQL syntax; check the manual that corresponds to your MySQL server version for right syntax to used near "111"" at line 1

При введенні вірних даних наш запит виглядав так:

SELECT * FROM `members` WHERE name = "Demo" AND password = "111"

При додаванні лапки наш запит перетворюється на наступне:

SELECT * FROM `members` WHERE name="Demo" "AND password="111"

Я поставив додаткові прогалини для наочності, тобто у нас виходить запит

до речі, запит вірний із синтаксису. І відразу після нього, без будь-яких роздільників йде продовження запиту:

"AND password ="111"

Воно-то все і ламає, оскільки кількість лапок, що відкривають і закривають, не дорівнює. Можна, наприклад, підставити ще одну лапку:

SELECT * FROM `members` WHERE name = "Demo" " " AND password = "111"

Адресний рядок:

http://localhost/test/mysql-inj-lab1/index.php?name=Demo»&password=111

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

Відповідь є – це коментарі.

Коментарі в MySQL можна задати трьома способами:

# (решітка - працює до кінця рядка)

(Два тире - працюють до кінця рядка, потрібен символ пробілу після двох тире)

/* це коментар */ група з чотирьох символів – все, що всередині – це коментар, все, що до або після цієї групи символів, не вважається коментарем.

Давайте в наш запит з однією лапкою, після цієї лапки поставимо знак коментаря, щоб відкинути хвостик, і знак +, який позначає пробіл, щоб запит вийшов таким:

SELECT * FROM `members` WHERE name = "Demo" --+ " AND password = "111"

Адресний рядок:

http://localhost/test/mysql-inj-lab1/index.php?name=Demo’—+&password=111

Помилка не тільки зникла, а й виведено коректні дані для користувача Demo. Оскільки тепер наш запит набув вигляду

SELECT * FROM `members` WHERE name = "Demo"

адже хвостик -+ 'AND password ='111'перетворився на коментар і більше на запит не впливає.

Подивіться ще раз уважно на новий запит:

SELECT * FROM `members` WHERE name = "Demo"

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

На жаль, я не знаю жодного легітимного імені, і мені потрібно придумати щось інше.

Подивимося уважно на цю частину запиту:

WHERE name = "Demo"

Пам'ятаєте про AND, що використовується у першому запиті? Воно означає логічну операцію «І». Нагадаю, логічна операція «І» видає «істина» (1) тільки якщо обидва вирази є істиною. Але логічний оператор «АБО» видає «істина» (1) навіть якщо хоча б один із виразів є істиною. Тобто. вираз

WHERE name = "Demo" OR 1

завжди буде істиною, завжди повертатиме 1. Оскільки один із двох порівнюваних виразів завжди повертає 1.

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

SELECT * FROM `members` WHERE name = "Demo" OR 1

Адресний рядок:

http://localhost/test/mysql-inj-lab1/index.php?name=Demo' OR 1 —+ &password=111

Результат:

Результат чудовий! Ми отримали список усіх записів у таблиці.

ORDER BY та UNION - головні друзі SQL-ін'єкцій

Ми вже зараз отримали дані, які були недоступні тим, хто не має валідних імені користувача та пароля. Чи можна ще щось отримати? Так, можна отримати повний дамп цієї таблиці (нагадаю, у нас, як і раніше, немає паролів. Більш того, ми можемо отримати всі дані з усіх баз на цьому сервері через одну крихітну дірочку!

UNION дозволяє об'єднувати SQL запити. У реальному житті у мене завдання прості, тому і прості запити до баз даних та можливостей UNION я не користуюсь. Але для SQL-ін'єкцій цінніше цього слова немає.

UNION дозволяє досить гнучко поєднувати SQL-запити з SELECT, у тому числі від різних баз даних. Але є важлива вимога до синтаксису: кількість стовпців у першому SELECT має дорівнювати кількості стовпців у другому SELECT.

ORDER BY задає сортування отриманих із таблиці даних. Можна задавати сортування на ім'я стовпця, а можна за його номером. Причому, якщо стовпця з таким номером немає, буде показано помилку:

Адресний рядок:

http://localhost/test/mysql-inj-lab1/index.php?name=-1′ ORDER BY 1 —+ &password=111

Запит має такий вигляд:

SELECT * FROM `members` WHERE name = "-1" ORDER BY 1

Ми замінили ім'я користувача на -1, щоб не виводилися жодні дані.

Помилки немає, також немає помилки і при запиті

SELECT * FROM `members` WHERE name = "-1" ORDER BY 2 SELECT * FROM `members` WHERE name = "-1" ORDER BY 3 SELECT * FROM `members` WHERE name = "-1" ORDER BY 4 SELECT * FROM `members` WHERE name = "-1" ORDER BY 5

А ось запит

SELECT * FROM `members` WHERE name = "-1" ORDER BY 6

йому відповідає адресна рядка

http://localhost/test/mysql-inj-lab1/index.php?name=-1′ ORDER BY 6 —+ &password=111

Видав помилку

Помилка: Unknown column "6" in "order clause"

Це означає, що з таблиці вибираються дані з п'яти колонок.

Конструюємо наш запит з UNION:

Як я сказав, кількість полів має бути в обох SELECT однакова, а ось що в цих полях – не дуже важливо. Можна, наприклад, прописати просто цифри – і саме їх і буде виведено. Можна прописати NULL – тоді замість поля нічого не буде виведено.

SELECT * FROM `members` WHERE name = "-1" UNION SELECT 1,2,3,4,5

Адресний рядок:

http://localhost/test/mysql-inj-lab1/index.php?name=-1′ UNION SELECT 1,2,3,4,5 —+ &password=111

Інший спосіб знаходження кількості стовпців – за допомогою того ж UNION. Лісеня додаємо кількість стовпців:

SELECT * FROM `members` WHERE name = "-1" UNION SELECT 1 SELECT * FROM `members` WHERE name = "-1" UNION SELECT 1,2 SELECT * FROM `members` WHERE name = "-1" UNION SELECT 1 ,2,3 SELECT * FROM `members` WHERE name = "-1" UNION SELECT 1,2,3,4

Всі вони викликатимуть одну і ту ж помилку:

Помилка: The used SELECT statements have different number of columns

Робіть так, поки не зникне повідомлення про помилку.

Зверніть увагу, що вміст деяких полів UNION SELECT 1, 2, 3, 4, 5 виводиться на екран. Замість цифр можна встановити функції.

Що писати в SELECT

Є деякі функції, які можна писати безпосередньо в UNION:

  • DATABASE()- показати ім'я поточної бази даних
  • CURRENT_USER()— показує ім'я користувача та ім'я хоста
  • @@datadir- виводить абсолютний шлях до бази даних
  • USER()- Ім'я користувача
  • VERSION()- Версія бази даних

У прикладі виводяться поля 2, 4 і 5. Тобто. ми можемо використовувати будь-яке з цих полів.

Використовуємо DATABASE() в UNION SELECT

http://localhost/test/mysql-inj-lab1/index.php?name=-1′ UNION SELECT 1,2,3,4,DATABASE() —+ &password=111

Результат:

Використовуємо CURRENT_USER() у UNION SELECT

http://localhost/test/mysql-inj-lab1/index.php?name=-1′ UNION SELECT 1,2,3,4,CURRENT_USER() —+ &password=111

Результат:

Використовуємо @@datadir у UNION SELECT

http://localhost/test/mysql-inj-lab1/index.php?name=-1′ UNION SELECT 1,2,3,4,@@datadir —+ &password=111

Результат:

Отримання імен таблиці, полів та дамп бази даних

У базі даних information_schemaє таблиця, яка називається tables. У цій таблиці міститься список усіх таблиць, які є у всіх базах даних цього сервера. Ми можемо відібрати наші таблиці, шукаючи у полі table_schemaназва нашої бази даних – 'db_library' (ім'я ми дізналися за допомогою DATABASE()).

Це називається повна техніка UNION. Матеріалу по ній достатньо в Інтернеті. На моєму MySQL сервері повна техніка UNION не працює. У мене з'являється помилка

Помилка: Illegal mix collations for operation "UNION"

Не працює не через кривизну рук, оскільки у sqlmap також ця техніка не приносить результатів:

Одного разу з'єднується з повним UNION technique (можна бути обмеження на відновленому номері даних). Falling back to partial UNION technique

Можливо це пов'язано з версією MySQL 5.6. Т.к. навести практичних прикладів я не можу, а переписувати чужі непрацюючі команди мені не цікаво – зараз і без мене в Інтернеті розвелося «великих теоретиків» скільки завгодно, то вирішив одразу перейти до розгляду часткової техніки UNION. Але це не найпростіша техніка, та й стаття вже вийшла досить великою.

п.с. ах так, забув про LIMIT. Теж наступного разу розповім про роль LIMIT у SQL-ін'єкціях.

Гарант є довіреним посередником між Учасниками під час угоди.


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

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

Як працює SQL-ін'єкціях

Типи атак, які можуть бути виконані з використанням SQL-ін'єкції, різняться за типом механізмів бази даних, що вражаються. Атака націлюється на динамічні оператори SQL. Динамічний оператор — це оператор, який створюється під час виконання на основі параметрів із веб-форми або рядка запиту URI .

Розглянемо просте веб-додаток із формою входу. Код HTML-форми наведено нижче:

  • Форма приймає адресу електронної пошти, а потім пароль відправляється у файл PHP з ім'ям index.php;
  • Сесія зберігається у файлі cookie. Ця можливість активується під час встановлення прапорця remember_me . Для надсилання даних використовується метод post. Це означає, що значення не відображаються в URL-адресі .

Припустимо, що запит для перевірки ідентифікатора користувача на стороні сервера виглядає так:

  • Запит використовує значення масиву $_POST безпосередньо, не саніруя його;
  • Пароль шифрується з використанням алгоритму MD5.

Ми розглянемо атаку з використанням SQL ін'єкції sqlfiddle. Відкрийте у браузері URL-адресу http://sqlfiddle.com/ . На екрані з'явиться вікно.

Примітка: Вам потрібно буде написати інструкції SQL:


Крок 1. Введіть цей код у ліву панель:

CREATE TABLE `users` (`id` INT NOT NULL AUTO_INCREMENT, `email` VARCHAR(45) NULL, `password` VARCHAR(45) NULL, PRIMARY KEY(`id`)); insert in users (email,password) values ​​(" [email protected]"md5("abc"));

Крок 2. Натисніть кнопку « Build Schema».
Крок 3. Введіть наведений нижче код у правій панелі:

select * from users;

Крок 4. Натисніть « Run SQL». Ви побачите наступний результат:

Припустимо, що користувач надає адресу електронної пошти [email protected]і 1234 як пароль. Запит, який має бути виконаний у базі даних, може виглядати так:

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

[email protected]" OR 1 = 1 LIMIT 1 -- " ]

та xxx у полі пароля.

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

  • [email protected]закінчується однією лапкою, яка завершує рядок;
  • OR 1 = 1 LIMIT 1 — це умова, яка завжди буде істинною, вона обмежує результати, що повертаються, тільки одним записом.

0; 'AND... — це коментар SQL, який виключає частину пароля.

Скопіюйте наведений вище запит і вставте його у текстове поле SQL FiddleRun SQL , як показано нижче:


Хакерська активність: SQL-ін'єкції у веб-додатки

У нас є простий веб-додаток, доступний за адресою http://www.techpanda.org/, який спеціально зроблений вразливим для атак з використанням SQL ін'єкцій для новачків у демонстраційних цілях. Код HTML-форми, наведений вище, взято зі сторінки авторизації цієї програми.

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

Ви можете використовувати поле пароля, щоб обійти його. На наведеній нижче діаграмі показано кроки, які потрібно виконати:


Припустимо, що зловмисник надає такі дані:

Крок 1: Вводить [email protected]як адреса електронної пошти;
Крок 2: Вводить xxx') OR 1 = 1 -];


Натискає кнопку «Надіслати».

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

На наведеній нижче діаграмі показано, як запит було згенеровано:


  • У запиті передбачається, що використовується шифрування md5;
  • Використовується одиночна лапка і дужка, що закривається;
  • До оператора додається умова, яка завжди буде істинною.

Як правило, зловмисники для досягнення своїх цілей намагаються застосувати в атаці з використанням ін'єкцій SQL кілька різних методів.

Інші типи атак із використанням SQL-ін'єкцій

SQL-ін'єкції можуть завдати набагато більших збитків, ніж вхід в систему в обхід механізму авторизації. Деякі з таких атак можуть:

  • Виконати видалення даних;
  • Виконати оновлення даних;
  • Виконати додавання даних;
  • Виконати на сервері команди, які завантажуватимуть та встановлюватимуть шкідливі програми;
  • Здійснити експорт на віддалений сервер зловмисника цінних даних, таких як реквізити кредитної картки, електронна пошта та паролі.

Наведений вище список не повний. Він просто дає уявлення про те, яку небезпеку становлять SQL-ін'єкції.

Інструменти для автоматизації SQL-ін'єкцій

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

  • SQLSmack;
  • SQLPing 2;
  • SQLMap.

Як запобігти SQL-ін'єкції

Ось кілька простих правил, які дозволять захиститися від атак з використанням SQL-ін'єкцій:

Введення даних користувача не повинно бути довіреним. Його завжди потрібно санувати, перш ніж дані використовуватимуться в динамічних операціях SQL.

Збережені процедури— вони можуть інкапсулювати SQL-запити та обробляти всі вхідні дані як параметри.

Підготовлені запити— спочатку створюються запити, а потім усі надані дані користувача обробляються як параметри. Це не впливає на синтаксис інструкції SQL.

Регулярні вирази- можуть бути використані для виявлення потенційно шкідливого коду та його видалення перед виконанням операторів SQL.

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

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

Хакерська активність: використання для SQL-ін'єкцій Havij

У цьому практичному сценарії ми збираємося використовувати програму Havij Advanced SQL Injection для сканування вразливостей сайту.

Примітка. Ваша антивірусна програма може реагувати на цю програму через її природу. Тому необхідно додати її до списку винятків або призупинити антивірусне програмне забезпечення:


Згаданий вище інструмент можна використовувати для оцінки вразливості/програми.

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

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

Приклад вразливості

Припустимо, є скрипт, що відображає список користувачів з цього міста, що приймає як GET-параметр id міста. Звернення до скрипту відбуватиметься за допомогою HTTP за адресою /users.php?cityid=20

У скрипті вище розробник вставляє GET-параметр SQL-запит, маючи на увазі, що в GET-параметрі завжди буде число. Зловмисник може передати як параметр рядок і цим пошкодити запит. Наприклад, він звернеться до скрипту як /users.php?cityid=20; DELETE * FROM users
SQL-запит вийде таким:

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

Як захиститись?

Давайте покладемо інформацію користувача в одинарні лапки. Чи допоможе це?

З прикладу вище видно, що укласти одиночні лапки недостатньо. Необхідно також екранувати всі лапки, що містяться у рядку. Для цього в PHP передбачена функція mysql_real_escape_string(), яка додає зворотний сліш перед кожною лапкою, зворотною лапкою і деякими іншими спецсимволами. Розглянемо код:

Отже, щоб захиститися від SQL-ін'єкцій, всі зовнішні параметри, які можуть містити текст, повинні бути перед включенням до SQL-запиту оброблені за допомогою mysql_real_escape_string() і укладені в поодинокі лапки.

Якщо відомо, що параметр повинен набувати числового значення числовим, його можна привести до числового вигляду явно за допомогою функції intval()або floatval(). У цьому прикладі ми могли б використовувати:

$sql = "SELECT username, realname
FROM users
WHERE cityid=""
.intval ( $_GET [ "cityid" ] ) .""" ;

Відмінності mysql_real_escape_string() та mysql_escape_string()

mysql_real_escape_string() є вдосконаленою версією функції mysql_escape_string(), що широко застосовується для формування безпечних запитів до БД MySQL. Відмінність цих двох функцій полягає в тому, що mysql_real_escape_string() правильно працює з багатобайтовими кодуваннями.

Припустимо, в даних є символ (скажімо, в UTF-8), код якого складається з двох байт - шістнадцяткових 27 і 2B (десяткові 39 і 43 відповідно). mysql_escape_string() сприймає кожен байт даних, що передаються їй, як окремий символ (точніше, як код окремого символу) і вирішить, що послідовність байт 27 і 2B — це два різні символи: одинарна лапка (") і плюс (+). Оскільки функція сприймає лапку як спеціальний символ, перед байтом з кодом 27, який насправді є частиною якогось нешкідливого символу, буде доданий слеш (\).

Варто зазначити, що mysql_real_escape_string() працює правильно у всіх випадках і може повністю замінити mysql_escape_string().

mysql_real_escape_string() доступна у PHP з версії 4.3.0.

Додаткові приклади

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

Ін'єкція у складних запитах

У найпростішому прикладі була можливість вбудувати код у кінець SQL-запиту. Насправді наприкінці SQL-запроса може бути додаткові умови, оператори сортування, групування та інші SQL-конструкции. У кожному конкретному випадку зловмисник намагатиметься вбудувати шкідливий шматок таким чином, щоб запит загалом залишився синтаксично коректним, але виконував іншу функцію. Тут ми розглянемо найпростіший приклад вразливого запиту з додатковою умовою.

В результаті умова age<35 нічого очікувати проводити вибірку, т.к. оператор OR має нижчий пріоритет, ніж AND, і WHERE із наведеного вище запиту по-іншому можна записати у вигляді WHERE (cityid="20" AND 1 ) OR ("1" AND age<"35" ) (нагадаємо, що вираз WHERE 1 істинно завжди). В результаті під умову підійдуть і ті рядки, у яких cityid = "20", і ті, у яких age<35, причем наличие последних не обязательно.

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

Результати запиту не відображаються користувачеві

Може виявитися, що вразливий запит, результати якого не відображаються користувачеві. Це може бути, наприклад, допоміжний запит:

$sql = "SELECT count(*)
FROM users
WHERE userid=""
.$_GET [ "userid" ] .""" ;

Запит вище лише перевіряє наявність користувача з даним userid: якщо він повертає будь-яку відмінну від нуля величину — показується профіль користувача з відповідним userid, якщо ж повернутий 0 (тобто, немає користувачів, що задовольняють критерію запиту) — повідомлення "користувач не знайдений".

І тут визначення пароля (чи іншої інформації) проводиться перебором. Зломщик передає як параметр userid рядок 2" AND password LIKE "a%. Підсумковий запит:

SELECT count (*) FROM users WHERE userid="2" AND password LIKE "a%"

Зломщик отримає "користувача не знайдено", якщо пароль не починається на букву "a", або стандартну сторінку з профілем користувача, в іншому випадку. Перебором визначається перша літера пароля, потім друга і т.д.

Висновки

  • Усі запити, які використовують зовнішні дані, потрібно захистити від ін'єкцій SQL. Зовнішні дані можуть бути передані не тільки як GET-параметрів, а й методом POST, взяті з COOKIE, зі сторонніх сайтів або бази даних, в яку користувач мав можливість занести інформацію.
  • Усі числові параметри слід явно перетворювати на числовий вигляд за допомогою функцій intval()і floatval()
  • Усі строкові параметри слід екранувати за допомогою mysql_real_escape_string()і укладати в лапки.
  • Якщо побудувати SQL-ін'єкцію складно, не слід очікувати, що зловмисник не здогадається, як це зробити. Особливо це стосується двигунів, вихідний код яких є публічним.

Успіхів у побудові безпечних додатків!