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


How do подготовленные заявления профилактика SQL-инъекций атаки?

в Википедии написано:

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

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

9 126

9 ответов:

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

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

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

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

вот 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, тогда как пример bind всегда работает одинаково заявление.)

вот пример связывания, чтобы избежать такого рода инъекции:

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"));
        }
    }
}

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

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=?

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

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

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

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

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

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

теперь, если значение в переменной inoutusername является чем-то вроде' или 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-инъекции...

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

все это обрабатывается для вас.

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

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

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

ResultSet rs = statement.executeQuery("select * from foo where value = " + httpRequest.getParameter("filter");

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

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

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

один объединяет пользовательский ввод с некоторой частичной строкой 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" никогда не будет выполняться в случае выше.

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

Первопричина #1-Проблема С Разделителем

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

Первопричина #2 - Человеческая Природа, Люди лукавы и Некоторые Хитрые Люди ЗлыеИ Все Люди Делают Ошибки

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

Как Структурированных Запросов Устраните основные причины SQL-инъекции

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

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

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