Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ВСТУП пхп.docx
Скачиваний:
1
Добавлен:
01.03.2025
Размер:
9.07 Mб
Скачать

MySql і проблеми безпеки

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

mysql_query ("INSERT INTO table SET name = '$name'");

В $name може зберігатися рядок, що містить апострофи.

Розглянемо, який запит прийде серверу MySQL, якщо $name = "cat's":

INSERT INTO table SET name = 'cat's'

Ця команда синтаксично некоректна і викличе помилку під час виконання.

Але може бути і гірше.

Розглянемо такий запит:

mysql_query ("DELETE FROM table WHERE name = '$name'");

Якщо параметр $name приходить з форми, і користувач вказав в ньому наступний рядок: "! 'or 1 = 1 or '! ", то після підстановки вийде такий запит до бази даних:

DELETE FROM table-WHERE name = '!' OR 1 = 1 OR '!'

Цей запит видалить всі записи з таблиці table, тому що вираз SQL 1 = 1 завжди істинно.

Розглянемо два способи захисту від подібних помилок чи дій зловмисника:

  • Екранування спецсимволів.

  • Шаблони запитів і placeholders.

Екранування спецсимволів

Перш ніж передавати значення змінних форми в SQL-запити, необхідно спеціальним чином екранувати в них деякі символи (зокрема, апостроф), наприклад, поставити перед ними зворотний слеш. Для вставки призначена функція:

mysql_escape_string ()

string mysql_escape_string (string $ str)

Функція схожа на іншу функцію addslashes (), однак вона додає слеш перед більш повним набором спеціальних символів. Практика показує, що для текстових даних можна застосовувати і функцію addslashes () замість mysql_escape_string (). В багатьох скриптах так і робиться.

За стандартом MySQL екрануванню піддаються символи, які в РНР записуються так: "\ х00", "\ n", "\ г", "\ \", "" ', "" і "\ х1А".

В це число входить символ з нульовим ASCII-кодом, а тому mysql_escape_string () припустимо застосовувати не тільки для текстових, але також і для бінарних даних. Можна, наприклад, считувати в змінну GIF-зображення (функція file_get_contents ()), а потім вставити його в базу даних, попередньо проекраніровав всі спецсимволи. При витяганні картинка виявиться в тому ж вигляді, в якому вона була спочатку.

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

З використанням mysql_escape_string () код попереднього запиту виглядає так:

mysql_query (

"DELETE FROM table WHERE name = '". Mysql_escape_string ($ name). "'");

Шаблони запитів і placeholders

Замість явного екранування і вставки змінних в запит на їх місце поміщають спеціальні маркери (placeholders, "хранителі місця"). Ті ж значення, які будуть підставлені замість них, передаються окремо, додатковими параметрами.

З використанням гіпотетичної функції mysql_qwo, код якої буде представлений нижче, попередній запит може бути переписаний так:

mysql_qw ('DELETE FROM table WHERE name =?', $ name);

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

В лістингу 13.1 міститься найпростіша реалізація функції mysql_qw () (qw - від англ. Query wrapper, "обгортка для запиту").

Лістинг 13.1 - Найпростіша функція для роботи з placeholders.

<? Php .

/ / Result-set, mysql_qw ($ connection_id, $ query, $ argl, $ arg2 ...).

/ / - Або -

/ / Result-set mysql_qw ($ query, $ argl, $ arg2, ...)

/ / Функція виконує запит до MySQL через з'єднання, заданий як

/ / $ Connection_id (якщо не вказано, то через останнє відкрите).

/ / Параметр $ query може містити знаки підстановки?,

/ / Замість яких будуть підставлені відповідні значення

/ / Аргументів $ arg1, $ arg2 і т. д. (по порядку), екрановані і

/ / Вкладені в апострофи.

function mysql_qw ()

{

/ / Отримуємо всі аргументи функції.

$ Args = func_get_args ();

/ / Якщо перший параметр має тип "ресурс", то це ID-з'єднання.

$ Соnn = null;

if (is_resource ($ args [0])) $ conn = array_shift ($ args);

/ / Формуємо запит за шаблоном.

$ Query = call_user_func_array ("mysql_make_qw", $ args);

/ / Викликаємо SQL-функцію.

return $ conn! == null? mysql_query ($ query, $ conn): mysql_query ($ query);

}

/ / String mysql_make_qw ($ query, $ argl, $ arg2, ...)

/ / Ця функція формує SQL-запит по шаблону $ query,

/ / Містить placeholders.

function mysql_make_qw ()

{

$ Args = func_get_args ();

/ / Отримуємо в $ tmp1 ЗАСЛАННЯ на шаблон запиту.

$ Tmp1 = & $ args [0];

$ Tmp1 - str_replace ("%", "%%", $ tmp1);

$ Tmp1 = str_replace ("?", "% S", $ tmp1);

/ / Після цього $ args [0] також виявиться зміненим.

/ / Тепер екрануючи всі аргументи, окрім першого.

foreach ($ args as $ i => $ v)

{

if (! $ i) continue; / / це шаблон

if (is_int ($ v)) continue; / / цілі числа не потрібно екранувати

$ Args [$ i] = "'". Mysql_escape_string ($ v). "'";

}

/ / На всяк випадок заповнюємо 20 останніх аргументів неприпустимими

/ / Значеннями, щоб у випадку, якщо число "?" перевищує кількість

/ / Параметрів, видавалася помилка SQL-запиту (допоможе при налагодженні).

for ($ i = $ c = count ($ args) -1; $ i <$ c +20; $ i + +)

$ Args [$ i +1] = "UNKNOWN_PLACEHOLDER_ $ i";

/ / Формуємо SQL-запит.

return call_user_func_array ("sprintf", $ args);

}

?>

Функція sprintf () сприймає символ% як керуючий. Щоб скасувати його, необхідно його подвоїти, що і робиться в функції. Потім? замінюється на% s, для sprintf () це означає "взяти черговий строковий аргумент".

Для зручності тестування цього коду головна функція розбита на дві, виділений код заміни знаків підстановки у функцію mysql_make_qw ().

В лістингу 13.2 наведено приклад того, як будуть виглядати SQL-запити після підстановки placeholders.

Лістинг 13.2 - Використання placeholders

<? php

require_once "lib_mysql_qw.php";

require_once "mysql_connect.php";

/ / Уявімо, що ми - хакери ...

$ name = "'OR '1";

/ / Допустимий запит.

echo mysql_make_qw ('DELETE FROM people WHERE name =?', $ name). "<br>";

/ / Неприпустимий запит.

echo mysql_make_qw ('DELETE FROM people WHERE name =? OR?', $ name). "<br>";

/ / Ось як виглядає виконання запиту.

mysql_qw ('DELETE FROM people WHERE name =? OR?', $ name)

or die (mysql_error ());

?>

В результаті роботи скрипта буде згенерована наступна сторінка(рис 13.1):

DELETE FROM people WHERE name = '\' OR \ '1 '

DELETE FROM people WHERE name = '\' OR \ '1' OR id = UNKNOWN_PLACEHOLDER_l

Unknown column 'UNKNOWN_PLACEHOLDER_1' in 'where clause1

Рис 13.1 - Результаті роботи скрипта

Перед апострофами в даних з'явилися Слеш, a placeholder, якому "не вистачило" аргументів функції, виявився заміненим на рядок UNKNOWN_PLACEHOLDER_l.

Тепер будь-яка спроба виконання такого запиту заздалегідь приречена на невдачу (про що говорить останнім діагностичне повідомлення, згенероване викликом die ()), що є важливою підмогою при налагодженні сценаріїв.