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