Чи захищають htmlspecialchars та mysql_real_escape_string мій PHP-код від введення?


116

Раніше сьогодні було задано питання щодо стратегій перевірки вводу даних у веб-додатках .

Найвища відповідь, під час написання, пропонує PHPпросто використовувати htmlspecialcharsта mysql_real_escape_string.

Моє запитання: чи цього завжди достатньо? Чи є більше, що ми повинні знати? Де розбиваються ці функції?

Відповіді:


241

Що стосується запитів до бази даних, завжди намагайтеся використовувати підготовлені параметризовані запити. mysqliІ PDOбібліотеки підтримують це. Це нескінченно безпечніше, ніж використання функцій втечі, таких як mysql_real_escape_string.

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

Уявіть собі наступний SQL:

$result = "SELECT fields FROM table WHERE id = ".mysql_real_escape_string($_POST['id']);

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

1 OR 1=1

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

SELECT fields FROM table WHERE id= 1 OR 1=1

Це прекрасний вектор ін'єкції SQL і дозволив би зловмиснику повернути всі рядки. Або

1 or is_admin=1 order by id limit 1

який виробляє

SELECT fields FROM table WHERE id=1 or is_admin=1 order by id limit 1

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

Хоча ці функції корисні, їх потрібно використовувати обережно. Вам потрібно переконатися, що всі вхідні дані в певній мірі перевірені. У цьому випадку ми бачимо, що нас можна експлуатувати, оскільки ми не перевіряли, чи змінна, яку ми використовували як число, насправді була числовою. У PHP вам слід широко використовувати набір функцій, щоб перевірити, чи є входи цілими числами, плаваючими, буквено-цифровими і т. Д. Але коли мова заходить про SQL, то найбільше слідкуйте за значенням підготовленого оператора. Вищевказаний код був би безпечним, якби це був підготовлений вислів, оскільки функції бази даних знали б, що 1 OR 1=1це не є дійсним літералом.

Що стосується htmlspecialchars(). Це власне мінне поле.

У PHP існує справжня проблема в тому, що він має цілий вибір різноманітних функцій, пов'язаних з html, і не має чітких рекомендацій щодо того, які саме функції виконувати.

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

echo '<img src= "' . htmlspecialchars($_GET['imagesrc']) . '" />';

Ми вже всередині тегу HTML, тому нам не потрібно робити <або> робити щось небезпечне. Наш вектор нападу просто міг бутиjavascript:alert(document.cookie)

Тепер результат HTML виглядає так

<img src= "javascript:alert(document.cookie)" />

Атака проходить прямо.

Це стає гірше. Чому? тому що htmlspecialchars(коли його називають таким чином) кодує лише подвійні лапки, а не одиничні. Так якби ми мали

echo "<img src= '" . htmlspecialchars($_GET['imagesrc']) . ". />";

Тепер наш злий нападник може вводити цілком нові параметри

pic.png' onclick='location.href=xxx' onmouseover='...

дає нам

<img src='pic.png' onclick='location.href=xxx' onmouseover='...' />

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

Навіть якщо ви використовуєте htmlspecialchars($string)теги HTML поза межами HTML, ви все ще вразливі до багатобайтових векторів атаки на комір.

Найефективнішим, яким ви можете бути, є використання комбінації mb_convert_encoding та htmlentity наступним чином.

$str = mb_convert_encoding($str, 'UTF-8', 'UTF-8');
$str = htmlentities($str, ENT_QUOTES, 'UTF-8');

Навіть це залишає IE6 вразливим, через те, як він обробляє UTF. Однак ви можете перейти до більш обмеженого кодування, такого як ISO-8859-1, поки використання IE6 не припиниться.

Для більш поглибленого вивчення багатобайтових проблем див. Https://stackoverflow.com/a/12118602/1820


24
Єдине, що тут пропущено, - це те, що перший приклад запиту БД ... простий intval () вирішив би ін'єкцію. Завжди використовуйте intval () замість mysqlescape ... (), коли потрібен номер, а не рядок.
Роберт К

11
і пам’ятайте, що використання параметризованих запитів дозволить вам завжди трактувати дані як дані, а не коди. Використовуйте бібліотеку, таку як PDO, та використовуйте параметризовані запити, коли це можливо.
Cheekysoft

9
Два зауваження: 1. У першому прикладі ви будете в безпеці, якщо також будете ставити лапки навколо параметра, наприклад $result = "SELECT fields FROM table WHERE id = '".mysql_real_escape_string($_POST['id'])."'";2. У другому випадку (атрибут, що містить URL), взагалі немає жодної користі htmlspecialchars; у цих випадках слід кодувати вхід, використовуючи схему кодування URL, наприклад, використовуючи rawurlencode. Таким чином, користувач не може вставити javascript:та ін.
Марсель Корпель

7
"Htmlspecialchars кодує лише подвійні лапки, а не поодинокі": це неправда, це залежить від встановлення прапорців, дивіться його параметри .
Марсель Корпель

2
Це слід виділити жирним шрифтом: у Take a whitelist approach and only let through the chars which are good.чорному списку завжди щось буде пропущено. +1
Джо Смо

10

Окрім чудової відповіді Cheekysoft:

  • Так, вони будуть зберігати вас у безпеці, але тільки якщо вони використовуються абсолютно правильно. Використовуйте їх неправильно, і ви все ще будете вразливими та можуть виникнути інші проблеми (наприклад, корупція даних)
  • Будь ласка, використовуйте замість них параметризовані запити (як зазначено вище). Ви можете використовувати їх, наприклад, через PDO або через обгортку типу PEAR DB
  • Переконайтесь, що magic_quotes_gpc та magic_quotes_runtime завжди вимкнено, і ніколи не вмикайтеся випадково, навіть ненадовго. Це рання і глибоко помилкова спроба розробників PHP запобігти проблемам із безпекою (що знищує дані)

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

У HTML, речі потрібно уникати по-різному, залежно від контексту. Особливо це стосується рядків, розміщених у Javascript.


3

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

Що стосується запитів до бази даних, завжди намагайтеся використовувати підготовлені параметризовані запити. Бібліотеки mysqli та PDO підтримують це. Це нескінченно безпечніше, ніж використання функцій евакуації, таких як mysql_real_escape_string.

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

Уявіть собі наступний SQL:

$ result = "ВИБІР поля з таблиці WHERE id =" .mysql_real_escape_string ($ _ POST ['id']);

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

1 АБО 1 = 1

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

ВИБІРТЬ поля з таблиці, де id = 1 або 1 = 1

Я зашифрував швидку маленьку функцію, яку я помістив у свій клас бази даних, який буде викреслювати все, що не має числа. Він використовує preg_replace, тому існує проблема трохи оптимізованішої функції, але вона працює в дрібному ...

function Numbers($input) {
  $input = preg_replace("/[^0-9]/","", $input);
  if($input == '') $input = 0;
  return $input;
}

Тож замість використання

$ result = "ВИБРАТИ поля з таблиці WHERE id =" .mysqlrealescapestring ("1 АБО 1 = 1");

Я б користувався

$ result = "ВИБІРТЬ поля з таблиці, де id =". чисел ("1 АБО 1 = 1");

і він би спокійно запустив запит

ВИБІРТЬ поля з таблиці, де id = 111

Впевнені, що це просто не дозволило йому відобразити правильний рядок, але я не думаю, що це велика проблема для того, хто намагається ввести sql на ваш сайт;)


1
Ідеально! Це саме такий вид санітарії, який вам потрібен. Початковий код не вдався, оскільки він не підтвердив, що число було числовим. Ваш код це робить. вам слід зателефонувати Numbers () на всі цілі числа vars, значення яких походять за межами бази коду.
Cheekysoft

1
Варто згадати, що для цього intval () буде прекрасно працювати, оскільки PHP автоматично примушує цілі числа до рядків для вас.
Адам Ернст

11
Я віддаю перевагу intval. Виходить 1abc2 до 1, а не 12.
jmucchiello

1
intval краще, особливо на ID. У більшості випадків, якщо його було зіпсовано, його так само, як вище, 1 або 1 = 1. Ви дійсно не повинні просочувати посвідчення людей. Таким чином, intval поверне правильний ідентифікатор. Після цього слід перевірити, чи є початкові та очищені значення однаковими. Це прекрасний спосіб не тільки зупиняти атаки, але й знаходити нападників.
триєдиність

2
Неправильний рядок був би згубним, якщо ви показуєте особисті дані, ви бачили б інформацію іншого користувача! натомість краще було б перевіритиreturn preg_match('/^[0-9]+$/',$input) ? $input : 0;
Френк Форте

2

Важливим фрагментом цієї головоломки є контексти. Хтось надсилає "1 АБО 1 = 1" як ідентифікатор - це не проблема, якщо ви цитуєте кожен аргумент у своєму запиті:

SELECT fields FROM table WHERE id='".mysql_real_escape_string($_GET['id'])."'"

Результати:

SELECT fields FROM table WHERE id='1 OR 1=1'

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


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

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

Я згоден; в ідеалі ОП використовуватиме підготовлені заяви.
Лукас Оман

1
Хоча цитування аргументів, запропонованих цією публікацією, не є надійною, це пом'якшить багато поширених атак типу 1 АБО 1 = 1, тому це варто згадати.
Сова

2
$result = "SELECT fields FROM table WHERE id = ".(INT) $_GET['id'];

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

Ви також повинні використовувати одну функцію / метод для очищення своїх значень. Навіть якщо ця функція є лише обгорткою для mysql_real_escape_string (). Чому? Оскільки одного дня, коли знайдеться експлуатуючий спосіб, який ви бажаєте очистити, вам доведеться оновити його лише в одному місці, а не знайти та замінити загальносистемну систему.


-3

чому, о ЧОМУ, ви б не включали цитати навколо вводу користувачів у свою операцію sql? здається, зовсім нерозумно не робити! включення лапок у вашому операторі sql призведе до "1 або 1 = 1" безрезультатної спроби, ні?

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

ну, просте виправлення для цього: просто видаліть користувальницькі дані. наприклад: input =~ s/'//g;. тепер мені все одно здається, що введення користувача буде захищене ...


"чому, о ЧОМУ, ви б не включали лапки щодо вводу користувачів у свою операцію sql?" - Питання нічого не говорить про те, що не цитувати введення користувача.
Квентін

1
"ну, просте виправлення для цього" - Страшне виправлення для цього. Це викидає дані. Рішення, згадане в самому питанні, є кращим підходом.
Квентін

хоча я погоджуюсь, що питання не стосується цитування вводу користувача, все ще здається, що не потрібно цитувати дані. і, я б швидше кидав дані, ніж вводив погані дані. як правило, при ін'єкційній атаці ви так і не хочете цих даних .... правда?
Jarett L

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

1
@JarettL Або звикніть до використання підготовлених заяв, або звикніть до Bobby Tables, що розбиває ваші дані кожного вівторка . Параметризований SQL - єдиний найкращий спосіб захистити себе від ін'єкції SQL. Вам не потрібно робити "перевірки інжекції SQL", якщо ви використовуєте підготовлений оператор. Вони надзвичайно прості у здійсненні (і, на мою думку, полегшують читання коду), захищають від різних ідіосинкразій з’єднання рядків і введення sql, і найкраще, що вам не потрібно винаходити колесо для його реалізації .
Сіюал
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.