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

Как подготовленные заявления помогают нам предотвратить атаки SQL injection?

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

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

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

+148
источник поделиться
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);

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

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

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

+267
источник

Вот пример 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, чтобы все возвращалось, включая информацию об Aaron, которая должна была быть скрыта.

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

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

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=?
+21
источник
другие ответы

Связанные вопросы


Похожие вопросы

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

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

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

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

+13
источник

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

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

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

+5
источник

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

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

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

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

+5
источник

В 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 [email protected]");
command.Parameters.Add(new SqlParameter("@userinput", 100));
command.Parameters.Add(new SqlParameter("@pass", 100));
command.prepare();

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

+4
источник

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

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

+3
источник
ResultSet rs = statement.executeQuery("select * from foo where value = " + httpRequest.getParameter("filter");

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

+2
источник

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

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

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

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

Как структурированные запросы разрешают корневые причины внедрения SQL

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

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

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

0
источник

Посмотрите другие вопросы по меткам или Задайте вопрос