Чи потрібно захищатись від введення SQL, якщо я використовував випадаючий список?


96

Я розумію, що НІКОЛИ не слід довіряти введенню користувачем форми, головним чином через можливість введення SQL.

Однак, чи стосується це також форми, де єдиним входом є спадне меню (див. Нижче)?

Я зберігаю $_POST['size']файл у сеансі, який потім використовується на всьому сайті для запитів до різних баз даних (із mysqliзапитом Select), і будь-яка ін’єкція SQL, безумовно, завдасть їм шкоди (можливо, знизить).

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

<form action="welcome.php" method="post">
<select name="size">
  <option value="All">Select Size</option> 
  <option value="Large">Large</option>
  <option value="Medium">Medium</option>
  <option value="Small">Small</option>
</select>
<input type="submit">
</form>

112
Так. Ніщо не заважає зловмисникові подати будь-які значення, які вони бажають, у ваші <select>дані. Справді, навіть трохи технічний користувач міг додати додаткові опції за допомогою консолі браузера. якщо ви зберігаєте білий список масиву доступних значень і порівнюєте вхідні дані з ним, ви можете пом'якшити це (і потрібно, бо це запобігає небажаним значенням)
Майкл Берковський,

13
Ви повинні розуміти основні речі про запит / відповідь і те, що не має значення, як побудований інтерфейс над запитом, тобто в цьому випадку випадаюче меню
Royal Bg

13
@YourCommonSense Оскільки це гарне запитання. Не всі усвідомлюють, наскільки клієнтом можна керувати. Це спровокує дуже цінні відповіді на цьому сайті.
Кранчер

8
@Cruncher я бачу. Для пересічного стакверфлоуія це ракетна наука, про яку вони чули раніше. Навіть незважаючи на найголосніше запитання під тегом PHP.
Ваш здоровий глузд

9
Msgstr "Я розумію, що ви НІКОЛИ не повинні довіряти введенню користувачем". Немає винятків.
Принцхорн,

Відповіді:


69

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

$possibleOptions = array('All', 'Large', 'Medium', 'Small');

if(in_array($_POST['size'], $possibleOptions)) {
    // Expected
} else {
    // Not Expected
}

Потім використовуйте mysqli_ *, якщо ви використовуєте версію php> = 5.3.0, якою ви мали б бути, для збереження результату. При правильному використанні це допоможе у введенні sql.


це скорочена версія "білого списку" OliverBS?
Tatters

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

9
Тим не менш, ви повинні використовувати Підготовлені заяви (рекомендовані) або mysqli_real_escape_string. Також належним чином уникайте значень при їх виведенні (наприклад, використовуйте htmlspecialchars () у документі HTML).
ComFreek

4
Я пропоную встановити третій параметр in_arrayна trueдля суворого порівняння. Я не впевнений, що може піти не так, але вільне порівняння досить химерне.
Brilliand

1
Значення @OliverBS $ _POST також можуть бути масивами. Числові рядки порівнюються як числа ('5' == '05'). Я не думаю, що у вашому конкретному прикладі є отвір у безпеці, але правила складні, і діри можуть відкритися з причин, які я навіть не розумію. Про суворе порівняння легше міркувати, а отже, легше використовувати безпечно.
Brilliand

195

Так, вам потрібно захиститися від цього.

Дозвольте мені показати вам, чому, використовуючи консоль розробника Firefox:

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

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

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

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

curl --data "size=%27%29%3B%20DROP%20TABLE%20*%3B%20--"  http://www.example.com/profile/save

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

Отже, я ще раз повторю:

НІКОЛИ не довіряйте вводу користувача. ЗАВЖДИ захищайся.

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


24
Не кажучи вже про створення спеціального корисного навантаження за допомогою curl. ЗАВЖДИ дезінфікуйте сервер вводу !
рекурсія.

14
Зображення говорить більше тисячі слів.
davidkonrad

4
Це має бути відповідь за замовчуванням. Також слід включити щось про curl. Люди не розуміють, що ви можете надіслати HTTP-запит з будь-якого місця в будь-яке місце, використовуючи будь-який формат і передаючи будь-які значення. Сервер повинен переконатися, що запит є дійсним перед його обробкою.
retrohacker

2
Як мило! Це маленькі столи Боббі!
Аура

45

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

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

Незалежно від HTML-матеріалів!
Важливо розуміти, що запити SQL повинні бути правильно відформатовані, незалежно від будь-яких зовнішніх факторів, будь то введення HTML або щось інше.

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

Тут ви можете знайти ґрунтовне пояснення, чому підготовлені виписки є обов’язковими, і як їх правильно використовувати, а також де вони не застосовуються і що робити в такому випадку: Посібник автостопа щодо захисту від ін’єкцій SQL

Крім того, це питання було позначено тегами . Переважно випадково, я припускаю, але в будь-якому випадку, я повинен попередити вас, що сирий mysqli не є адекватною заміною старих функцій mysq_ * . Просто тому, що якщо використовувати його у старому стилі, це зовсім не додасть жодної безпеки. Незважаючи на те, що підтримка підготовлених висловлювань є болючою і клопітною, до того, що пересічний користувач PHP взагалі не може їх докласти. Таким чином, якщо жоден ORM чи якась бібліотека абстракції не є опцією, тоді PDO - це ваш єдиний вибір.


5
i = випадковий (0, 15); // деякий запит за допомогою i. Чи потрібно мені тут ще готувати заяву?
Кранчер

2
В чому реалізація random?
Slicedpan

9
@YourCommonSense вузький перегляд? Для мене "Завжди роби X, і я не буду міркувати про це" є вузьким поглядом.
Кранчер

5
@Cruncher Я не можу придумати жодної причини, щоб не завжди використовувати підготовлені висловлювання, кожен раз, для всього, завжди. Ви можете сказати мені одну?
Wesley Murch

3
@Cruncher З чим я погоджуюсь, просто здається, що ти граєш у Devil's Advocate. Відповідь також передбачає "залучення будь-яких змінних даних" . Є кілька випадків, таких як PHP int casting, але, мабуть, краще просто використовувати підготовлені твердження. Говорити (недосвідченим) людям про те, що незначний "приріст" продуктивності важливіший, ніж безпека, є далеко не так. Відповідь "неповна", але вона надсилає сильний сигнал, який інші ігнорують. Я відпочину у своїй справі.
Wesley Murch

12

Так.

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

ТАК, для перевірки випадаючих меню ви можете просто перевірити, щоб переконатися, що значення, з яким ви працюєте, було у випадаючому списку - щось подібне було б найкращим (найбільш розумним параноїчним) способом:

if(in_array($_POST['ddMenu'], $dropDownValues){

    $valueYouUseLaterInPDO = $dropDownValues[array_search("two", $arr)];

} else {

    die("effin h4x0rs! Keep off my LAMP!!"); 

}

5
Ця відповідь неповна, додатково слід використовувати підготовлені твердження, щоб ін'єкція була неможливою незалежно від того, для якого значення встановлено значення
Slicedpan

7
@Slicedpan Справді? Це звучить так, ніби ти стрибаєш, не знаючи чому ... Якщо у мене є кілька можливих введень у запит, таких, що я можу переконатися, що з ними все гаразд (що я знаю, бо я їх зробив), тоді ви не накопичуєте ніяких додаткових переваг для безпеки, використовуючи підготовлену заяву
Кранчер

3
За винятком того, що примусове використання підготовлених операторів як домовленість захищає вас від впровадження вразливих місць введення SQL у майбутньому.
Slicedpan

1
@Cruncher Даючи обіцянку "всі значення у спадному меню завжди будуть у безпеці" - це дуже небезпечна обіцянка. Значення змінюються, код не обов'язково оновлюється. Той, хто оновлює значення, може навіть не знати, що таке небезпечне значення! Особливо у сфері веб-програмування, яке багате на всілякі проблеми безпеки, скупитися на щось подібне просто безвідповідально (та інші менш приємні слова).
Гайд

1
@Cruncher Якщо ви правильно обробляєте свої матеріали SQL, ви можете приймати будь-які вхідні дані, не втручаючись у належну роботу SQL. Частина SQL повинна бути підготовлена ​​і готова приймати все, незалежно від того, звідки вона береться. Все інше схильне до помилок.
glglgl

8

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

<?php
// No, you don't need to specify the numbers in the array but as we're using them I always find having them visually there helpful.
$sizes = array(0 => 'All', 1 => 'Large', 2 => 'Medium', 3 => 'Small');
$size = filter_input(INPUT_POST, "size", FILTER_VALIDATE_INT);

echo '<select name="size">';
foreach($sizes as $i => $s) {
    echo '<option value="' . $i . '"' . ($i == $size ? ' selected' : '') . '>' . $s . '</option>';
}
echo '</select>';

Тоді ви можете використовувати $sizeу своєму запиті, знаючи, що він коли-небудь міститиме FALSEабо ціле число.


2
@OliverBS Що робити, filter_inputякщо не чек на стороні сервера? Ніхто не може розміщувати нічого, крім цілого числа.
Стифон

Дякую Стифону, отже, це замінює форму в ОП? І чи можна це застосувати до великих форм із кількома спадними меню? Якби я додав до нього ще одне спадне меню, щоб сказати "колір"?
Tatters

@SamuelTattersfield Так і так. Ви можете використовувати це для будь-якої кількості випадаючих меню, скільки завгодно, із скільки завгодно варіантів. Ви просто створюєте новий масив для кожного випадаючого списку і вводите всі параметри випадаючого списку в масиві.
Стифон

Приємно! Мені це подобається. Я спробую і подивлюсь, що я отримаю при тестуванні.
Tatters

@SamuelTattersfield Чудово, просто не забудьте використати цю filter_inputчастину також для перевірки, інакше це так само корисно, як шоколадний чайник для безпеки.
Стифон

7

Інші відповіді вже охоплюють те, що вам потрібно знати. Але, можливо, це допоможе пояснити ще:

Є ДВА РЕЧІ, які вам потрібно зробити:

1. Перевірити дані форми.

Як відповідь Джонатана Хоббса показує дуже чітко, вибір елемента html для введення форми не робить для вас жодної надійної фільтрації.

Зазвичай перевірка виконується таким чином, що не змінює дані, але знову показує форму, поля з позначкою "Будь ласка, виправте це".

У більшості фреймворків та CMS є конструктори форм, які допоможуть вам у виконанні цього завдання. І не лише це, вони також допомагають проти CSRF (або "XSRF"), що є іншою формою атаки.

2. Дезінфекція / втеча змінних у операторах SQL ..

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

Якщо ви створюєте оператор (My) SQL із будь-якими змінними, наданими користувачем чи ні, вам потрібно вникнути та вказати ці змінні.

Як правило, будь-яка така змінна, яку ви вставляєте в оператор MySQL, повинна бути або рядком, або чимось, що PHP можна надійно перетворити на рядок, який MySQL може засвоїти. Такі як цифри.

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

  • У старій школі MySQL + PHP mysql_real_escape_string () виконує свою роботу. Проблема в тому, що це занадто легко забути, тому вам слід абсолютно використовувати підготовлені виписки або конструктори запитів.
  • У MySQLi ви можете використовувати підготовлені оператори.
  • Більшість фреймворків та CMS надають конструктори запитів, які допоможуть вам у виконанні цього завдання.

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

Важливо зазначити, що ви уникаєте змінних для оператора SQL, а НЕ для самої бази даних . База даних буде зберігати вихідний рядок, але для оператора потрібна екранована версія.

Що станеться, якщо пропустити одне з них?

Якщо ви не використовуєте перевірку форми , але дезінфікуєте свої дані SQL, ви можете бачити всілякі погані речі, але ви не побачите введення SQL! (*)

По-перше, він може перевести вашу заявку в стан, який ви не планували. Наприклад, якщо ви хочете розрахувати середній вік усіх користувачів, але один користувач вказав "aljkdfaqer" для віку, ваш розрахунок не вдасться.

По-друге, можуть бути всілякі інші ін'єкційні атаки, які вам слід враховувати: Наприклад, користувацькі дані можуть містити javascript чи інше.

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

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

(*) або це було б щось справді екзотичне.

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

По-перше, ви ризикуєте, що коли ви зберігаєте дані в базі даних і завантажуєте їх знову, це вже не будуть ті самі дані, "загублені в перекладі".

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

По-третє, це все одно може спричинити ін’єкцію SQL.

Якщо ваші дані користувача з форм уже відфільтровані / перевірені, навмисне введення SQl може стати менш вірогідним, ЯКЩО ваше введення буде зведено до жорстко закодованого списку варіантів або якщо воно обмежене цифрами. Але будь-який вільний введення тексту може бути використаний для введення SQL, якщо ви неправильно уникнете змінних у операторах SQL.

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


Ні занадто довгі дані, ні рядок у числовому полі нічого не зруйнують
Ваш здоровий глузд

хм, щойно спробував це зараз, і справді воно показує попередження замість помилки. Я думаю, що саме PDO перетворює це на помилку. Я впевнений, що я обговорював у дюжині випусків на drupal.org, де якийсь рядок занадто довгий для varchar.
donquixote

6

Ваш веб-браузер не "знає", що він отримує сторінку з php, він бачить лише html. А рівень http знає навіть менше цього. Ви повинні мати можливість обробляти майже будь-який тип введення, який може перетинати рівень http (на щастя, для більшості введених php вже видає помилку). Якщо ви намагаєтеся не допустити, щоб шкідливі запити зіпсували вашу базу даних, вам потрібно припустити, що хлопець з іншого боку знає, що робить, і що він не обмежується тим, що ви можете бачити у своєму браузері за звичайних обставин ( не кажучи вже про те, що ви можете возитися з інструментами розробника браузера). Так що так, вам потрібно врахувати будь-які вхідні дані зі спадного меню, але для більшості введених даних ви можете дати помилку.


6

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


5

Найкраще використовувати параметризований запит для того, щоб захиститись від введення SQL. У такому випадку вигляд запиту буде таким:

SELECT * FROM table WHERE size = ?

Коли ви подаєте такий запит, як текст, неперевірений для цілісності (введення не перевірено на сервері), і він містить код введення SQL, він буде оброблений правильно. Іншими словами, запит призведе до того, що щось подібне відбуватиметься на рівні бази даних:

SELECT * FROM table WHERE size = 'DROP table;'

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


4

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

Приклад того, що може і станеться, коли ви довіряєте клієнту.


4

Хакер може повністю обійти браузер, включаючи перевірку форми Javascript, надіславши запит за допомогою Telnet. Звичайно, він перегляне код вашої html-сторінки, щоб отримати імена полів, якими він повинен скористатися, але відтепер у нього все "йде". Отже, ви повинні перевірити всі значення, подані на сервері, ніби вони не походять з вашої сторінки html.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.