стрілка вверх

sql інєкція

Фільтрація та шифрування даних в php,
що надходять до серверу Бази Даних

лютий 2016

Зміст

  1. Інтро
  2. HTML-форма
  3. SQL-інєкція
  4. Захист від SQL-ін'єкції
  5. PHP-функції санітаризації вхідних даних
    • real_escape_string()
    • intval(), (int)
    • htmlspecialchars
    • trim()
    • strip_tags()
    • prepared statement
  6. Короткий підсумок вищесказаного
  7. PHP-функції шифрування даних
  8. Функції: crypt(), password_hash(), password_verify(), crypt(), hash_equals(), md5()

  9. Корисні лінки

1. Інтро

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

Розглянемо дві теми:
1. Фільтрація даних,
2. Шифрування даних.

Структура проходження даних до таблиці серверу БД:
1. Внесення даних до Html-форми користувачем веб сайту,
2. Відправлення даних до php-обробника веб сервера,
3. Обробляємо отримані дані (виконуємо операції з фільтрації та шифрування даних),
4. Зберігаємо дані в таблиці БД.


Передача, обробка 
та збереження даних в таблиці БД

Рис.1. Передача, обробка та збереження даних в таблиці БД



За замовчуванням відразу постає питання безпеки роботи веб-сайту. Тобто, якщо ми маємо HTML-форму на сайті, то неодмінно варто потурбуватись про деякі заходи безпеки, розпочинаючи від приховування даних при їх передачі до серверу, так як є такі зловмисні методики на зразок MitM-атаки. MitM-атака або атака «Людина посередині» (Англ. Man in the middle - MitM-attack) - термін криптографії, що позначає ситуацію, коли атакуючий здатний читати і видозмінювати по своїй волі повідомлення, якими обмінюються кореспонденти, причому жоден з останніх не може здогадатися про його присутність в каналі. MitM-атакою називають метод компрометації каналу зв'язку, при якому зломщик, підключившись до каналу між контрагентами, здійснює активне втручання в протокол передачі, видаляючи, спотворюючи інформацію або нав'язуючи хибну.

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


Певний матеріал стосовно саме безпеки роботи веб-сайту можна переглянути за лінком Тема веб-безпеки.



2. HTML-форма відправлення даних до серверу

Якщо переглянути просту форму, то як правило, вона може мати вигляд щось на зразок:


<form action="action_page.php" method="post">
  First name:
  <input type="text" name="firstname" value="Mickey" >
  Last name:
  <input type="text" name="lastname" value="Mouse">
  <input type="submit" value="Submit">
</form>

І звісно матиме вигляд на веб-сторінці на зразок:

First name:

Last name:



Дані, що будуть введені в поля html-форми, відправляються на обробку до серверу згідно вказівками атрибуту action, тобто в нашому випадку до php-скрипту action_page.php

Безпечна передача даних до обробника

HTML-форма дозволяє вибрати два способи передачі даних до серверу.

<form action="action_page.php" method="get">

або:

<form action="action_page.php" method="post">

Можна застосовувати метод GET (що встановлений по замовчуванню), проте в такому випадку дані будуть видимі в адресній стрічці вашого браузера:

action_page.php?firstname=Mickey&lastname=Mouse

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

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

Хоча можна відмітити, що для безпечної передачі можна застосувати HTTPS протокол передачі даних.



3. SQL-ін'єкція

Коротко, що таке SQL-ін'єкція та навіщо захищатись від ін'єкції?

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

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

Наприклад SQL-запит:

$res = mysql_query("SELECT id_news,header,body,author 
                    FROM news
                    WHERE id_news = ". $_REQUEST['id']);

Якщо зловмисник передасть параметр id конструкцію

-1 UNION SELECT 1,username, password,1 FROM admin

це викличе виконання SQL-запиту:

SELECT id_news, header, body, author 
FROM news 
WHERE id_news =-1 
UNION SELECT 1,username,password,1 
FROM admin

Таким чином зловмисник дізнається Ім'я та Пароль користувачів ресурсу.

Пошук скриптів вразливих до SQL-ін'єкцій

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

Наприклад, замість відправлення даних:
http://site/test.php?id=12

відправляються дані на зразок:
http://site/test.php?id=12'
http://site/test.php?id=aaa
http://site/test.php?id=13-1

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


Стосовно повідомлень про помилки виконання роботи php-скрипту Error Reporting.



4. Захист від SQL-ін'єкції

4.1. Рекомендації захисту серверу БД.

Методика уникнення вразливостей за ресурсом php.net: SQL Injection.


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

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

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

Інші методи загроз відбуваються через перевірку загальноприйнятих імен таблиць чи атрибутів полів. Наприклад, форма входу, що використовує такі імена як: 'users' таблиця зазвичай має назви колонок 'id', 'username', та 'password'.

Загрози для БД можливі за умови, що розробник програмного застосунку не переймався питанням безпеки БД. Добрим правилом буде, якщо ви (розробник скрипту) будете завжди перевіряти дані, що надходять із клієнтської сторони, навіть якщо це дані вибору форми select box, приховані вхідні дані поля форми чи дані cookie. Не варто їм (даним , що вводить користувач) довіряти.

Рекомендації щодо підвищення безпеки серверу БД:

  1. Ніколи не з'єднуйтесь із БД, як адміністратор чи власник БД. Використовуйте завжди зареєстрованого користувача із обмеженими правами.
  2. Використовуйте підготовлені вирази із зв'язаними змінними. Їх використання спроможне завдяки застосуванню PDO , MySQLi чи іншими відповідними бібліотеками.
  3. Перевіряйте чи введені дані відповідають очікуваному типу даних. PHP має широкий спектр відповідних функцій валідації, від найпростіших Variable Functions та Character Type Functions (також, is_numeric(), ctype_digit() відповідно).
  4. Якщо скрипт очікує числових вхідних даних, подумайте про верифікацію даних за допомогою ctype_digit(), або змініть тип даних застосовуючи settype(), чи використовуйте їх числові відображення sprintf().
  5. Якщо ж БД не підтримує зв'язані змінні, тоді рекомендується брати в лапки кожні нецифрові дані, що приходять з клієнтської сторони та передавати до серверу БД із застосуванням функції mysqli_real_escape_string, sqlite_escape_string().
  6. Не розголошуйте ніякої специфічної інформації стосовно власної БД, особливо про структуру запитів. Корисним буде переглянути Error Reporting та Error Handling and Logging Functions.
  7. Ви можете застосувати збережені процедури, що унеможливлює користувачем передачу даних напряму до БД.
  8. Крім того, корисно вести журнал логів запитів до серверу БД. Звісно журнал ніяким чином не унеможливлює вразливість серверу БД, проте він надасть вам інформацію про сторонні запити та корисну інформацію щодо аналізу для покращення безпеки запиту.

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

<?php

settype
( $offset'integer' );
$query  "SELECT id, name 
         FROM products 
         ORDER BY name 
         LIMIT 20 
         OFFSET 
$offset ;";

// Зауважте, знак %d має тип даних string, застосування ж знаку %s немає сенсу
$query  sprintf( "SELECT id, name 
         FROM products 
         ORDER BY name 
         LIMIT 20 
         OFFSET %d;"
, $offset);

?>

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

- Які дієві засоби необхідні проти методики SQL-ін'єкцій?

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

Розглянемо деякі з них...



5. Способи фільтрації вхідних даних

  1. Екранування потенційно небезпечних символів, застосування функції real_escape_string()
  2. Перевірка вхідних даних на числовий тип даних, застосування intval()
  3. Перетворюємо в html-сутності htmlspecialchars()
  4. Видаляємо пусті символи до та після тексту trim()
  5. Екрануємо html, php теги strip_tags()
  6. Використання параметризованих запитів (prepare, bind and execute )
  7. Обмеження кількості вхідних символів повідомлень

дещо про роботу функцій:

5.1. Функція real_escape_string()

Офіційна документація функція real_escape_string ().

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

Функція перетворює деякі спеціальні символи строкових даних відповідно до встановленого типу кодування.

Формат спеціальних символів повинен бути встановлений на сервері або за допомогою функції mysqli_set_charset().

Символи, що кодуються \0, \n, \r, \, ', ", and Control-Z .

Застосування функції real_escape_string():

<?php
$mysqli 
= new mysqli("host""user""pass""DB");

$mysqli->query("CREATE TEMPORARY TABLE myCity LIKE City");

$city "'s Hertogenbosch";

/* даний запит не виконається,так як не екранована змінна $city */
if (!$mysqli->query("INSERT into myCity (Name) VALUES ('$city')")) {
    
printf("Error: %s\n"$mysqli->sqlstate);
}

$city $mysqli->real_escape_string($city);

/* Змінна екранована $city - запит буде працювати коректно*/
if ($mysqli->query("INSERT into myCity (Name) VALUES ('$city')")) {
    
printf("%d Row inserted.\n"$mysqli->affected_rows);
}

$mysqli->close();
?>

5.2. Функція intval(), (int)

Офіційна документація функція intval().

За умови, що нам необхідно отримувати лише числові значення даних для фільтрації вхідних даних можна застосувати функції intval() або дозволяється(int) - повертає ціле значення числового даного.

Приклад перевірки на числовий тип даних:

$input_data=intval ($_POST['input_data']);
//або такий варіант
$input_data=(int) $_POST['input_data'];
// повертає лише числа
echo $input_data;  

Якщо ж будуть введені строкові дані,- вихідне значення після застосування (int) буде 0.


5.3. Функція htmlspecialchars()

Офіційна документація функція htmlspecialchars().

Функція перетворює деякі елементи, такі як &, ", ', <, > - в кодування встановленого формату, наприклад, UTF08.

htmlspecialchars() перетворює спеціальні символи в HTML сутності:
'&' (амперсанд) перетворюється в '&amp;'
'"' (двійні кавички) перетворюється в '&quot;'
''' (одинарні кавички) перетворюється в '&#039;'
'<' (знак "менше ніж") перетворюється в '&lt;'
'>' (знак "більше ніж") перетворюється в '&gt;'


В такому випадку досить легко працювати з html-тегами, наприклад:

<?php
$str 
"Це <b>виділений</b> текст.");
echo htmlspecialchars( $str);

<?php
$new 
htmlspecialchars("<a href='test'>Test</a>"ENT_QUOTES);
echo 
$new// &lt;a href=&#039;test&#039;&gt;Test&lt;/a&gt;
?>

5.4. Функція trim()

Офіційна документація функція trim()

Функція trim() видаляє справа та зліва символи:
" " (ASCII 32 (0x20)), символ пробілу.
"\t" (ASCII 9 (0x09)), символ табуляції.
"\n" (ASCII 10 (0x0A)), символ переводу рядка.
"\r" (ASCII 13 (0x0D)), символ повернення каретки.
"\0" (ASCII 0 (0x00)), NUL-байт, в деяких мовах символ кінця рядка.
"\x0B" (ASCII 11 (0x0B)), вертикальна табуляція.


5.5. Функція strip_tags()

Офіційна документація функція strip_tags()

Функція вилучає HTML та PHP теги із тексту.

<?php
$text 
'<p>Перевіряємо параграф.</p><!-- Коментарій --> <a href="#fragment">Інший текст</a>' ;
echo 
strip_tags( $text);
echo 
"\n" ;

// Дозволяємо теги <p> та <a>
echo strip_tags( $text'<p><a>' );
?>

Результат роботи скрипту буде:

Перевіряємо параграф. Інший текст
<p>Перевіряємо параграф.</p> <a href="#fragment">Інший текст</a>

5.6. Використання підготовлених виразів MySQLi

Детальніше про роботу підготовлених виразів за матеріалом статті Prepared Statements

Приклад використання підготовлених виразів.

<?php
$mysqli 
= new mysqli ("example.com" "user""pass" "db");
if (
$mysqli->connect_errno ) {
   echo 
"Помилка підключення до серверу БД: (" 
    $mysqli-> connect_errno  ") " .
    $mysqli ->connect_error ;
}

/* Виконуємо Не підготовлений вираз */
if (! $mysqli->query ("DROP TABLE IF EXISTS test" ) ||
    ! 
$mysqli-> query("CREATE TABLE test(id INT)" )) {
    echo 
"Помилка створення таблиці: ("
     $mysqli->errno  ") " $mysqli ->error;
}

/* Підготовлений вираз, Етап 1: Підготовка */
if (!($stmt  $mysqli->prepare ("INSERT INTO test(id) VALUES (?)" ))) {
    echo 
"Помилка підготовки виразу: (" .
    $mysqli ->errno  ") "  $mysqli-> error;
}

$id 1 ;
if (!
$stmt ->bind_param ("i"$id )) {
    echo 
"Помилка під час звязування параметрів: ("
     $stmt-> errno ") "  $stmt->error ;
}

if (!
$stmt-> execute ()) {
    echo 
"Помилка під час виконання виразу: ("
     $stmt->errno  ") "  $stmt->error ;
}

/* Багаторазове використання підготовленого виразу,
відправляються лише дані до серверу */
for ($id  2$id  5$id ++) {
    if (!
$stmt ->execute()) {
        echo 
"Помилка під час виконання: ("
     $stmt-> errno ") "  $stmt-> error;
    }
}

/* Рекомендується явно закривати зєднання із сервером БД */
$stmt ->close();

/* Звичайний запит до БД */
$res  $mysqli-> query("SELECT id FROM test" );
var_dump( $res->fetch_all ());
?>


6. Короткий підсумок поданого матеріалу

Розглянемо невеликий скрипт, що демонструє все вищесказане.
1. Маємо певну HTML-форму
2. PHP-скрипт, що санітаризує вхідні дані.
3. jQuery-скрипт, що вказує кількість введених символів.

Зразок HTML-форми

<form action="Handler_Form.php" method="post">
 Text: <textarea name="message" id="text_for_coment">
</textarea> 
 <input type="submit" value="Submit">
</form>
Кількість дозволених символів:
<div id="num_letters">400</div>

Зразок PHP-обробника Handler_Form.php

<?php
// Отримуємо дані
$msg=$_POST["message"];

// Перетворюємо деякі символи
$msg = htmlspecialchars($msg);

// Контролюємо кількість введених символів
$count_chars=strlen($msg);
if ($count_chars>400) {
  echo 'Повідомлення завелике!';
};

// Заносимо значення до БД
$connect_db = new mysqli("host","user","pass","db");
// застосовуємо real_escape_string
$msdg_to_db = $connect_db->real_escape_string($msg);
$result = $db->query("INSERT INTO `Some_table`(`Some_field`) 
             VALUES ('$msdg_to_db');");
$connect_db->close();

?>

Вважаю, додатково варто обмежувати кількість символів повідомлення.
Скажімо, якщо довжина текстового повідомлення буде 400 символів jQuery-скрипт виглядатиме приблизно ось так:

<script src="https://code.jquery.com/jquery-1.10.2.js"></script>>

<script>  
$(document).ready(function(){
  $("#text_for_coment").keyup(function()	{
    var box=$("#text_for_coment").val();
    var counter= 400 - box.length;		
    $('#num_letters').html(counter);		
  });	
}); 
</script>

Коротко, скрипт рахує кількість символів, що введені в полі з id='text_for_coment' та виводить інформаційне повідомлення кількості символів, що дозволяється ввести в полі повідомлення, в область з id='num_letters'

Форма звернення:

Можна перевірити роботу jQuery-скрипту:



Кількість дозволених символів: 400


Корисно:

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

Привіт! Це повідомлення без тега pre, тому всі повідомлення будуть вноситись із таблиць бази даних в один рядочок; а якщо повідомлення користувача ще й довге, то це дещо не зручно. Все ж краще розбивати повідомленнь на абзаци, та зберігати авторський наголос думки....


Привіт! Це повідомлення з тегом pre, тому всі повідомлення будуть із таблиць БД за стилем користувача....

Реалізується, досить просто, встановленням тегу pre в код php-скрипту обробника даних таблиці:

$connect_db = new mysqli("host","user","pass","db");
$result = $connect_db->query("SELECT `Some_field` 
   FROM `Some_table`);

//Отримуємо результуючу вибірку даних
while ($row = $result->fetch_assoc()) {
  echo "Повідомлення:<pre>".$row['Some_field']."</pre>";
};

$connect_db->close();

Тоді повідомлення матимуть вигляд, на зразок:

Повідомлення: Тестуємо одинарні лапки ' та теги html <a>



7. PHP-функції шифрування даних

Матеріал стосовно шифрування даних розглянемо на новій веб-сторінці Шифрування даних...



8. Корисні лінки

1. SQL Injection. Переклад статті за матеріалом ресурсу php.net

2. Prepared Statements. Переклад статті за матеріалом ресурсу php.net