Як працює інжекція SQL з коміксу XKCD "Столи Бобі"?


1093

Просто дивлячись:

XKCD Strip (Джерело: https://xkcd.com/327/ )

Що робить цей SQL:

Robert'); DROP TABLE STUDENTS; --

Я знаю і те, 'і --для коментарів, але чи не DROPкоментується також слово , оскільки воно є частиною того ж рядка?


16
Якщо ви слухаєте Stack Overflow Podcast № 31 (27 листопада 2008 р.), Вони насправді обговорюють це.
EBGreen

93
У MySQL 'не для коментарів . Навіть якби він був, перед ним немає місця, тому він може лише закінчити рядок, який йому передує.
Гонки легкості по орбіті

45
Що стосується XKCD, якщо у вас є якісь питання щодо деяких коміксів, ви завжди можете перейти до Пояснення XKCD і з'ясувати свою відповідь. Є навіть вікі XKCD , що дуже допомагає для таких складних коміксів, як XKCD geohashing
Anatoli

13
Я вважаю, що це посилання має бути записано тут: bobby-tables.com
Arioch '

2
beta.companieshouse.gov.uk/company/10542519 - це реєстрація на консультацію з назвою; ДРОП ТАБЛИКА "КОМПАНІЇ"; - LTD
Олексій Дюпюй

Відповіді:


1116

Це скидає стіл студентів.

Оригінальний код програми школи, ймовірно, виглядає приблизно так

q = "INSERT INTO Students VALUES ('" + FNMName.Text + "', '" + LName.Text + "')";

Це наївний спосіб додавання тексту в запит, і це дуже погано , як ви побачите.

Після того, як значення з імені, текстового поля середнього імені FNMName.Text (який є Robert'); DROP TABLE STUDENTS; --) та текстового поля прізвища LName.Text (назвемо це Derper) з'єднуються з рештою запиту, результат фактично є двома запитами, розділеними термінатор оператора (крапка з комою). Другий запит введено в перший. Коли код виконує цей запит проти бази даних, він буде виглядати приблизно так

INSERT INTO Students VALUES ('Robert'); DROP TABLE Students; --', 'Derper')

який простою англійською мовою приблизно відповідає двом запитам:

Додайте новий запис у таблицю "Студенти" зі значенням імені "Роберт"

і

Видаліть таблицю студентів

Все, що минуло другий запит, позначається як коментар : --', 'Derper')

Ім'я 'студента - це не коментар, це розділовий рядок завершального рядка . Оскільки ім'я студента є рядком, його потрібно синтаксично виконати гіпотетичним запитом. Атаки ін'єкції працюють лише тоді, коли запит SQL, який вони вводять, призводить до дійсного SQL .

Відредаговано знову відповідно до проникливого коментаря dan04


3
Гм, де ГД з круглими дужками навколо аргументів є доволі незвичним, але принаймні це дозволяє уникнути помилки синтаксису ... :-)
PhiLho

60
@PhiLho: Якби початковий вислів був an INSERT, тоді дужки мали б більше сенсу. Це також пояснить, чому підключення до бази даних не знаходиться в режимі лише для читання.
dan04

3
Як пояснює @ dan04, круглі дужки мають більше сенсу з INSERT. Якщо подумати назад, то SELECTінакше б не бігати, оскільки Вставлення столів маленького хлопчика в стіл уже б опустив стіл.
ypercubeᵀᴹ

10
Насправді, у цьому прикладі перший запит ("додати новий запис ...") буде невдалим, оскільки Studentsочікує більше, ніж лише один стовпець (оригінальний / правильний виклад забезпечив два стовпці). Враховуючи це, наявність другого стовпця є корисним для того, щоб показати, чому потрібно коментувати; і оскільки ніхто не може змінити ім'я Боббі, мабуть, найкраще залишити як-є лише трохи більше, ніж це спостереження як виноска.
eggyal

7
Прізвище Бобі - або, принаймні, у матері, - Робертс , поясніть XKCD . Я не впевнений, що виправлення цього покращить ясність відповіді.
WBT

611

Скажімо, ім'я було використано в змінної $Name.

Потім ви запустите цей запит :

INSERT INTO Students VALUES ( '$Name' )

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

Ви хотіли, щоб SQL був:

ВСТУПИТИ В ЦІННІ УЧНІВ (' Robert Tables')

Але розумний користувач може поставити все, що завгодно:

ВСТАВЛЯЙТЬСЯ В НАВЧАЛЬНІ ЗНАЧЕННЯ (' Robert'); DROP TABLE Students; --')

Що ви отримуєте:

INSERT INTO Students VALUES ( 'Robert' );  DROP TABLE STUDENTS; --' )

--Тільки коментарі залишок рядка.


87
Це набагато краще, ніж той, хто проголосував вище, тому що це пояснює дужки, що закриваються.
Тім Бют

1
До речі, директора школи в коміксах немає відомості, або XSS, оскільки таблиця учнів видалена, він не може знати, хто це зробив.
xryl669

@ xryl669 Журнали дуже корисні в таких ситуаціях ... Іноді всі запити реєструються, а іноді інша інформація, що реєструється, може допомогти вам визначити винуватця.
inemanja

165

Як уже вказували всі інші, ');закривається початковий вислів, а далі йде друге твердження. Більшість фреймворків, включаючи такі мови, як PHP, мають налаштування безпеки за замовчуванням, які не дозволяють використовувати кілька операторів в одній строці SQL. Наприклад, у PHP, ви можете запускати декілька операторів у одній строці SQL, використовуючи mysqli_multi_queryфункцію.

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

$query="SELECT * FROM users WHERE username='" . $_REQUEST['user'] . "' and (password='".$_REQUEST['pass']."')";
$result=mysql_query($query);

Якщо ви вказали peterім'я користувача та secretпароль, отриманий рядок SQL виглядатиме так:

SELECT * FROM users WHERE username='peter' and (password='secret')

Все добре. Тепер уявіть, що ви вказали цей рядок як пароль:

' OR '1'='1

Тоді отриманий рядок SQL буде таким:

SELECT * FROM users WHERE username='peter' and (password='' OR '1'='1')

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


71

Ні, 'це не коментар у SQL, а роздільник.

Мама вважала, що програміст бази даних зробив запит, який виглядає так:

INSERT INTO 'students' ('first_name', 'last_name') VALUES ('$firstName', '$lastName');

(наприклад), щоб додати нового учня, де $xxxзміст змісту було виведено безпосередньо з форми HTML, не перевіряючи формат і не уникаючи спеціальних символів.

Отже, якщо $firstNameмістить Robert'); DROP TABLE students; --база даних, програма виконає наступний запит безпосередньо в БД:

INSERT INTO 'students' ('first_name', 'last_name') VALUES ('Robert'); DROP TABLE students; --', 'XKCD');

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

Ммм, я занадто повільний, я бачу вже 8 відповідей перед моїми в помаранчевій смузі ... :-) Популярна тема, здається.


39

TL; DR

- Додаток приймає вхідні дані , в даному випадку «Ненсі», не намагаючись - санувати вхід, наприклад, з допомогою спеціальних символів 
школи => INSERT INTO студентів VALUES ( «Ненсі» ); ВСТАВКА 0 1
   
  

- ін'єкції SQL відбувається , коли вхід в команду бази даних маніпулюють , щоб - викликати сервер бази даних виконати довільний SQL 
школи => INSERT INTO студентів VALUES ( «Robert» ); ДРОПУВАТИ ТАБЛИЧНІ студенти ; - '); ВСТАВКА 0 1 ТАБЛИКА ДРОПА
      
  
 

- Студентські записи тепер пропали - це могло бути ще гірше! 
школа => ВИБРАТИ * ВІД учнів ; 
ПОМИЛКА :   співвідношенню «студенти» зовсім НЕ існує   
РЕКЛАМ 1 : ВИБІРТЕ * З учнів ; ^   
                      

Це скидає (видаляє) таблицю учнів.

( Усі приклади коду в цій відповіді були запущені на сервері баз даних PostgreSQL 9.1.2. )

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

школа => СТВОРИТИ ТАБЛИЧНІ студенти ( назва ТЕКСТ ПЕРШИЙ КЛЮЧ ); 
УВАГА : CREATE TABLE / PRIMARY KEY буде створювати неявну індекс "students_pkey" для таблиці "Студенти" CREATE TABLE 
школи => INSERT INTO студентів ЗНАЧЕННЯ ( 'Джон' ); ВСТАВКА 0 1             
    
  

Припустимо, що програма використовує наступний SQL для вставки даних у таблицю:

INSERT INTO студентів ЗНАЧЕННЯ ( 'Foobar' );  

Замініть foobarфактичним прізвищем студента. Нормальна операція вставки виглядатиме так:

- Вхідні дані: 
школа Ненсі => ВСТАВЛЯЙТЬСЯ В НАВЧАЛЬНІ ЗНАЧЕННЯ студентів ( «Ненсі» ); ВСТАВКА 0 1   
  

Коли ми запитуємо таблицю, ми отримуємо це:

школа => ВИБРАТИ * ВІД учнів ;   
 назва
-------
 Джон
 Ненсі
( 2 ряди ) 

Що станеться, коли ми вставимо в таблицю ім'я маленьких Боббі Столів?

- Вхід: Роберт '); ДРОПУВАТИ ТАБЛИЧНІ студенти; - 
школа => INSERT INTO студентів ЗНАЧЕННЯ ( 'Роберт' ); ДРОПУВАТИ ТАБЛИЧНІ студенти ; - '); ВСТАВКА 0 1 ТАБЛИКА ДРОПА      
  
 

Ін'єкція SQL тут є результатом того, що ім'я студента закінчує оператор і включає окрему DROP TABLEкоманду; обидві тире в кінці вводу призначені для коментування будь-якого коду, що залишився, який інакше спричинив би помилку. Останній рядок виводу підтверджує, що сервер бази даних скинув таблицю.

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

Результат?

школа => ВИБРАТИ * ВІД учнів ; 
ПОМИЛКА :   співвідношенню «студенти» зовсім НЕ існує   
РЕКЛАМ 1 : ВИБІРТЕ * З учнів ; ^   
                      

Інжекція SQL - еквівалент бази даних віддаленої уразливості виконання довільного коду в операційній системі або додатку. Потенційний вплив успішної атаки ін'єкції SQL не можна недооцінювати - залежно від системи бази даних та конфігурації програми, зловмисник може використовувати її для втрати даних (як у цьому випадку), отримання несанкціонованого доступу до даних або навіть виконання довільний код на самій хост-машині.

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

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


30

Скажіть, ви наївно написали такий метод створення учнів:

void createStudent(String name) {
    database.execute("INSERT INTO students (name) VALUES ('" + name + "')");
}

І хтось вводить ім’я Robert'); DROP TABLE STUDENTS; --

Що працює в базі даних, це цей запит:

INSERT INTO students (name) VALUES ('Robert'); DROP TABLE STUDENTS --')

Точка з комою закінчується командою вставки та запускає іншу; - коментує решту рядка. Команда DROP TABLE виконується ...

Ось чому параметри зв’язування - це хороша річ.


26

Єдина цитата - це початок і кінець рядка. Точка з комою - це кінець заяви. Тож якщо вони робили такий вибір:

Select *
From Students
Where (Name = '<NameGetsInsertedHere>')

SQL стане:

Select *
From Students
Where (Name = 'Robert'); DROP TABLE STUDENTS; --')
--             ^-------------------------------^

У деяких системах selectспочатку буде запущено команду, а потім dropзаяву! Повідомлення таке: НЕ ЗАПАЛУЄМО ЦІННОСТІ В СВОЙ SQL. Замість цього використовуйте параметри!


18

На ');торцях запиту, він не починає коментар. Потім він опускає таблицю учнів та коментує решту запиту, який повинен був бути виконаний.


17

Письменник бази даних, ймовірно, зробив:

sql = "SELECT * FROM STUDENTS WHERE (STUDENT_NAME = '" + student_name + "') AND other stuff";
execute(sql);

Якщо ім'я студента вказане, це робить вибір з іменем "Роберт", а потім скидає таблицю. Частина "-" змінює решту поданого запиту на коментар.


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

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

17

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

$sql = "INSERT INTO `Students` (FirstName, LastName) VALUES ('" . $fname . "', '" . $lname . "')";

Отже, символ 'закінчує літеральний рядок ще до того, як його очікував програміст. У поєднанні з; символу для завершення заяви, зловмисник може тепер додавати будь-який sql, який вони хочуть. Коментар в кінці полягає в тому, щоб переконатися, що будь-який залишився sql в початковому операторі не перешкоджає запиту збиратися на сервері.

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


16

'Символ в SQL використовується для строкових констант. У цьому випадку він використовується для закінчення константи рядка, а не для коментарів.


7

Ось як це працює: Припустимо, адміністратор шукає записи студентів

Robert'); DROP TABLE STUDENTS; --

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

Код для отримання імені користувача з запиту є

Тепер запит буде приблизно таким (для пошуку студентської таблиці)

String query="Select * from student where username='"+student_name+"'";

statement.executeQuery(query); //Rest of the code follows

Отриманий запит стає

Select * from student where username='Robert'); DROP TABLE STUDENTS; --

Оскільки введення користувача не санітоване, вищезазначений запит маніпулюється на 2 частини

Select * from student where username='Robert'); 

DROP TABLE STUDENTS; --

Подвійний тире (-) просто прокоментує частину запиту, що залишилася.

Це небезпечно, оскільки може звести нанівець автентифікацію пароля, якщо така є

Перший зробить звичайний пошук.

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


SELECT* FROM sutdents ...- ви забули "с". Це те, що ти скидаєш. DROP TABLE STUDENTS;
DevWL

4

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

Ніхто раніше цього не вказував, через те, я можу попередити когось із вас.

Переважно ми спробуємо виправити введення форм. Але це не єдине місце, де можна напасти на ін'єкцію SQL. Ви можете зробити дуже просту атаку з URL, яка надсилає дані через GET-запит; Розглянемо наступний приклад:

<a href="/show?id=1">show something</a>

Ваша URL-адреса виглядатиме http://yoursite.com/show?id=1

Тепер хтось міг спробувати щось подібне

http://yoursite.com/show?id=1;TRUNCATE table_name

Спробуйте замінити ім'я_матеріалу справжнім іменем таблиці. Якщо він отримає правильне ім'я вашого столу, він випорожнить ваш стіл! (Дуже легко змусити цю URL-адресу простою скриптом)

Ваш запит виглядатиме приблизно так ...

"SELECT * FROM page WHERE id = 4;TRUNCATE page"

Приклад коду вразливого PHP за допомогою PDO:

<?php
...
$id = $_GET['id'];

$pdo = new PDO($database_dsn, $database_user, $database_pass);
$query = "SELECT * FROM page WHERE id = {$id}";
$stmt = $pdo->query($query);
$data = $stmt->fetch(); 
/************* You have lost your data!!! :( *************/
...

Рішення - використовуйте методи PDO priprema () та bindParam ():

<?php
...
$id = $_GET['id'];

$query = 'SELECT * FROM page WHERE id = :idVal';
$stmt = $pdo->prepare($query);
$stmt->bindParam('idVal', $id, PDO::PARAM_INT);
$stmt->execute();
$data = $stmt->fetch();
/************* Your data is safe! :) *************/
...
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.