Как подготовленные операторы могут защитить от атак SQL-инъекций? - PullRequest
140 голосов
/ 25 ноября 2011

Как подготовленные операторы помогают нам предотвратить SQL-инъекцию атак?

Википедия говорит:

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

Я не очень хорошо вижу причину. Каким было бы простое объяснение на простом английском и некоторые примеры?

Ответы [ 9 ]

256 голосов
/ 25 ноября 2011

Идея очень проста - запрос и данные отправляются на сервер базы данных отдельно .
Вот и все.

Корень проблемы внедрения SQL смешивание кода и данных.

Фактически, наш SQL-запрос легитимная программа .И мы создаем такую ​​программу динамически, добавляя некоторые данные на лету.Таким образом, эти данные могут мешать программному коду и даже изменять его, как показывает каждый пример SQL-инъекции (все примеры в PHP / Mysql):

$expected_data = 1;
$query = "SELECT * FROM users where id=$expected_data";

будет производить регулярныйзапрос

SELECT * FROM users where id=1

, в то время как этот код

$spoiled_data = "1; DROP TABLE users;"
$query        = "SELECT * FROM users where id=$spoiled_data";

создаст вредоносную последовательность

SELECT * FROM users where id=1; DROP TABLE users;

Это работает, потому что мы добавляем данные непосредственно в тело программы иона становится частью программы, поэтому данные могут изменить программу, и в зависимости от переданных данных у нас будет либо обычный вывод, либо таблица users.

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

Сначала мы отправляем программу на сервер

$db->prepare("SELECT * FROM users where id=?");

, где данные заменяются некоторой переменной , называемой параметром или заполнителем.

Обратите внимание, что тот же запрос отправляется на сервер, без каких-либо данных в нем!И затем мы отправляем данные с помощью второго запроса, по существу отделенного от самого запроса:

$db->execute($data);

, поэтому он не может изменить нашу программу и причинить какой-либо вред.
Довольно просто - не так ли?

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

Заполнитель - это общая идея для замены фактических данных переменной для будущей обработки (см., Например, printf()), в то время как подготовленный оператор является единственным его подмножеством.

Существуют случаи (в частности, PDO в PHP может это сделать), когда подготовленный оператор может быть эмулирован, и запрос фактически составляется вместе с данными и отправляется на сервер в одном запросе.Но важно понимать, что этот подход одинаково безопасен , потому что каждый бит данных правильно отформатирован в соответствии с его типом , и, следовательно, ничего плохого не может произойти.

Единственное, что я должен добавить, которое всегда опускается в каждом руководстве:

Подготовленные операторы могут защитить только данные , но не может защитить саму программу .
Итак, как только мы добавим, скажем, динамический идентификатор - например, имя поля, подготовленные операторы не могут нам помочь.Я недавно объяснил этот вопрос , поэтому я не буду повторяться.

20 голосов
/ 25 ноября 2011

Вот SQL для настройки примера:

CREATE TABLE employee(name varchar, paymentType varchar, amount bigint);

INSERT INTO employee VALUES('Aaron', 'salary', 100);
INSERT INTO employee VALUES('Aaron', 'bonus', 50);
INSERT INTO employee VALUES('Bob', 'salary', 50);
INSERT INTO employee VALUES('Bob', 'bonus', 0);

Класс Inject уязвим для внедрения SQL.Запрос динамически вставляется вместе с пользовательским вводом.Целью запроса было показать информацию о Бобе.Заработная плата или бонус, в зависимости от ввода пользователя.Но злонамеренный пользователь манипулирует вводом, повреждая запрос, привязывая эквивалент 'или true' к предложению where, чтобы все возвращалось, включая информацию об Аароне, который должен был быть скрыт.

import java.sql.*;

public class Inject {

    public static void main(String[] args) throws SQLException {

        String url = "jdbc:postgresql://localhost/postgres?user=user&password=pwd";
        Connection conn = DriverManager.getConnection(url);

        Statement stmt = conn.createStatement();
        String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='" + args[0] + "'";
        System.out.println(sql);
        ResultSet rs = stmt.executeQuery(sql);

        while (rs.next()) {
            System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
        }
    }
}

При выполнении этого, первый случай - при обычном использовании, а второй - со злонамеренным внедрением:

c:\temp>java Inject salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary'
salary 50

c:\temp>java Inject "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary' OR 'a'!='b'
salary 100
bonus 50
salary 50
bonus 0

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

Вот пример Binding, чтобы избежать такого рода инъекций:

import java.sql.*;

public class Bind {

    public static void main(String[] args) throws SQLException {

        String url = "jdbc:postgresql://localhost/postgres?user=postgres&password=postgres";
        Connection conn = DriverManager.getConnection(url);

        String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?";
        System.out.println(sql);

        PreparedStatement stmt = conn.prepareStatement(sql);
        stmt.setString(1, args[0]);

        ResultSet rs = stmt.executeQuery();

        while (rs.next()) {
            System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
        }
    }
}

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

c:\temp>java Bind salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?
salary 50

c:\temp>java Bind "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?
13 голосов
/ 21 апреля 2012

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

Это связано с тем, что подготовленные операторы сначала «подготавливают» запрос SQL, чтобы найти эффективный план запроса, и отправляют фактические значения, которые предположительно поступают из формы, позже - в то времязапрос фактически выполнен.

Более подробная информация здесь:

Подготовленные операторы и SQL-инъекция

5 голосов
/ 13 июля 2016

Когда вы создаете и отправляете подготовленный оператор в СУБД, он сохраняется как запрос SQL для выполнения.

Позже вы связываете свои данные с запросом, так что СУБД использует эти данные в качестве параметров запроса длявыполнение (параметризация).СУБД не использует данные, которые вы связываете, в качестве дополнения к уже скомпилированному запросу SQL;это просто данные.

Это означает, что принципиально невозможно выполнить SQL-инъекцию с использованием подготовленных операторов.Этому препятствует сама природа подготовленных высказываний и их связь с СУБД.

4 голосов
/ 25 ноября 2011

В SQL Server использование подготовленного оператора определенно защищено от инъекций, поскольку входные параметры не формируют запрос. Это означает, что выполненный запрос не является динамическим запросом. Пример уязвимого оператора SQL-инъекции.

string sqlquery = "select * from table where username='" + inputusername +"' and password='" + pass + "'";

Теперь, если значение в переменной inoutusername является чем-то вроде a 'или 1 = 1 -, этот запрос теперь становится:

select * from table where username='a' or 1=1 -- and password=asda

А остальное комментируется после --, поэтому оно никогда не выполняется и не игнорируется, как в примере с подготовленным оператором, как показано ниже.

Sqlcommand command = new sqlcommand("select * from table where username = @userinput and password=@pass");
command.Parameters.Add(new SqlParameter("@userinput", 100));
command.Parameters.Add(new SqlParameter("@pass", 100));
command.prepare();

Таким образом, фактически вы не можете отправить другой параметр, что позволяет избежать внедрения SQL ...

3 голосов
/ 27 декабря 2017

Я прочитал ответы и все еще чувствовал необходимость подчеркнуть ключевой момент, который освещает суть подготовленных заявлений. Рассмотрим два способа сделать запрос к базе данных, в которой участвует пользовательский ввод:

Наивный подход

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

String SQLString = "SELECT * FROM CUSTOMERS WHERE NAME='"+userInput+"'"

Например, злонамеренный ввод пользователя может привести к SQLString, равному "SELECT * FROM CUSTOMERS WHERE NAME='James';DROP TABLE CUSTOMERS;'

Из-за злонамеренного пользователя, SQLString содержит 2 утверждения, где 2-е ("DROP TABLE CUSTOMERS") нанесет вред.

Подготовленные заявления

В этом случае из-за разделения запроса и данных пользовательский ввод никогда не обрабатывается как оператор SQL, и, следовательно, никогда не выполняется . По этой причине любой введенный вредоносный код SQL не причинит вреда. Таким образом, "DROP TABLE CUSTOMERS" никогда не будет выполнено в случае выше.

В двух словах, с подготовленными утверждениями вредоносный код, введенный через пользовательский ввод, не будет выполняться!

3 голосов
/ 25 ноября 2011

Ключевая фраза need not be correctly escaped. Это означает, что вам не нужно беспокоиться о людях, пытающихся добавить черточки, апострофы, кавычки и т. Д ...

Это все для вас.

2 голосов
/ 25 ноября 2011
ResultSet rs = statement.executeQuery("select * from foo where value = " + httpRequest.getParameter("filter");

Давайте предположим, что вы имеете это в сервлете, вы правы. Если злонамеренный человек передал неверное значение для «фильтра», вы можете взломать вашу базу данных.

0 голосов
/ 08 апреля 2017

Основная причина № 1 - Проблема разделителя

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

Основная причина № 2 - Человеческая природа, люди хитры и Некоторые коварные люди злобны И все люди делаютОшибки

Другой основной причиной инъекции sql является человеческая природа.Люди, в том числе программисты, делают ошибки.Когда вы делаете ошибку в структурированном запросе, это не делает вашу систему уязвимой для внедрения SQL.Если вы не используете структурированные запросы, ошибки могут привести к уязвимости SQL-инъекций.

Как структурированные запросы устраняют первопричины SQL-инъекций

Структурированные запросы решают проблему разделителя,путем помещения команд sql в один оператор и помещения данных в отдельный оператор программирования.Программные операторы создают необходимое разделение.

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

Итак, у вас есть это, причины внедрения SQL и структурированные запросы, которые делают их невозможными при их использовании.

...