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

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

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

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

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

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-инъекции и структурированные запросы природы, которые делают их невозможными, когда они используются.