Как PreparedStatement предотвращает или предотвращает внедрение SQL?

122

Я знаю, что PreparedStatements избегает / предотвращает внедрение SQL. Как оно это делает? Будет ли окончательный запрос формы, созданный с использованием PreparedStatements, быть строкой или иначе?

Прабху Р
источник
3
Технически спецификация JDBC не настаивает на отсутствии недостатков SQL-инъекций. Я не знаю ни одного затронутого привода.
Том Хотин - tackline
3
@Jayesh Я предлагаю добавить сюда содержимое вашего блога в качестве ответа. Большинство ответов просто говорят о различиях генерации ч / б динамического SQL-запроса и подготовленного stmt. Они не обращаются к вопросу о том, почему подготовленные утверждения работают лучше, чем ваш блог.
Паван Манджунатх
1
Добавлено в качестве ответа, надеюсь, это поможет.
Jayesh

Ответы:

78

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

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

Тангенс
источник
1
Конечно, но вы все равно можете жестко запрограммировать некоторые или все свои параметры.
tangens
16
Пример, пожалуйста. Но если вы не используете пользовательский ввод в качестве параметра для подготовленного оператора, а вместо этого создаете свою команду SQL, объединяя строки вместе, вы все равно уязвимы для инъекций SQL даже при использовании подготовленных операторов.
Дэвид Блейн
4
FWIW Подготовленные операторы - это не JDBC, а SQL. Вы можете подготовить и выполнить подготовленные операторы из консоли SQL. PreparedStatement просто поддерживает их из JDBC.
beldaz
198

Рассмотрим два способа сделать то же самое:

PreparedStatement stmt = conn.createStatement("INSERT INTO students VALUES('" + user + "')");
stmt.execute();

Или

PreparedStatement stmt = conn.prepareStatement("INSERT INTO student VALUES(?)");
stmt.setString(1, user);
stmt.execute();

Если "пользователь" пришел из пользовательского ввода, а пользовательский ввод был

Robert'); DROP TABLE students; --

Тогда в первую очередь вас закроют из шланга. Во втором случае вы будете в безопасности, и Little Bobby Tables будут зарегистрированы в вашей школе.

Пол Томблин
источник
8
Итак, если я правильно понял, запрос во втором примере, который будет выполняться, будет выглядеть так: INSERT INTO student VALUES ("Robert '); DROP TABLE student; -") - или, по крайней мере, что-то в этом роде. Это правда?
Макс,
18
Нет, в ПЕРВОМ случае вы получите это утверждение. Во втором он вставит «Robert '); DROP TABLE student; -» в пользовательскую таблицу.
Пол Томблин,
3
Вот что я имел в виду во втором примере («безопасном»), строку Robert '); DROP TABLE студенты; - будет сохранено в поле в таблице ученика. Я что-то еще написал? ;)
Макс
7
Извините, я стараюсь избегать вложенных цитат из-за такой путаницы. Вот почему мне нравятся PreparedStatements с параметрами.
Пол Томблин,
59
Маленькие столики Бобби. XD Отличная ссылка
Амальговинус
130

Чтобы понять, как PreparedStatement предотвращает SQL-инъекцию, нам необходимо понять этапы выполнения SQL-запроса.

1. Фаза компиляции. 2. Этап исполнения.

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

Этапы выполнения запроса

  1. Этап синтаксического анализа и нормализации: на этом этапе запрос проверяется на синтаксис и семантику. Он проверяет, существуют ли ссылки на таблицу и столбцы, используемые в запросе. У него также много других задач, но не будем вдаваться в подробности.

  2. Этап компиляции: на этом этапе ключевые слова, используемые в запросе, например select, from, where и т. Д., Преобразуются в формат, понятный для машины. Это этап, на котором интерпретируется запрос и принимается решение о соответствующем действии. У него также много других задач, но не будем вдаваться в подробности.

  3. План оптимизации запроса: на этом этапе создается дерево решений для поиска способов выполнения запроса. Он определяет количество способов, которыми может быть выполнен запрос, и стоимость, связанную с каждым способом выполнения запроса. Он выбирает лучший план для выполнения запроса.

  4. Кэш: лучший план, выбранный в плане оптимизации запросов, сохраняется в кеше, поэтому всякий раз, когда в следующий раз поступает тот же запрос, ему не нужно снова проходить этапы 1, 2 и 3. Когда в следующий раз поступит запрос, он будет проверен непосредственно в кэше и оттуда будет взят для выполнения.

  5. Этап выполнения: на этом этапе выполняется предоставленный запрос, и данные возвращаются пользователю в виде ResultSetобъекта.

Поведение PreparedStatement API на вышеуказанных шагах

  1. PreparedStatements не являются полными SQL-запросами и содержат заполнители, которые во время выполнения заменяются фактическими данными, предоставленными пользователем.

  2. Всякий раз, когда какой-либо PreparedStatment, содержащий заполнители, передается в механизм SQL Server, он проходит следующие этапы

    1. Фаза синтаксического анализа и нормализации
    2. Фаза компиляции
    3. План оптимизации запросов
    4. Кэш (Скомпилированный запрос с заполнителями хранится в кэше.)

ОБНОВЛЕНИЕ пользовательского набора username =? и пароль =? ГДЕ id =?

  1. Вышеупомянутый запрос будет проанализирован, скомпилирован с заполнителями в качестве специальной обработки, оптимизирован и будет кэширован. Запрос на этом этапе уже скомпилирован и преобразован в понятный для машины формат. Таким образом, мы можем сказать, что запрос, хранящийся в кеше, предварительно скомпилирован, и только заполнители необходимо заменить данными, предоставленными пользователем.

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

PrepareStatementWorking

(Помните, что после того, как заполнители заменяются пользовательскими данными, окончательный запрос не компилируется / интерпретируется снова, и механизм SQL Server обрабатывает пользовательские данные как чистые данные, а не SQL, который необходимо анализировать или компилировать снова; в этом прелесть PreparedStatement. )

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

Примечание. Это фаза компиляции после фазы синтаксического анализа, которая понимает / интерпретирует структуру запроса и придает ей осмысленное поведение. В случае PreparedStatement запрос компилируется только один раз, а кэшированный скомпилированный запрос всегда выбирается для замены пользовательских данных и выполнения.

Благодаря одноразовой функции компиляции PreparedStatement, он свободен от атак SQL Injection.

Вы можете получить подробное объяснение с примером здесь: https://javabypatel.blogspot.com/2015/09/how-prepared-statement-in-java-prevents-sql-injection.html

Jayesh
источник
3
хорошее объяснение
Дирадж Джоши
4
Буквально наиболее полный ответ на вопрос, КАК это работает
jouell
Это было очень полезно. Спасибо за подробное объяснение.
Неизвестный
26

SQL, используемый в PreparedStatement, предварительно компилируется в драйвере. С этого момента параметры отправляются драйверу как буквальные значения, а не как исполняемые части SQL; таким образом, нельзя ввести SQL с помощью параметра. Еще один полезный побочный эффект PreparedStatements (предварительная компиляция + отправка только параметров) - повышение производительности при многократном выполнении оператора даже с разными значениями параметров (при условии, что драйвер поддерживает PreparedStatements), поскольку драйверу не нужно выполнять синтаксический анализ и компиляцию SQL каждый раз. время изменения параметров.

Трэвис Хесеман
источник
Это не обязательно должно быть реализовано таким образом, и я считаю, что часто это не так.
Том Хотин - tackline
4
На самом деле SQL обычно предварительно компилируется в базе данных. То есть в базе данных готовится план выполнения. Когда вы выполняете запрос, план выполняется с этими параметрами. Дополнительным преимуществом является то, что один и тот же оператор может выполняться с разными параметрами без необходимости каждый раз компилировать новый план обработчику запросов.
beldaz
3

Я думаю, это будет веревка. Но входные параметры будут отправлены в базу данных, и перед созданием фактического оператора SQL будут применены соответствующие приведения / преобразования.

Например, он может попытаться проверить, работает ли CAST / Conversion.
Если это сработает, на его основе можно будет сделать окончательное утверждение.

   SELECT * From MyTable WHERE param = CAST('10; DROP TABLE Other' AS varchar(30))

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

Теперь попробуйте передать строковую переменную (с содержанием, неприемлемым в качестве числового параметра). Смотрите, что происходит?

shahkalpesh
источник
3

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

Например stmt.setString(1, user);, преобразует userпараметр в строку.

Предположим, что параметр содержит строку SQL, содержащую исполняемую команду : использование подготовленного оператора не позволит этого.

Он добавляет к этому метасимвол (он же автоматическое преобразование).

Это делает его более безопасным.

Гуру Р Ханда
источник
2

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

Например:

Строковый запрос = «ВСТАВИТЬ ЗНАЧЕНИЯ студентов ('» + пользователь + «')»

когда пользователь вводит «Роберт»); DROP TABLE студенты; - ”в качестве входных данных вызывает SQL-инъекцию.

Как подготовленное заявление предотвращает это?

Строковый запрос = «ВСТАВИТЬ ЗНАЧЕНИЯ учеников ('» + «: имя» + «')»

parameters.addValue («имя», пользователь);

=> когда пользователь снова вводит «Роберт»); DROP TABLE студенты; - «, входная строка предварительно скомпилирована в драйвере как буквальные значения, и я предполагаю, что она может быть приведена в следующем виде:

CAST ( 'Роберт'); DROP TABLE студенты; - 'AS varchar (30))

Итак, в конце строка будет буквально вставлена ​​как имя в таблицу.

http://blog.linguiming.com/index.php/2018/01/10/why-prepared-statement-avoids-sql-injection/

разъем
источник
1
Если я не ошибаюсь, часть CAST(‘Robert’);из CAST(‘Robert’); DROP TABLE students; –‘ AS varchar(30))сломалась бы, тогда она продолжила бы падение таблицы, если бы это было так. Он останавливает инъекцию, поэтому я считаю, что этот пример недостаточно полный, чтобы объяснить сценарий.
Héctor Álvarez
1

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

1) Предварительная компиляция и кэширование оператора SQL на стороне БД приводит к общему более быстрому выполнению и возможности многократно использовать один и тот же оператор SQL в пакетах.

2) Автоматическое предотвращение атак SQL-инъекций путем экранирования кавычек и других специальных символов. Обратите внимание, что для этого необходимо использовать любой из методов PreparedStatement setXxx () для установки значения.

Мукеш Кумар
источник
1

Как объясняется в этом посте , само по PreparedStatementсебе не поможет, если вы все еще объединяете строки.

Например, один злоумышленник по-прежнему может делать следующее:

  • вызвать функцию сна, чтобы все соединения с базой данных были заняты, что сделало ваше приложение недоступным
  • извлечение конфиденциальных данных из БД
  • обход аутентификации пользователя

Не только SQL, но даже JPQL или HQL могут быть скомпрометированы, если вы не используете параметры привязки.

В итоге, вы никогда не должны использовать конкатенацию строк при построении операторов SQL. Для этого используйте специальный API:

Влад Михалча
источник
1
Спасибо, что указали на важность использования привязки параметров, а не только PreparedStatement. Однако ваш ответ, похоже, подразумевает, что использование специального API необходимо для защиты от внедрения SQL. Поскольку это не так, и использование PreparedStatement с привязкой параметров также работает, не могли бы вы изменить формулировку?
Wild Pottok
-3

В подготовленных отчетах пользователь вынужден вводить данные в качестве параметров. Если пользователь вводит некоторые уязвимые операторы, такие как DROP TABLE или SELECT * FROM USERS, данные не будут затронуты, поскольку они будут рассматриваться как параметры оператора SQL.

Шреяс К.
источник
Тот же ответ, что и выбранный ответ, с меньшей точностью.
Жюльен Марет,