Как работает SQL-инъекция из комикса" таблицы Бобби " XKCD?

просто смотрю на:

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

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

Robert'); DROP TABLE STUDENTS; --

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

12 ответов


он роняет таблицу студентов.

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

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

это наивный способ добавить текстовый ввод в запрос и является очень плохо, как вы увидите.

после значений из первого имени, отчество textbox FNMName.Текст (т. е. Robert'); DROP TABLE STUDENTS; --) и текстовое поле Фамилия л. Неймане.Текст (назовем его Derper) являются объединенный с остальной частью запроса, результат теперь фактически два запроса разделены заявление Терминатор (точка с запятой). Второй запрос был вводят в первый. Когда код выполняет этот запрос к базе данных, он будет выглядеть следующим образом

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

что, на простом английском языке, грубо переводится на два вопроса:

добавить новую запись в таблицу Students со значением имени "Роберт"

и

удалить таблицу студентов

все после второго запроса - помечено как комментарий: --', 'Derper')

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

редактировать снова по состоянию на dan04проницательный комментарий


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

INSERT INTO Students VALUES ( '$Name' )

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

вставить в значения студентов ('Роберт Таблицы` )

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

вставить в значения студентов ('Robert'); падение таблицы студентов;--' )

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

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

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


как уже указывали все остальные,'); закрывает исходный оператор, а затем следует второй оператор. Большинство фреймворков, включая такие языки, как PHP, имеют настройки безопасности по умолчанию, которые не позволяют использовать несколько операторов в одной строке SQL. В PHP, например, вы можете запускать только несколько операторов в одной строке SQL с помощью


нет, ' - это не комментарий в 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');

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

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


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

это удаляет (удаляет) таблицу student.

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

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

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 in ADO.NET, вход будет, как минимум, автоматически дезинфицироваться для защиты от SQL-инъекций.

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


скажем, вы наивно написали метод создания студента, как это:

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 --')

точка с запятой завершает команду insert и запускает другую; -- комментирует остальную часть строки. Выполняется команда DROP TABLE...

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


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

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

SQL станет:

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

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


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


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

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

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


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

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

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

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


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


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

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

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

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

первый будет выполнять обычный поиск.

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