Як PreparedStatement уникає або запобігає введенню SQL?


122

Я знаю, що PreparedStatements уникають / запобігають ін'єкції SQL. Як це робити? Чи буде запит остаточної форми, побудований за допомогою PreparedStatements, рядок чи іншим чином?


3
Технічно специфікація JDBC не наполягає на тому, що немає вад ін'єкції SQL. Я не знаю жодних накопичувачів, на які це постраждало.
Том Хоутін - тайклін

3
@Jayesh Я пропоную тут додати вміст свого блогу як відповідь. Більшість відповідей - це лише те, що свідчить про відмінності генерації динамічного SQL-запиту та підготовленого stmt. Вони не вирішують питання, чому підготовлені заяви краще працюють у вашому блозі.
Pavan Manjunath

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; --

Тоді, в першу чергу, вам покладуть шланг. По-друге, ви б були в безпеці, і столики маленького Бобі будуть зареєстровані для вашої школи.


8
Отже, якби я це правильно зрозумів, запит у другому прикладі, який буде виконаний, насправді буде: ВСТАВИТЬ У НАВЧАЛЬНІ ЗНАЧЕННЯ ("Роберт"); УРОКУВАТИ СТОЛЮВАННЯ СТУДЕНЬ; - ") - або хоча б щось подібне. Це правда?
Макс

18
Ні, у ПЕРШІЙ інстанції ви отримаєте це твердження. У другому він буде вставляти "Robert"); DROP TABLE students; - "в таблицю користувача.
Пол Томблін

3
Ось що я мав на увазі у другому прикладі ("безпечний"), рядок Роберта "); ДРОПУВАТИ ТАБЛИЦІ студентів - буде збережено у полі в таблиці учнів. Я щось ще писав? ;)
Макс

7
Вибачте, котирування вкладу - це те, чого я намагаюся уникати через подібну плутанину. Ось чому мені подобаються PreparedStatements з параметрами.
Пол Томблін

59
Столики маленького Бобі. XD Відмінна довідка
Амальговін

128

Щоб зрозуміти, як PreparedStatement перешкоджає ін'єкції SQL, нам потрібно зрозуміти фази виконання SQL Query.

1. Фаза компіляції. 2. Фаза виконання.

Щоразу, коли двигун SQL-сервера отримує запит, він повинен пройти нижче фаз,

Фази виконання запитів

  1. Етап розбору та нормалізації: На цій фазі запит перевіряється на синтаксис та семантику. Він перевіряє, чи існує таблиця посилань та стовпці, використані в запиті, чи ні. У нього також є багато інших завдань, які потрібно виконати, але не будемо детально розбиратися.

  2. Фаза компіляції: На цій фазі ключові слова, які використовуються в запиті, такі як select, from, where etc., перетворюються у формат, зрозумілий машиною. Це етап, коли інтерпретується запит і вирішується відповідна дія, яку слід здійснити. У нього також є багато інших завдань, які потрібно виконати, але не будемо детально розбиратися.

  3. План оптимізації запитів: На цьому етапі створюється Дерево рішень для пошуку шляхів виконання запиту. Він визначає кількість способів виконання запиту та вартість, пов'язану з кожним способом виконання запиту. Він вибирає найкращий план для виконання запиту.

  4. Кеш: Найкращий план, вибраний у Плані оптимізації запитів, зберігається в кеші, так що кожного разу, коли наступний раз надходить той самий запит, йому не доведеться повторно проходити через Фазу 1, Фазу 2 та Фазу 3. Коли наступний раз надходить запит, він перевірятиметься безпосередньо в кеші та вибирається звідти для виконання.

  5. Фаза виконання: На цій фазі поданий запит виконується і дані повертаються користувачеві як ResultSetоб'єкт.

Поведінка API PreparedStatement на наведених вище кроках

  1. PreparedStatements не є повними запитами SQL і містять заповнювачі, які під час виконання замінюються фактичними даними, наданими користувачем.

  2. Кожен раз, коли будь-який готовий заставник, що містить заповнювачі, передається в двигун SQL Server, він проходить нижче фаз

    1. Фаза розбору та нормалізації
    2. Фаза компіляції
    3. План оптимізації запитів
    4. Кеш (Складений запит із заповнювачами зберігається в Кеші.)

ОНОВЛЕННЯ користувача встановити ім'я користувача =? а пароль =? ДЕ id =?

  1. Наведений вище запит буде проаналізований, скомпільований із заповнювачами як спеціальна обробка, оптимізований та кешований. Запит на цьому етапі вже складений та перетворений у машинозрозумілому форматі. Таким чином, ми можемо сказати, що Запит, що зберігається в кеші, має попередній збір, і лише заповнювачі потрібно замінити на дані, надані користувачем.

  2. Тепер під час запуску, коли надходять дані, що надаються користувачем, попередньо складений запит вибирається з кешу, а заповнювачі замінюються наданими користувачем даними.

ПідготуватиРабота з роботою

(Пам'ятайте, що після того, як власники місць замінюються на дані користувачів, остаточний запит не збирається / інтерпретується знову, і двигун 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


3
приємне пояснення
Dheeraj Joshi

4
Буквально найповніша відповідь на те, як це працює твір
Jouell

Це було дуже корисно. Дякую за детальне пояснення.
Невідомо

26

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


Це не потрібно реалізовувати так, і я вважаю, що це часто не так.
Том Хоутін - таклін

4
Насправді SQL, як правило, попередньо компілюється в базі даних. Тобто на базі даних готується план виконання. При виконанні запиту план виконується за допомогою цих параметрів. Додатковою перевагою є те, що одне і те ж твердження може виконуватися з різними параметрами, без того, щоб процесор запитів щоразу повинен складати новий план.
beldaz

3

Я думаю, це буде рядок. Але вхідні параметри будуть надіслані до бази даних, а відповідні кадри / перетворення будуть застосовані до створення фактичного оператора SQL.

Щоб навести приклад, він може спробувати перевірити, чи працює CAST / конверсія.
Якщо він працює, він може створити з нього остаточну заяву.

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

Спробуйте приклад з оператором SQL, що приймає числовий параметр.
Тепер спробуйте передати рядкову змінну (з числовим вмістом, прийнятним як числовий параметр). Чи викликає це помилка?

Тепер спробуйте передати рядкову змінну (із вмістом, неприйнятним як числовий параметр). Бачите, що відбувається?


3

Підготовлена ​​заява є більш безпечною. Він перетворить параметр у вказаний тип.

Наприклад stmt.setString(1, user);, перетворить userпараметр у рядок.

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

До цього додається метахарактор (він же автоматичне перетворення).

Це робить його більш безпечним.


2

Введення SQL: коли користувач має шанс ввести щось, що може бути частиною оператора sql

Наприклад:

Рядок запит = "ВСТАВИТЬ У НАВЧАЛЬНІ ЗНАЧЕННЯ (" "+ користувач +" ")

при введенні користувачем "Роберт"); ДРОПУВАТИ ТАБЛИЦІ студентів; - ”як вхід, він спричиняє введення SQL

Наскільки підготовлена ​​заява перешкоджає цьому?

Рядок запит = "ВСТАВИТЬ У ВІДЧУКУ студентів (" "+": ім'я "+" ")"

parametri.addValue ("ім'я", користувач);

=> коли користувач знову вводить "Роберт"); ДРОПУВАТИ ТАБЛИЦІ студентів; - ", рядок введення попередньо компілюється на драйвер у вигляді буквальних значень, і я думаю, що це може бути зроблено як:

CAST ('Роберт'); ДРОПУВАТИ ТАБЛИЦІ студентів; - 'AS варчар (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))ламається, то вона б продовжувала скидати стіл, якби це було так. Це зупиняє ін'єкцію, тому я вважаю, що приклад просто недостатній для пояснення сценарію.
Гектор Альварес

1

Підготовлений стан:

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

2) Автоматична профілактика атак ін'єкцій SQL шляхом вбудованого виходу цитат та інших спеціальних символів. Зверніть увагу, що для цього потрібно використовувати будь-який із методів набору PreparedStatementXxx ().


1

Як пояснено в цій публікації , PreparedStatementодне не допоможе вам, якщо ви все ще об'єднуєте струни.

Наприклад, один зловмисник може все ще зробити наступне:

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

Не тільки SQL, але навіть JPQL або HQL можуть бути порушені, якщо ви не використовуєте параметри прив'язки.

Підсумок, ви ніколи не повинні використовувати конкатенацію рядків при побудові операторів SQL. Для цього використовуйте спеціальний API:


1
Дякуємо вам за вказівку на важливість використання прив'язки параметрів, а не лише PreparedStatement. Однак ваша відповідь, мабуть, означає, що для захисту від ін'єкції SQL необхідне використання спеціалізованого API. Оскільки це не так, і якщо Ви також не хочете скористатися PreparedStatement із прив’язкою до параметрів, переформатуйте?
Дикий Поток

-3

У Підготовлені Звіти користувач змушений вводити дані як параметри. Якщо користувач вводить деякі вразливі висловлювання, такі як DROP TABLE або SELECT * FROM USERS, на дані не впливатиме, оскільки вони вважатимуться параметрами оператора SQL


Відповідь така ж, як і вибрана відповідь, з меншою точністю.
Жульєн Марет
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.