Как работает SQL-инъекция из комикса XKCD «Таблицы Бобби»? - PullRequest
1051 голосов
/ 02 декабря 2008

Просто глядя на:

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

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

Robert'); DROP TABLE STUDENTS; --

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

Ответы [ 12 ]

1070 голосов
/ 02 декабря 2008

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

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

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

585 голосов
/ 02 декабря 2008

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

INSERT INTO Students VALUES ( '$Name' )

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

ВСТАВИТЬ В СТОИМОСТЬ ШКОЛЬНИКОВ (' Роберт Таблиц `)

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

ВСТАВИТЬ В СТОИМОСТЬ студентов (' Роберт'); DROP TABLE Студенты; - ')

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

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

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

154 голосов
/ 14 сентября 2011

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

70 голосов
/ 02 декабря 2008

Нет, ' - не комментарий в 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 ответов перед моим в оранжевой полосе ... :-) Кажется, популярная тема.

37 голосов
/ 27 сентября 2011

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)

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

--                            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 . Для дополнительной защиты многие системы баз данных поддерживают подготовленные операторы . При правильной реализации в бэкэнде подготовленные операторы могут сделать внедрение SQL невозможным, обработав ввод данных как семантически отдельный от остальной части команды.

28 голосов
/ 02 декабря 2008

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

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 выполнена ...

Вот почему параметры связывания - это хорошо.

25 голосов
/ 02 декабря 2008

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

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

SQL станет:

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

В некоторых системах сначала запускается select, за которым следует оператор drop! Сообщение: НЕ ВСТАВЛЯЙТЕ ЗНАЧЕНИЯ В ВАШ SQL. Вместо этого используйте параметры!

17 голосов
/ 02 декабря 2008

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

16 голосов
/ 02 декабря 2008

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

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

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

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

16 голосов
/ 02 декабря 2008

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

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

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

...