Как работает SQL-инъекция из комикса XBCD Bobby Tables?

Просто посмотри:

XKCD Strip (Источник: https://xkcd.com/327/)

Что делает этот SQL:

Robert'); DROP TABLE STUDENTS; --

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

Ответы

Ответ 1

Отбрасывает таблицу учеников.

Исходный код в школьной программе, вероятно, выглядит примерно как

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 проницательный комментарий

Ответ 2

Пусть говорят, что имя было использовано в переменной $Name. Затем вы запускаете этот запрос:

INSERT INTO Students VALUES ( '$Name' )

Код ошибочно помещает все, что пользователь поставил в качестве переменной. Вы хотели, чтобы SQL был:

INSERT INTO Students VALUES ('Robert Tables')

Но умный пользователь может предоставить все, что захочет:

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

Что вы получаете:

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

" -- комментирует остальную часть строки.

Ответ 3

Как уже отмечали все остальные, '); закрывает исходный оператор, а затем следует второе утверждение. В большинстве фреймворков, включая языки, такие как 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-инъекцию, хотя вы можете делать более разрушительные вещи, если сможете предоставить несколько операторов.

Ответ 4

Нет, ' не является комментарием в 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');

т. он будет разорвать раннюю инструкцию insert, выполнить любой вредоносный код, который хочет взломщик, а затем прокомментировать любой остаток кода, который может быть.

Ммм, я слишком медленный, я вижу уже 8 ответов до моего в оранжевой группе...:-) Популярная тема, кажется.

Ответ 5

TL; DR

-- The application accepts input, in this case 'Nancy', without attempting to
-- sanitize the input, such as by escaping special characters
school=> INSERT INTO students VALUES ('Nancy');
INSERT 0 1

-- SQL injection occurs when input into a database command is manipulated to
-- cause the database server to execute arbitrary SQL
school=> INSERT INTO students VALUES ('Robert'); DROP TABLE students; --');
INSERT 0 1
DROP TABLE

-- The student records are now gone - it could have been even worse!
school=> SELECT * FROM students;
ERROR:  relation "students" does not exist
LINE 1: SELECT * FROM students;
                      ^

Это уменьшает (удаляет) таблицу учеников.

(Все примеры кода в этом ответе выполнялись на сервере базы данных PostgreSQL 9.1.2.)

Чтобы было ясно, что происходит, попробуйте это с помощью простой таблицы, содержащей только поле имени и добавляющей одну строку:

school=> CREATE TABLE students (name TEXT PRIMARY KEY);
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "students_pkey" for table "students"
CREATE TABLE
school=> INSERT INTO students VALUES ('John');
INSERT 0 1

Предположим, что приложение использует следующий SQL для вставки данных в таблицу:

INSERT INTO students VALUES ('foobar');

Замените foobar на фактическое имя ученика. Обычная операция вставки будет выглядеть так:

--                            Input:   Nancy
school=> INSERT INTO students VALUES ('Nancy');
INSERT 0 1

Когда мы запрашиваем таблицу, мы получаем следующее:

school=> SELECT * FROM students;
 name
-------
 John
 Nancy
(2 rows)

Что происходит, когда мы вставляем имя Little Bobby Tables в таблицу?

--                            Input:   Robert'); DROP TABLE students; --
school=> INSERT INTO students VALUES ('Robert'); DROP TABLE students; --');
INSERT 0 1
DROP TABLE

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

Важно заметить, что во время операции INSERT приложение не проверяет ввод для каких-либо специальных символов и поэтому позволяет вводить произвольный ввод в команду SQL. Это означает, что злоумышленник может вставлять в поле, обычно предназначенное для ввода пользователем, специальные символы, такие как кавычки, а также произвольный код SQL, чтобы заставить систему базы данных выполнить ее, а затем SQL-инъекцию.

Результат?

school=> SELECT * FROM students;
ERROR:  relation "students" does not exist
LINE 1: SELECT * FROM students;
                      ^

SQL-инъекция - это эквивалент базы данных уязвимости удаленного произвольного выполнения кода в операционной системе или приложении. Потенциальное воздействие успешной атаки SQL-инъекции нельзя недооценивать - в зависимости от конфигурации базы данных и конфигурации приложения злоумышленник может использовать потерю данных (как в этом случае), получить несанкционированный доступ к данным или даже выполнить произвольный код на самой машине.

Как отметил комикс XKCD, одним из способов защиты от атак SQL-инъекций является дезинфекция входов базы данных, например, экранирование специальных символов, так что они не могут изменять базовую команду SQL и, следовательно, не могут вызывать выполнение произвольного кода SQL. Если вы используете параметризованные запросы, например, используя SqlParameter в ADO.NET, вход будет, как минимум, автоматически дезинфицирован для защиты от SQL-инъекции.

Тем не менее, дезинфекция входных данных на уровне приложений может не остановить более сложные методы SQL-инъекций. Например, есть способы обойти mysql_real_escape_string PHP mysql_real_escape_string. Для дополнительной защиты многие системы баз данных поддерживают подготовленные заявления. Если они правильно реализованы в бэкэнд, подготовленные операторы могут сделать SQL-инъекцию невозможной, рассматривая входы данных как семантически отделенные от остальной части команды.

Ответ 6

Скажите, что наивно написал метод создания ученика следующим образом:

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...

Вот почему параметры привязки - хорошая вещь.

Ответ 7

Единая кавычка - это начало и конец строки. Точка с запятой - это конец инструкции. Поэтому, если они делали выбор следующим образом:

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

SQL стал бы следующим:

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

В некоторых системах сначала запускается select, а затем оператор drop! Это сообщение: DONT EMBED VALUES IN YOUR SQL. Вместо этого используйте параметры!

Ответ 8

'); завершает запрос, он не запускает комментарий. Затем он отбрасывает таблицу студентов и комментирует остальную часть запроса, который должен был быть выполнен.

Ответ 9

Писатель базы данных, вероятно, сделал

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

Если имя_датчика - одно заданное, это делает выбор с именем "Роберт", а затем удаляет таблицу. Часть "-" изменяет остальную часть данного запроса на комментарий.

Ответ 10

В этом случае 'не является символом комментария. Он используется для разграничения строковых литералов. Комический художник берет на себя идею о том, что в рассматриваемой школе есть динамический sql файл, который выглядит примерно так:

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

Итак, теперь символ заканчивает строковый литерал до того, как программист его ожидает. В сочетании с; чтобы закончить утверждение, злоумышленник теперь может добавить все, что захочет. Комментарий в конце состоит в том, чтобы убедиться, что любой оставшийся sql в исходном заявлении не мешает компрометации запроса на сервере.

FWIW, я также думаю, что этот комикс имеет важную деталь неправильно: если вы думаете о дезинфекции ваших входов в базу данных, как это предлагает комикс, вы все еще делаете это неправильно. Вместо этого вам следует подумать о том, как карантинировать ваши входы в базу данных, а правильный способ сделать это - с помощью параметризованных запросов.

Ответ 11

Символ ' в SQL используется для строковых констант. В этом случае он используется для завершения строковой константы, а не для комментария.

Ответ 12

Вот как это работает: Предположим, что администратор ищет записи студента

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; --

Двойная тире (-) просто закомментирует оставшуюся часть запроса.

Это опасно, так как это может аннулировать аутентификацию пароля, если присутствует

Первый выполнит обычный поиск.

Во втором случае, если учетная запись обладает достаточными привилегиями, вы удалите ученика таблицы (обычно учетная запись школьного администратора будет запускать такой запрос и будет иметь привилегии, о которых говорилось выше).

Ответ 13

Вам не нужно вводить данные формы, чтобы сделать 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

Попробуйте заменить 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 prepare() и 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! :) *************/
...