PHP: \ "Лапки \". Складання запитів mysql, слеші, екранування лапок.


  • Швидкі рекомендації.
  • Правила складання запитів MySQL
  • Динамічне складання запитів
  • Правильна робота з спецсимволів при складанні запитів
  • підготовлені вираження
  • SQL Injection
  • Особливості роботи з
  • Про Слеш. Як від них позбутися
  • зауваження
  • Примітка: форми
  • Коментарі
  • Швидкі рекомендації.

  • Для запобігання SQL ін'єкцій слід дотримуватися два простих правила:
    1. Не поміщати в БД дані без обробки.
    Це можна зробити або за допомогою підготовлених виразів, або обробляючи параметри вручну.
    Якщо запит залишається вручну, то
    - Все числові параметри повинні бути приведені до потрібного типу
    - Всі інші параметри повинні бути оброблені функцією mysql_real_escape_string () і укладені в лапки.

    2. Не поміщати в запит керуючі структури і ідентифікатори, введені користувачем.
    А заздалегідь прописувати в скрипті список можливих варіантів, і вибирати тільки з них.

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

    Правила складання запитів MySQL
    Для початку - трохи про те, чому взагалі потрібні ці слеші.
    Якщо ми підставляємо в запит будь-які дані, то, щоб відрізнити ці дані від команд SQL, їх треба брати в лапки.
    Наприклад, якщо написати
    SELECT * FROM table WHERE name = Bill
    то база вирішить, що Bill - це ім'я іншого поля, не знайде його, і видасть помилку. Тому що підставляються дані (в даному випадку ім'я Bill) треба брати в лапки - тоді база вважатиме його рядком, значення якої треба привласнити полю name:
    SELECT * FROM table WHERE name = 'Bill'
    Однак, і в самих даних можуть зустрічатися лапки теж. Наприклад,
    SELECT * FROM table WHERE name = 'Д'Артаньян'
    Тут база даних вирішить, що 'Д' - це дані, а Артаньян - команда, яку вона не знає, і теж видасть помилку. Тому і треба прослешівать всі дані, щоб пояснити базі, що зустрічаються в них лапки (і деякі інші спецсимволи) відносяться до даних.
    В результаті ми отримаємо правильний запит, який помилок не викличе:
    SELECT * FROM table WHERE name = 'Д\'Артаньян'

    Таким чином, ми з'ясували, що при підстановці даних в запит, слід дотримуватися двох правил:
    - Все що вставляються в запит дані повинні бути укладені в лапки (одинарні або подвійні, але зручніше і частіше використовуються одинарні).
    - У всіх строкових змінних повинні бути екрановані Слеш спецсимволи.

    Слід спеціально зазначити: додані слеші НЕ йдуть в базу. Вони потрібні тільки в запиті. При попаданні в базу слеші відкидаються. Відповідно, поширеною помилкою є застосування stripslashes при отриманні даних з бази.

    Насправді, все вищесказане відноситься до даних строкового типу і датам. Числа можна вставляти НЕ прослешівая і не окружaя лапками. Якщо ви так робите, то ОБОВ'ЯЗКОВО! насильно приводите дані до потрібного типу перед вставкою в запит, наприклад:
    $id = intval ( $id );
    Однак для простоти (і надійності) можна і з числами працювати, як з рядками (проскольку mysql все одно перетворює їх до потрібного типу). Відповідно, ми будемо будь-які дані, що вставляються в запит, прослешівать і брати в лапки.

    Так само, є ще одне правило - необов'язкове, але його слід дотримуватися, щоб уникнути появи помилок:
    Імена полів і таблиць слід укладати в зворотні одинарні лапки - "` "(клавіша з цим символом знаходиться на стандартній клавіатурі зліва від клавіші" 1 ") Адже ім'я поля може збігатися з ключовими словами mysql, але якщо ми використовуємо зворотний лапки, то MySQL зрозуміє все правильно:
    SELECT * FROM `table` WHERE `date` = '2006-04-04'
    Слід розрізняти ці лапки і не плутати одні з іншими. Слід також пам'ятати, що зворотні лапки Слеш НЕ екрануються.

    Динамічне складання запитів
    Якщо SQL запит в скрипті написаний цілком, і ніяк не змінюється, наприклад
    SELECT * FROM `table`
    то ніяких проблем з ним і не буде.
    Але вся сила наших скриптів саме в динамічному складанні запитів!
    Замість того, щоб писати готові запити на всі випадки життя, ми складаємо їх на підставі вступників в скрипт даних.
    І ось тут нас підстерігає небезпека.
    Припустимо, ми складаємо запит з використанням змінної:
    SELECT * FROM table WHERE name = '$name'
    Начебто - все нормально?
    А якщо $ name у нас буде Д'Артаньян? Запит видасть помилку!
    Тобто, змінну перед підстановкою в запит треба прослешіть.
    Це можна зробити декількома шляхами.
    Найпростіший (і неправильний) - покластися на чарівні лапки. Як ви вже здогадалися, саме для цього випадку вони і були придумані. Заради того, щоб уберегти SQL запити забудькуватих програмістів від помилок, ВСЕ надходять в скрипт дані прослешіваются без розбору.
    Якщо ви використовуєте чужий код, то краще скористатися чарівними лапками. Це може створювати певні незручності і не гарантує вас від помилок або злому (оскільки прослешіваніем правила складання запитів не вичерпуються) але хоча б знижує ризик. Тому, при використанні чужого коду, обов'язково переконайтеся, що чарівні лапки включені.
    Якщо ж ви пишете весь код самостійно, то слід навчитися правильному складанню запитів.

    Правильна робота з спецсимволів при складанні запитів
    Отже. Як ми вже дізналися вище, щоб правильно скласти запит, треба укладати дані в лапки і прослешівать їх.
    З першим все зрозуміло. При складанні динамічних запитів ми ніколи не забуваємо все дані взяти в лапки:
    $query = "INSERT INTO `table` VALUES(NULL,'$name','$date','$price')" ;
    Якщо змінна $ price повинна бути типу int і ми наведемо її до цього типу, то можна її не укладати в лапки. Однак, якщо укладемо, то біди особливої ​​не буде, але зате можна буде зробити роботу з даними однакової.

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

    Спочатку відключимо чарівні лапки. Так, як це описано в самому початку.
    Чому це слід зробити?
    З багатьох причин. Найбільш очевидна - логічна. "Чарівні лапки" додають слеші не там, де вони потрібні - при складанні запиту, а ще до потрапляння в скрипт! Але ж дані зовсім не обов'язково після цього будуть вставлятися в запит. Може бути, їх доведеться виводити користувачеві, і слеші будуть тільки заважати. Плюс до того, додані слеші завадять, наприклад, правильно перевірити довжину введеної рядки. До того ж, прослешівать нам треба не тільки прийшли від користувача дані, а взагалі будь-які, що вставляються в запит - багатьом цей очевидний факт навіть не приходив в голову! Список можна продовжувати, але висновок один: додавати слеші треба не автоматом, без розбору, до початку виконання скрипта, а тільки там, де дійсно треба - при складанні запиту.
    Є і ще одна причина: при використанні кодування Unicode, яка набуває все більшої популярності, а згодом займе домінуюче положення в веб, чарівні лапки можуть просто зіпсувати текст, прийнявши частину мультібайтной рядки за спецсимвол.

    Тепер займемося додаванням слешів самостійно.
    По-перше, для прослешіванія ми скористаємося функцією mysql_real_escape_string()
    Слід пам'ятати, що застосовувати її можна тільки після встановлення з'єднання з базою.
    Ця функція робить набагато більше, ніж застарілі addslashes і mysql_escape_string. По-перше, вона полегшує ведення і читання логів mysql, замінюючи, наприклад, символ перекладу рядка на "\n" і деякі інші символи на escape-послідовності. По-друге, і найголовніше - вона коректно працює з багатобайтові кодуваннями, беручи до уваги поточну кодування MySQL і не псує, таким чином, тексти в кодуванні Unicode.
    По-друге, не забуваймо, що прослешіть треба все ті дані, які ми уклали в запиті в лапки:

    $name
    = mysql_real_escape_string ( $name );
    $age = mysql_real_escape_string ( $age );
    $query = "INSERT INTO table (name,age,class) VALUES ('$name','$age',11)" ;

    або:
    $query = "SELECT * FROM table WHERE name LIKE '" . mysql_real_escape_string ( $_GET [ 'name' ]). "%'" ;
    Видно, що код виходить досить громіздким. Для полегшення складання запитів можна пуститися на різні ухіщненія - зробити функцію для складання запитів з масиву (дуже зручно для запитів типу INSERT і UPDATE), прослешівать масив даних в циклі, і так далі.
    Ви можете написати і свою бібліотеку або функцію для складання запитів.
    Головне - пам'ятати, що тільки неухильне дотримання правил складання запитів гарантує вас від злому БД, а так само усвідомлювати, що застосування "чарівних лапок", при видимої легкості складання запитів, не дає такої гарантії, а тільки заважає нормальній роботі програми.

    підготовлені вираження
    Є ще один спосіб відправляти запити в БД, званий "підготовленими виразами" (prepared statements).
    Суть його полягає в тому, що готується шаблон запиту, зі спеціальними маркерами, на місце яких будуть підставлені динамічні компоненти. Приклад такого шаблону:
    SELECT * FROM table WHERE name=? Знак питання тут - це той самий маркер. По-іншому він називаетсо плейсхолдером (placeholder). Весь секрет у тому, що дані на його місце підставляє спеціальна функція, яка "прив'язує" змінну до запиту.
    Ось як виглядає код в такому випадку:
    $stmt = $mysqli -> prepare ( "SELECT District FROM City WHERE Name=?" );
    $stmt -> bind_param ( "s" , $city );
    $stmt -> execute ();
    У першому рядку ми готуємо шаблон запиту.
    У другій - прив'язуємо до маркера значення змінної $ city.
    У третьому рядку виконуємо підготовлений таким чином запит.
    При цьому запит і дані йдуть в базу не разом, а окремо, виключаючи можливість будь-якої помилки або зловмисної маніпуляції.

    Зрозуміло, що виникає багато питань. Але ні обсяг, ні тематика даної статті не дозволяють зупинитися на них більш докладно. Рекомендую звернутися до документації по бібліотеках mysqli і < PDO , які реалізують даний принцип.
    Так само, можна використовувати бібліотеку DbSimple Дмитра Котеровим або PEAR :: DB. Основна відмінність цих двох полягає в тому, що вони реалізують механізм підготовлених виразів тільки зовні. А всередині працюють по-старому - складаючи запит і відправляючи його в базу, беручи на себе роботу по обробці змінних. А PDO і mysqli працюють, як було описано вище - тобто, шаблон запиту і дані йдуть в базу окремо.

    SQL Injection

    Отже, ми навчилися правильно підставляти в запит дані.
    АЛЕ! Динамічне складання запитів не вичерпується підстановкою даних. Часто нам доводиться підставляти в запит команди SQL і імена полів. І тут ми вже переходимо до теми безпеки:

    SQL Injection - це спосіб хакерської атаки, коли передаються скрипту дані модифікуються таким чином, що запит, що формується в цьому скрипті, починає виконувати зовсім не те, для чого він призначався.
    Правила захисту від таких атак можна розділити на два пункти:
    1. Робота з даними.
    2. Робота з керуючими елементами запиту.

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

    Другий пункт набагато складніше, оскільки не існує такого ж єдиного універсального правила, як для даних - зворотна лапки ніяк не захистить ім'я поля від модифікації хакером. Неможливо лапками захистити ім'я таблиці, оператори SQL, параметри команди LIMIT, і інші оператори.
    Тому основне правило при підстановці керуючих елементів в запит таке:
    Якщо потрібно динамічно підставляти в запит оператори SQL або імена полів, баз даних, таблиць, то ні в якому разі не вставляти їх в запит безпосередньо.
    Всі варіанти таких змін повинні бути ЗАРАНЕЕ прописані у вашому скрипті і вибиратися на підставі того, що ввів користувач.
    Наприклад, якщо треба передати ім'я поля в оператор order by, то ні в якому разі не можна підставляти його безпосередньо. Треба спочатку перевірити його. Наприклад, зробити масив допустимих значень, і підставляти в запит тільки якщо переданий параметр в цьому масиві присутній:
    $orders =array( "name" , "price" , "qty" );
    $key = array_search ( $_GET [ 'sort' ], $orders ));
    $orderby = $orders [ $key ];
    $query = "SELECT * FROM `table` ORDER BY $orderby" ;
    Ми шукаємо в масиві заздалегідь описаних варіантів введене користувачем слово, і, якщо знаходимо, то вибираємо відповідний елемент масиву. Якщо збіги не буде знайдено, то буде обраний перший елемент масиву.
    Таким чином, в запит підставляється не те, що ввів користувач, а то, що було прописано у нас в скрипті.
    Точно так же треба чинити і у всіх інших випадках
    Наприклад, якщо динамічно формується оператор WHERE:
    if (!empty( $_GET [ 'price' ])) $where .= "price='" . mysql_real_escape_string ( $_GET [ 'price' ]). "'" ;
    $query = "SELECT * FROM `table` WHERE $where" ;
    Мені складно уявити собі випадок, коли ім'я таблиці може підставлятися в запит динамічно, але якщо таке трапиться, то ім'я теж треба вставляти тільки з заздалегідь прописаного в скрипті набору.
    Параметри оператора LIMIT слід примусово приводити до целочисленному типу за допомогою арифметичних операцій або функції intval ().
    Не слід думати, що перерахованими тут прикладами вичерпуються всі варіанти динамічного складання запитів. Потрібно просто зрозуміти принцип, і застосовувати його у всіх подібних випадках.

    Особливості роботи з оператором LIKE
    Абсолютно окремий випадок - оператор LIKE.
    По-перше, крім звичайного прослешіванія, в змінних, які підставляються в LIKE, треба подвоювати слеші. Тобто, якщо у змінній міститься символ \, то його треба подвоїти, а після цього виконати звичайне прослешіваніе, через mysql_real_escape_string.
    Наприклад, якщо ми шукаємо рядок
    символ \ називається "backslash" і нам потрібно точний збіг, то ми просто застосовуємо mysql_real_escape_string і запит виходить стандартний:
    SELECT * FROM test WHERE field = 'символ \\ называется \"backslash\"' Якщо ж ми хочемо підставити цей рядок в LIKE, то спочатку треба замінити кожен слеш на два, а потім застосувати mysql_real_escape_string. В результаті вийде
    SELECT * FROM table WHERE field LIKE '% символ \\\\ називається \ "backslash \"%'
    По-друге, слід звернути увагу на те, що жодна з функцій, що додають слеші, що не додає їх до метасимволів пошуку "%" і "_", використовуваним в операторі LIKE. Тому, якщо ви використовуєте цей оператор, і не хочете, щоб символи _ і% використовувалися, як маски, то додавайте слеші вручну. Це можна зробити командою
    $ data = addCslashes ($ data, '% _'); Увага - це не addslashes! В імені цієї функції є додаткова буква "c".

    Таким чином виходить, що змінні, використовувані в операторі LIKE ми повинні обробляти окремо.
    спочатку замінювати один слеш на два, за допомогою такого, наприклад, коду:
    $ var = str_replace ( '\\', '\\\\', $ var); потім (можна нарівні з усіма іншими даними, що йдуть в запит) прослешіваем:
    $var = mysql_real_escape_string ( $var ); а потім, якщо хочемо, щоб _ і% відповідали точно самим собі, робимо
    $var = addCslashes ( $var , '_%' );
    В результаті, якщо ми будемо шукати, наприклад, такий рядок
    символ \ называется "backslash", а символ _ называется "underscore" то після обробки, в запиті вона повинна виглядати так:
    '%символ \\\\ называется \"backslash\", а символ \_ называется \"underscore\" Тобто, слеш, який був в рядку спочатку - почетверити. Решта символів прослешілісь, як зазвичай. Плюс - прослешілся символ підкреслення.

    Про Слеш. Як від них позбутися
    Слеш, або бекслеш, від англійського back slash - зворотна коса риска ( "\"), яка незрозумілим чином раптом сама собою з'являється в ваших змінних. Додається він до деяких спецсимволи, але в основному його помічають через лапок.
    Відбувається це через спеціальних налаштувань PHP, зазвичай включених на хостингу за замовчуванням. Теоретично, ці установки можуть підвищити безпеку скриптів, работаюющіх з БД. Практично ж, від автоматичного додавання слешів часто виходить плутанина і незручність, як при роботі з БД, так і при її відсутності.
    Нижче ми докладно розберемо обидва ці випадки.

    За автоматичне додавання слешів відповідають директиви php.ini, які носять загальну назву "чарівні лапки":
    magic_quotes_gpc і magic_quotes_runtime Якщо включена перша, то PHP автоматично додає слеші до даних, які прийшли від користувача - з POST, GET запитів і кук (а так само - до логіну та паролю, отриманим через HTTP Authorisation).
    Якщо друга, то слеші додаються до даних, отриманим під час виконання скрипта - наприклад, з файлу або бази даних.

    Якщо ви працюєте без бази даних, або ж працюєте з БД правильно (про що буде написано нижче), зайві слеші вам тільки заважають, і від них треба позбавлятися. Простіше і правильніше за все відключити автоматичне додавання, в настройках PHP.
    Це можна зробити або поправивши відповідні директиви в php.ini, якщо у вас є до нього доступ, або створивши в Конєва каталозі сайту файл .htaccess , і додавши в нього рядки
    php_flag magic_quotes_gpc 0
    php_flag magic_quotes_runtime 0

    Якщо відключити таким чином не виходить, то доведеться писати код різного ступеня складності, щоб очистити від слешів вхідні дані. (Втім, якщо ви хочете написати переносний додаток, яке не залежить від налаштувань PHP, то написати його все одно доведеться. І включати окремим блоком на початку ваших скриптів).

    З даними, одержуваними під час роботи, розібратися найпростіше: досить на початку скрипта написати
    set_magic_quotes_runtime(0); Для даних, отриманих від користувача, все набагато складніше. Для цього коду нам потрібно дві функції:
  • перевірити, додав чи PHP, можна за допомогою функції get_magic_quotes_gpc .
  • видаляє слеші функція stripslashes .
    Відповідно, за допомогою першої треба перевірити, і, якщо PHP додав, то перебрати всі вхідні змінні і очистити за допомогою другої.
    Якщо ви працюєте правильно, при register_globals = off , то досить застосувати stripslashes до всіх масивів, що містить дані, що приходять з браузера.
    наприклад, можна включити в усі скрипти сайту ось такий код:
    function strips (& $el ) {
    if (
    is_array ( $el ))
    foreach(
    $el as $k => $v )
    strips ( $el [ $k ]);
    else
    $el = stripslashes ( $el );
    }
    if (
    get_magic_quotes_gpc ()) {
    strips ( $_GET );
    strips ( $_POST );
    strips ( $_COOKIE );
    strips ( $_REQUEST );
    if (isset(
    $_SERVER [ 'PHP_AUTH_USER' ])) strips ( $_SERVER [ 'PHP_AUTH_USER' ]);
    if (isset(
    $_SERVER [ 'PHP_AUTH_PW' ])) strips ( $_SERVER [ 'PHP_AUTH_PW' ]);
    }
    У разі ж неправильні налаштування register_globals прийнятне рішення і зовсім буде знайти важко, тому краще - повторюся - відразу працювати при правильних налаштуваннях.

    зауваження
    • Серед причин, за якими не варто покладатися на "чарівні лапки", є ще одна. Вельми малоймовірна, але все ж. До "чарівним лапок" відноситься насправді не дві директиви, а три. Третя - magic_quotes_sybase . Мало того, що вона замість слеша додає лапки - так вона ще й скасовує дію magic_quotes_gpc. Якщо якимось дивом обидві ці директиви мають статус 'on', то остання не спрацює! Тобто, покладаючись на "чарівні лапки", ми в цьому випадку отримаємо всі принади неправильно складених запитів. Взагалі, чисто теоретично, треба враховувати наявність цієї директиви, оскільки вона підносить ще й такий сюрприз, як ... зміна поведінки функцій addslashes і stripslashes! Якщо magic_quotes_sybase = on , то ці функції починають замість слеша додавати і видаляти одинарні лапки відповідно.
    • Всі наведені приклади стосуються тільки БД Mysql. Конкретні правила складання запитів можуть відрізнятися для інших СУБД, але загальний принцип залишається тим самим:
      • если API для работы с БД или сторонняя библиотека предоставляет специальные функции для составления запросов , и есть возможность их использования, то пользоваться в первую очередь надо ими.
      • если таких функций нет, то следует искать в документации функции экранирования спецсимволов для этой СУБД.

    Примітка: форми
    При выводе value в тегах input форм, слеши не помогают.
    Чтобы текст в таком поле выводился целиком, value надо заключать в кавычки , а к выводимым данным применять функцию htmlspecialchars()
    приклад:
    <input type="text" name="name" value=" <? echo htmlspecialchars ( $name , ENT_QUOTES ) ?> "> <textarea> <? echo htmlspecialchars ( $text , ENT_QUOTES ) ?> </textarea>
    Необходимо так же отметить (хоть это уже совсем не имеет отношения к кавычкам и слешам), что функцию htmlspecialchars следует применять при выводе в браузер вообще ко всем данным, которые получены от непроверенного пользователя. Почему это следует делать, можно почитать в гугле по запросу что такое XSS уязвимость
    by phpfaq.ru