Як боротись з SQL-ін’єкцією за допомогою PHP

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

  1. Що таке SQL-ін’єкція

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

  1. Приклади SQL-ін’єкцій

Відправити свій запит зловмисник може не тільки вписавши його в поле для вводу інформації (використовуючи $_POST ), він може також підставити свої $_GET змінні до адресної стрічки, або власноруч змінити свої $_COOKIE. Тому слід бути обачними при роботі з цими глобальними масивами даних.

2.1 $_POST:

Якщо у вас на сторінці є форма для вводу певної інформації, зловмисник може використати її поля для своїх цілей. Замість очікуваної стрічки (ім’я, пароль, тощо) він введе в таке поле свій власний запит.

Приклад SQL-ін’єкції

Більшість таких SQL-ін’єкцій виглядають наступним чином:

asd' or 1=1--

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

SELECT * FROM users WHERE username = 'asd' or 1=1--' and password = 'asd'

Отже як видно з прикладу, наш код буде успішно виконано. А так як вираз 1=1 буде завжди повертати true, ми гарантовано отримаємо доступ.

Вас швидше за все зацікавить, для чого нам подвійний дефіс (--). Цей подвійний прочерк в кінці стрічки повідомляє SQL серверу, що йому слід проігнорувати решту запиту. Якщо ж ви хочите написати ін’єкцію не для SQL серверу, то в такому випадку, вам у пригоді стане, заміна подвійного дефісу на одинарну кавичку.

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

') or ('1'='1
"or "1"="1
' or '1'='1
Or 1=1--
" or 1=1--
' or 1=1--

2.2 $_GET

Також доволі часто зловмисники використовують адресну стрічку (URL) для своїх атак. Цей метод так як і попередній, не менш небезпечний. Коли на сервері використовується PHP та MySQL (на даний момент, найпопулярніша комбінація), адреса до скрипта як правило виглядає приблизно наступним чином:

http://somesite.com/login_script.php?id=1

Додаючи до такої стрічки трішки SQL-я можна робити страшні речі:

http://somesite.com/login_script.php?id=1‘; DROP TABLE login; #

В даному випадку використано знак # натомість подвійного дефісу, так як він говорить SQL серверу проігнорувати всі наступні запити, які йтимуть після нашого. І що саме небезпечніше (якщо ви ще не помітили), ми щойно сказали серверу вилучити табличку з користувачами. Даний приклад чудово демонструє наскільки небезпечними можуть бути SQL-ін’єкції.

  1. Що потрібно зробити для захисту своїх скриптів від SQL-ін’єкцій

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

На щастя, дана небезпека відома вже досить давно. В PHP навіть з’явилась спеціальна функція (починаючи з версії 4.3.0), яка бориться з цим типом атак — mysql_real_escape_string.

mysql_real_escape_string перетворює стрічку на безпечну для вживання в запитах до бази даних, шляхом екранування всіх потенційно небезпечних символів. Як правило таким сиволом є одинарна кавичка ('), яка після використання цієї функції буде екранована (\').

Для того щоб захиститись від SQL-ін’єкції, всі зовнішні параметри ($_GET, $_POST, $_COOKIE), слід перед тим як включити їх до SQL запиту опрацювати за допомогою функції mysql_real_escape_string(), а в самому запиті помістити їх до одинарних кавичок. Якщо дотримуватись цього простого правила, тоді дії зловмисника приведуть до формування безпечних запитів, так як весь текст його SQL-ін’єкцій тепер всередині кавичок.

Давайте розглянемо що ж насправді виконуватиме сервер при такій обробці:

SQL-ін’єкція (стрічка яку зловмисник вписав в поле «Логін» натомість свого логіну):

sql' or 1=1--

PHP:

$name = mysql_real_escape_string($_POST['username']);
$res = mysql_query("SELECT * FROM users WHERE username = '".$name."' and password = 'asd'");

MySQL:

SELECT * FROM users WHERE username = 'sql\' or 1=1--' and password = 'asd'

Тобто, при такому запиті, натомість небезпечних дій, ми намагаємось вибрати дані користувача в якого доволі дивний username (sql' or 1=1--)

Можна піти далі й написати функціонал який буде автоматично обробляти масиви $_GET, $_POST та $_COOKIE відповідним чином, але це все залижить тільки від ваших побажань. Запам’ятати слід тільки те що, потрібно захистити ВСІ місця де в базу даних передаються дані від користувача.

Данна стаття є копією моєї статті «Боримось з SQL-ін’єкцією за допомогою PHP»

Коментарі 14

zenyk - 30 липня 2010, 13:37

цікава стаття, особливо для початківців

можна перемістити в спільноту PHP

Taran2L - 30 липня 2010, 13:59

Дякую за пораду, щось не звернув уваги куди буде публікуватись стаття. Перемістив до спільноти PHP

kilotaras - 30 липня 2010, 14:44

користувача в якого доволі дивний username (sql\' or 1=1--)

Якщо я не помиляюсь, то з юзернеймом sql' or 1=1--

Taran2L - 30 липня 2010, 14:53

Дякую, виправив.

xaos - 30 липня 2010, 16:10

Більш професійним підходом буде взагалі не використовувати динамічно згенерованих SQL запитів. Натомість використовувати 'prepared statements' з плейсхолдерами. Для прикладу в статті

$stmt = $mysqli->prepare("SELECT * FROM users WHERE username = ? and password = ?");
$stmt->bind_param('ss', $user, $pass);

$user = $_POST['username'];
$password = $_POST['password'];

$stmt->execute();

Таким чином крім того, що змінні будуть екрановані автоматично, ми отримаємо чистіший і читабельніший код а також цілий ряд інших переваг (про які можна прочитати в документації до відповідної БД наприклад dev.mysql.com/tech-resources/articles/4.1/prepared-statements.html)

Слід також згадати ектремальний спосіб уникнення SQL-ін’єкцій: не використовувати SQL взагалі en.wikipedia.org/wiki/NoSQL ;)

xaos - 30 липня 2010, 16:13

s/ектремальний/екcтремальний/

дуже не вистарчає можливості редагувати свої коментарі

connoisseur - 30 липня 2010, 20:50

Приблизно місяць назад Dan Kaminsky представив по-справжньому унікальну та досить радикальну методику боротьби з SQL Injection. Суть її полягає в кодуванні всієї отриманої від користувача інформації в base64 з подальшим декодуванням вже в тілі самого SQL-запиту. Таким чином користувач може вводити що завгодно, система нормально опрацьовуватиме це «завгодно» і без жодних змін записуватиме отриману інформацію в БД.

Наведу також лінк цієї новини на ОпенНеті, де методика описана дещо детальніше, аніж це зробив я.

Taran2L - 02 серпня 2010, 09:43

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

connoisseur - 08 серпня 2010, 19:56

«всі змінні які поступають від користувачів хешувати» так, але вони і зараз в будь-якому випадку якось обробляються

«розмір бази виросте» — хм… чому? ця методика жодним чином не вплине на розмір БД. Дані будуть вставлятись в точно такому вигляді, як їх ввів в формі користувач.

tamerlan - 15 грудня 2010, 10:43

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

tamerlan - 15 грудня 2010, 10:44

Якщо я не правий, з радістю вислухаю любу критику...))

Taran2L - 16 грудня 2010, 15:47

Але не кожен хостер дозволяє тригери використовувати

tamerlan - 17 грудня 2010, 11:10

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

Taran2L - 17 грудня 2010, 21:52

Мій замовник розміщав проекти на канадських хостерах 2 з 3-ьох не надавали можливості написання трігерів в цілях безпеки…

Коментувати
© 2009 - 2020, Розробка - соціальна ІТ спільнота.
Контакти: info@rozrobka.com
Правила користування