Інжекція SQL, яка охоплює mysql_real_escape_string ()


645

Чи існує можливість ін'єкції SQL навіть при використанні mysql_real_escape_string()функції?

Розглянемо цю вибіркову ситуацію. SQL побудований у PHP так:

$login = mysql_real_escape_string(GetFromPost('login'));
$password = mysql_real_escape_string(GetFromPost('password'));

$sql = "SELECT * FROM table WHERE login='$login' AND password='$password'";

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

Класичні уколи, як це:

aaa' OR 1=1 --

не працюють.

Чи знаєте ви про можливу ін'єкцію, яка потрапила б за PHP-кодом вище?


34
@TentistMaster - я вважаю за краще не давати багатослівних помилок, таких як недійсний пароль користувача / недійсний пароль ... він повідомляє грубим продавцям, що вони мають дійсний ідентифікатор користувача, і це просто пароль, який їм потрібно вгадати
Марк Бейкер

18
Це жахливо з точки зору зручності використання. Іноді ви не можете скористатися своїм основним псевдонімом / ім’ям користувача / електронною адресою і через деякий час забули це, або сайт видалив ваш обліковий запис через бездіяльність. Тоді це дуже дратує, якщо ви продовжуєте пробувати паролі і, можливо, навіть заблокуєте ваш IP-адресу, навіть якщо це недійсне лише ваше ім'я користувача.
ThiefMaster

50
Будь ласка, не використовуйте mysql_*функції в новому коді . Вони більше не підтримуються, і процес депресації розпочався на ньому. Бачите червоний ящик ? Дізнайтесянатомістьпро підготовлені заяви , а також використовуйте PDO або MySQLi - ця стаття допоможе вам вирішити, які саме. Якщо ви вибрали PDO, ось хороший підручник .
tereško

13
@machineaddict, оскільки 5.5 (який нещодавно був випущений) mysql_*функції вже створюють E_DEPRECATEDпопередження. ext/mysqlРозширення не підтримується в протягом більше 10 років. Ти справді такий брехливий?
tereško

13
@machineaddict Вони просто видалили це розширення на PHP 7.0, і це ще не 2050 рік.
GGG

Відповіді:


379

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

$iId = mysql_real_escape_string("1 OR 1=1");    
$sSql = "SELECT * FROM table WHERE id = $iId";

mysql_real_escape_string()не захистить вас від цього. Те, що ви використовуєте одиничні лапки ( ' ') навколо змінних всередині вашого запиту, - це те, що захищає вас від цього. Нижче також є варіант:

$iId = (int)"1 OR 1=1";
$sSql = "SELECT * FROM table WHERE id = $iId";

9
Але це не буде справжньою проблемою, тому mysql_query()що не виконує кілька заяв, ні?
Pekka

11
@Pekka, Хоча звичайний приклад DROP TABLE, але на практиці зловмисник частіше SELECT passwd FROM users. В останньому випадку другий запит зазвичай виконується за допомогою UNIONпункту.
Жакко

58
(int)mysql_real_escape_string- це не має сенсу. Це зовсім не відрізняється від (int). І вони дадуть однаковий результат для кожного входу
zerkms

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

11
@ircmaxell, та все ж відповідь є абсолютно оманливою. Очевидно, що питання ставиться про зміст у цитатах. "Цитат немає" - це не відповідь на це питання.
Pacerier

629

Коротка відповідь - так, так, є спосіб обійтиmysql_real_escape_string() .

За дуже ОБОВ'ЯЗКУВАННЯ ОБРАЗІВ З ОБРАЗЮ !!!

Довга відповідь не така проста. Він заснований на нападах, продемонстрованих тут .

Атака

Отже, почнемо, показуючи атаку ...

mysql_query('SET NAMES gbk');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

За певних обставин це поверне більше 1 ряду. Давайте розберемо, що тут відбувається:

  1. Вибір набору символів

    mysql_query('SET NAMES gbk');

    Щоб ця атака спрацювала, нам потрібно кодування, яке сервер очікує на з'єднання як для кодування, 'так і в ASCII, тобто, 0x27 і мати якийсь символ, кінцевим байтом якого є ASCII, \тобто 0x5c. Як з'ясовується, є 5 таких кодувань , підтримуваних в MySQL 5.6 за замовчуванням: big5, cp932, gb2312, gbkі sjis. Ми виберемо gbkтут.

    Тепер дуже важливо відзначити використання SET NAMESтут. Це встановлює набір символів НА СЕРВЕРІ . Якби ми використовували дзвінок до функції API API mysql_set_charset(), нам було б добре (у релізах MySQL з 2006 року). Але більше про те, чому за хвилину ...

  2. Корисний вантаж

    Корисне навантаження, яке ми будемо використовувати для цього введення, починається з послідовності байтів 0xbf27. В gbk, це недійсний багатобайтовий символ; в latin1, це рядок ¿'. Зверніть увагу , що в latin1 і gbk , 0x27само по собі є буквальним 'характер.

    Ми вибрали це корисне навантаження, оскільки, якби ми зателефонували addslashes()на нього, ми вставимо ASCII, \тобто 0x5cперед 'символом. Таким чином, ми закінчилися 0xbf5c27, що gbkє послідовністю двох символів: 0xbf5cдалі 0x27. Або іншими словами, дійсний символ, за яким слідує непризначений '. Але ми не використовуємо addslashes(). Тож до наступного кроку ...

  3. mysql_real_escape_string ()

    Виклик API API mysql_real_escape_string()відрізняється від того, addslashes()що він знає набір символів з'єднання. Таким чином, він може виконати втечу належним чином для набору символів, якого очікує сервер. Однак до цього часу клієнт вважає, що ми все ще використовуємо latin1для з'єднання, оскільки ми ніколи не говорили про це інакше. Ми сказали серверу, який ми використовуємо gbk, але клієнт все ще вважає, що це latin1.

    Тому заклик mysql_real_escape_string()вставляти звороту косу рису, і ми маємо вільний висячий 'персонаж у нашому "униклому" вмісті! Справді, якби ми повинні були дивитися на $varв gbkнаборі символів, ми бачимо:

    縗 'АБО 1 = 1 / *

    А саме цього вимагає атака.

  4. Запит

    Ця частина є лише формальністю, але ось наданий запит:

    SELECT * FROM test WHERE name = '縗' OR 1=1 /*' LIMIT 1

Вітаємо, щойно ви успішно атакували програму, використовуючи mysql_real_escape_string()...

Поганий

Погіршується. PDOза замовчуванням для емуляції підготовлених операторів з MySQL. Це означає, що на стороні клієнта, він в основному робить спринт через mysql_real_escape_string()(у бібліотеці С), що означає, що наступне призведе до успішного введення:

$pdo->query('SET NAMES gbk');
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));

Тепер варто зауважити, що ви можете запобігти цьому, відключивши емульовані підготовлені заяви:

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

Це зазвичай призводить до істинного підготовлену заяву (тобто дані, посланого через в окремому пакеті з запиту). Однак майте на увазі, що PDO буде мовчки відновлюватись до емуляції висловлювань, які MySQL не може самостійно підготувати: ті, які вони можуть бути перелічені в посібнику, але будьте обережні, щоб вибрати відповідну версію сервера).

Потворний

Я на самому початку сказав, що ми могли б перешкодити усьому цьому, якби використали mysql_set_charset('gbk')замість цього SET NAMES gbk. І це правда, якщо ви використовуєте реліз MySQL з 2006 року.

Якщо ви використовуєте більш ранню версію MySQL, потім помилку в mysql_real_escape_string()вигляді , що неприпустимі символи мультибайтних , такі як в наших корисних навантаженнях розглядалися як окремі байти для втечі цілей , навіть якщо клієнт був правильно поінформований про кодування з'єднання і тому ця атака буде все-таки досягти успіху. Помилка була виправлена ​​в MySQL 4.1.20 , 5.0.22 та 5.1.11 .

Але найгірше те, що PDOне виставляв API C mysql_set_charset()до 5.3.6, тому в попередніх версіях він не може запобігти цій атаці для кожної можливої ​​команди! Зараз це відкрито як параметр DSN .

Збережуюча благодать

Як ми говорили на початку, для цієї атаки спрацьовує з'єднання з базою даних, використовуючи вразливий набір символів. неutf8mb4 є вразливим, але все ж може підтримувати кожен символ Unicode: ви можете використовувати це замість цього, але він доступний лише з MySQL 5.5.3. Альтернативою є utf8, яка також не є вразливою і може підтримувати всю багатомовну площину Unicode Basic .

Крім того, ви можете ввімкнути NO_BACKSLASH_ESCAPESрежим SQL, який (серед іншого) змінює роботу mysql_real_escape_string(). Якщо цей режим увімкнутий, 0x27він буде замінений, 0x2727а не, 0x5c27і, таким чином, процес виходу не може створити дійсні символи в жодному з уразливих кодувань, де вони не існували раніше (тобто 0xbf27є і 0xbf27т. Д.) - тому сервер все одно буде відкидати рядок як недійсний . Однак див. Відповідь @ eggyal щодо іншої вразливості, яка може виникнути при використанні цього режиму SQL.

Безпечні приклади

Наступні приклади безпечні:

mysql_query('SET NAMES utf8');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

Тому що сервер очікує utf8...

mysql_set_charset('gbk');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

Тому що ми правильно встановили набір символів таким чином, щоб клієнт та сервер відповідали.

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->query('SET NAMES gbk');
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));

Тому що ми вимкнули наслідувані підготовлені заяви.

$pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=gbk', $user, $password);
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));

Тому що ми правильно встановили набір символів.

$mysqli->query('SET NAMES gbk');
$stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$param = "\xbf\x27 OR 1=1 /*";
$stmt->bind_param('s', $param);
$stmt->execute();

Тому що MySQLi весь час робить дійсно підготовлені заяви.

Підведенню

Якщо ви:

  • Використовуйте сучасні версії MySQL (кінець 5.1, усі 5.5, 5.6 тощо) І mysql_set_charset() / $mysqli->set_charset()/ PDO параметр DSN-діаграми (в PHP ≥ 5.3.6)

АБО

  • Не використовуйте вразливий набір символів для кодування з'єднання (ви використовуєте лише utf8/ latin1/ ascii/ тощо)

Ви на 100% безпечні.

Інакше ви вразливі, навіть якщо використовуєтеmysql_real_escape_string() ...


3
PDO емуляція готувати заяви для MySQL, насправді? Я не бачу жодної причини, чому це зробить так, оскільки драйвер підтримує це на самоті. Ні?
netcoder

16
Це робить. Кажуть, що в документації цього немає. Але у вихідному коді це чітко видно і легко виправити. Я крейдую це до некомпетентності дияволів.
Теодор Р. Сміт

5
@ TheodoreR.Smith: Виправити це не так просто. Я працював над тим, щоб змінити типовий параметр, але це не дає навантажувальному коду тесту при перемиканні. Тож це більша зміна, ніж здається. Я все ще сподіваюся закінчити його до 5,5 ...
ircmaxell

14
@shadyyx: Ні, вразливість, про яку розповідала стаття addslashes. Я грунтувався на цій вразливості. Спробуйте самі. Перейдіть на MySQL 5.0 і запустіть цей подвиг і переконайтеся самі. Щодо того, як це вкласти в PUT / GET / POST, це ТРІВІАЛЬНО. Вхідні дані - це просто байтові потоки. char(0xBF)це просто читаний спосіб генерації байта. Я демонстрував цю вразливість в прямому ефірі перед кількома конференціями. Повірте мені з цього приводу ... Але якщо ви цього не зробите, спробуйте самі. Працює ...
ircmaxell

5
@shadyyx: Що стосується передачі такої функціональності в $ _GET ... ?var=%BF%27+OR+1=1+%2F%2Aв URL-адресі, $var = $_GET['var'];в коді, а Боб - ваш дядько.
cHao

183

TL; DR

mysql_real_escape_string()не забезпечить ніякого захисту (і може додатково об'єднати ваші дані), якщо:

  • NO_BACKSLASH_ESCAPESУвімкнено режим SQL MySQL (який він може бути, якщо явно не вибирати інший режим SQL кожен раз при підключенні ); і

  • ваші літеральні рядки SQL цитуються за допомогою "символів з подвійним цитуванням .

Це було подано як помилка № 72458 і було виправлено у MySQL v5.7.6 (див. Розділ під заголовком " Збереження благодаті ", наведений нижче).

Це ще один, (можливо, менший?) Незрозумілий випадок EDGE !!!

В знак поваги до відмінної відповіді @ ircmaxell (насправді, це має бути лестощі, а не плагіат!), Я прийму його формат:

Атака

Починаючи з демонстрації ...

mysql_query('SET SQL_MODE="NO_BACKSLASH_ESCAPES"'); // could already be set
$var = mysql_real_escape_string('" OR 1=1 -- ');
mysql_query('SELECT * FROM test WHERE name = "'.$var.'" LIMIT 1');

Це поверне всі записи з testтаблиці. Розсічення:

  1. Вибір режиму SQL

    mysql_query('SET SQL_MODE="NO_BACKSLASH_ESCAPES"');

    Як задокументовано в String Literals :

    Існує кілька способів включення символів цитати в рядок:

    • " '" Всередині рядка, цитованого " '", може бути записано як " ''".

    • " "" Всередині рядка, цитованого " "", може бути записано як " """.

    • Попередьте символ цитування символом втечі (" \").

    • " '" Всередині рядка, яке посилається на " "", не потребує спеціального оброблення і не повинно бути подвоєним або уникальним. Таким же чином " "" всередині рядка, цитованого " '", не потребує спеціального лікування.

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

  2. Корисний вантаж

    " OR 1=1 -- 

    Корисне навантаження ініціює цю ін'єкцію досить буквально з "персонажем. Немає конкретного кодування. Немає спеціальних символів. Ніяких дивних байтів.

  3. mysql_real_escape_string ()

    $var = mysql_real_escape_string('" OR 1=1 -- ');

    На щастя, mysql_real_escape_string()перевіряє режим SQL і відповідно коригує його поведінку. Дивіться libmysql.c:

    ulong STDCALL
    mysql_real_escape_string(MYSQL *mysql, char *to,const char *from,
                 ulong length)
    {
      if (mysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES)
        return escape_quotes_for_mysql(mysql->charset, to, 0, from, length);
      return escape_string_for_mysql(mysql->charset, to, 0, from, length);
    }

    Таким чином escape_quotes_for_mysql(), якщо використовується NO_BACKSLASH_ESCAPESрежим SQL , викликається інша основна функція . Як було сказано вище, така функція повинна знати, який символ буде використовуватися для цитування літералу, щоб повторити його, не викликаючи повторного повторення іншого символу цитати.

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

    /*
      Escape apostrophes by doubling them up
    
    // [ deletia 839-845 ]
    
      DESCRIPTION
        This escapes the contents of a string by doubling up any apostrophes that
        it contains. This is used when the NO_BACKSLASH_ESCAPES SQL_MODE is in
        effect on the server.
    
    // [ deletia 852-858 ]
    */
    
    size_t escape_quotes_for_mysql(CHARSET_INFO *charset_info,
                                   char *to, size_t to_length,
                                   const char *from, size_t length)
    {
    // [ deletia 865-892 ]
    
        if (*from == '\'')
        {
          if (to + 2 > to_end)
          {
            overflow= TRUE;
            break;
          }
          *to++= '\'';
          *to++= '\'';
        }

    Отже, "символи подвійної цитати залишають недоторканими (і подвоює всі символи з одною цитатою ') незалежно від реального символу, який використовується для цитування буквального ! У нашому випадку $varзалишається точно такими ж , як аргумент , який був наданий mysql_real_escape_string()-it як ніби не минути не відбулося взагалі .

  4. Запит

    mysql_query('SELECT * FROM test WHERE name = "'.$var.'" LIMIT 1');

    Щось формальність, наданий запит:

    SELECT * FROM test WHERE name = "" OR 1=1 -- " LIMIT 1

Як сказав мій знайомий друг: вітаю, ви просто вдало атакували програму, використовуючи mysql_real_escape_string()...

Поганий

mysql_set_charset()не може допомогти, оскільки це не має нічого спільного з наборами символів; також не можна mysqli::real_escape_string(), оскільки це просто інша обгортка навколо цієї ж функції.

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

Потворний

Погіршується. NO_BACKSLASH_ESCAPESможе бути не все, що в дикій природі внаслідок необхідності його використання для сумісності зі стандартним SQL (наприклад, див. розділ 5.3 специфікації SQL-92 , а саме - <quote symbol> ::= <quote><quote>граматичне виробництво та відсутність будь-якого особливого значення, яке надається зворотній косої риси). Крім того, його використання було явно рекомендовано як вирішення проблеми (давно виправленої) помилки, яку описує публікація ircmaxell. Хто знає, деякі DBA можуть навіть налаштувати його за замовчуванням, як заважає використовувати неправильні методи, такі як addslashes().

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

Збережуюча благодать

Поки ви завжди чітко встановлюєте режим SQL не включати NO_BACKSLASH_ESCAPESабо цитувати літеральні рядки MySQL за допомогою символу з однією цитатою, ця помилка не може змінити свою некрасиву голову: відповідно escape_quotes_for_mysql()не буде використовуватися, або її припущення про те, які символи цитат вимагають повторення буде бути правильним.

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

У PDO PDO::quote()закликають як його еквівалентну функцію, так і підготовлений емулятор висловлювань mysql_handle_quoter()- що робить саме це: він гарантує, що уникнутий літерал цитується в одних лапках, тож ви можете бути впевнені, що PDO завжди захищений від цієї помилки.

Станом на MySQL v5.7.6, ця помилка була виправлена. Переглянути журнал змін :

Функціональність додана чи змінена

  • Несумісні зміни: Нова функція C API,mysql_real_escape_string_quote()була реалізована як заміна,mysql_real_escape_string()оскільки остання функція не може правильно кодувати символи, колиNO_BACKSLASH_ESCAPESвключений режим SQL. У цьому випадкуmysql_real_escape_string()не вдається уникнути символів цитат, окрім подвоєння їх, і щоб це зробити належним чином, він повинен знати більше інформації про контекст цитування, ніж доступно. mysql_real_escape_string_quote()бере додатковий аргумент для визначення контексту цитування. Детальну інформацію про використання див. У розділі mysql_real_escape_string_quote () .

     Примітка

    Програми повинні бути модифіковані для використання mysql_real_escape_string_quote(), замість того mysql_real_escape_string(), що тепер виходить з ладу і створює CR_INSECURE_API_ERRпомилку, якщо NO_BACKSLASH_ESCAPESввімкнено.

    Список літератури: Дивіться також помилку № 19211994.

Безпечні приклади

Наступні приклади разом із помилкою, поясненою ircmaxell, є повністю безпечними (якщо припустити, що ми використовуємо MySQL пізніше 4.1.20, 5.0.22, 5.1.11; або кодування не використовує кодування з'єднання GBK / Big5) :

mysql_set_charset($charset);
mysql_query("SET SQL_MODE=''");
$var = mysql_real_escape_string('" OR 1=1 /*');
mysql_query('SELECT * FROM test WHERE name = "'.$var.'" LIMIT 1');

... тому що ми явно вибрали режим SQL, який не включає NO_BACKSLASH_ESCAPES.

mysql_set_charset($charset);
$var = mysql_real_escape_string("' OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

... тому що ми цитуємо наш рядковий буквал одинарними лапками.

$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(["' OR 1=1 /*"]);

... оскільки PDO підготовлені заяви захищені від цієї вразливості (і ircmaxell теж, за умови, що ви використовуєте PHP≥5.3.6 і набір символів був правильно встановлений в DSN; або що емуляція підготовленого оператора була вимкнена) .

$var  = $pdo->quote("' OR 1=1 /*");
$stmt = $pdo->query("SELECT * FROM test WHERE name = $var LIMIT 1");

... тому що quote()функція PDO не тільки уникає прямого, але й цитує його (в одноцитатних 'символах); зауважте, що щоб уникнути помилки ircmaxell в цьому випадку, ви повинні використовувати PHP≥5.3.6 і правильно встановити набір символів у DSN.

$stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$param = "' OR 1=1 /*";
$stmt->bind_param('s', $param);
$stmt->execute();

... тому що підготовлені заяви MySQLi є безпечними.

Підведенню

Таким чином, якщо ви:

  • використовувати підготовлені висловлювання

АБО

  • використовувати MySQL v5.7.6 або новішої версії

АБО

  • на додаток до використання одного із рішень у резюме ircmaxell, використовуйте принаймні одне з:

    • PDO;
    • одноцитовані рядкові літерали; або
    • явно встановлений режим SQL, який не включає NO_BACKSLASH_ESCAPES

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


10
Отже, TL; DR виглядатиме так: "існує серверний режим NO_BACKSLASH_ESCAPES mysql, який може викликати ін'єкцію, якщо ви не використовуєте єдиних лапок.
Ваше загальне розуміння,

Я не можу отримати доступ до bugs.mysql.com/bug.php?id=72458 ; Я просто отримую сторінку з забороненим доступом. Це приховується від громадськості через те, що є проблемою безпеки? Крім того, чи правильно я розумію з цієї відповіді, що ви відкривач вразливості? Якщо так, вітаю.
Марк Амері

1
Люди "в першу чергу не повинні використовувати для рядків. SQL каже, що це для ідентифікаторів. Але так ... лише черговий приклад MySQL, який говорить: "гвинтові стандарти, я буду робити все, що мені хочеться" (На щастя, ви можете включити ANSI_QUOTESв режим виправлення порушеності котирування. Однак, відкрите нехтування стандартами є більшою проблемою, яка може зажадати більш жорстких заходів.)
cHao

2
@DanAllen: моя відповідь була дещо ширшою, тому що ви можете уникнути цієї конкретної помилки завдяки quote()функції PDO, але підготовлені заяви є набагато безпечнішим і правильнішим способом уникнути ін'єкції взагалі. Звичайно, якщо у вашому SQL безпосередньо пов'язані незмінені змінні, ви, безумовно, вразливі до ін'єкцій, незалежно від того, якими методами ви користуєтесь далі.
eggyal

1
@eggyall: Наша система спирається на 2-й безпечний приклад вище. Є помилки, коли mysql_real_escape_string опущено. Виправлення тих, хто перебуває в аварійному режимі, представляється доцільним шляхом, сподіваючись, що ми не будемо виправлені до виправлень. Моє обґрунтування - перетворення підготовлених заяв буде набагато довшим процесом, який повинен буде відбутися після. Чи є підготовлена ​​заява безпечнішою тим, що помилки не створюють уразливості? Іншими словами, чи правильно реалізований другий приклад вище є настільки ж безпечним, як і підготовлені заяви?
DanAllen

18

Що ж, насправді нічого такого не може пройти, крім %підстановки. Це може бути небезпечно, якщо ви використовуєте LIKEоператор як зловмисник, %якщо ви не відфільтруєте це, ви можете поставити так само логін, і вам доведеться просто встановити пароль будь-якого з ваших користувачів. Люди часто пропонують використовувати підготовлені висловлювання, щоб зробити його на 100% безпечним, оскільки дані не можуть перешкоджати самому запиту таким чином. Але для таких простих запитів, мабуть, було б ефективніше зробити щось подібне$login = preg_replace('/[^a-zA-Z0-9_]/', '', $login);


2
+1, але символи підстановки - це пункт LIKE, а не проста рівність.
Др

7
Якою мірою ви вважаєте просту заміну, more efficientніж використання підготовлених операторів? (Підготовлені заяви завжди працюють; бібліотеку можна швидко виправити у разі нападів, не викриває помилку людини [наприклад, неправильне введення повної рядки заміни] та має значні переваги від продуктивності, якщо оператор буде повторно використаний.)
MatBailie

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

2
@cHao, моя пропозиція стосується лише входу в систему. Очевидно, що вам не потрібно фільтрувати паролі, вибачте, це не чітко зазначено у моїй відповіді. Але насправді це може бути хорошою ідеєю. Використовувати "камінь у просторі невідомого дерева" замість важко запам’ятовуваного та типу "a4üua3! @V \" ä90; 8f "було б набагато важче для грубої сили. Навіть використовуючи словник, скажіть 3000 слів, щоб допомогти вам, знаючи ви вживали рівно 4 слова - це все одно буде приблизно 3,3 * 10 ^ 12 комбінацій. :)
Slava

2
@ Слава: Я вже бачив цю ідею; див. xkcd.com/936 . Проблема в тому, що математика не зовсім це витримує. Ваш приклад 17-char пароль мав би 96 ^ 17 можливостей, і це якщо ви забули umlauts та обмежилися для друку ASCII. Це приблизно 4,5х10 ^ 33. Ми говоримо буквально в мільярд трильйонів разів більше роботи з грубою силою. Навіть 8-char ASCII пароль матиме 7.2x10 ^ 15 можливостей - в 3 тисячі разів більше.
cHao
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.