Чому механізм запобігання ін'єкцій SQL еволюціонував у напрямку використання параметризованих запитів?


59

Як я це бачу, атаки ін'єкцій SQL можна запобігти:

  1. Ретельно перевіряйте, фільтруючи, кодуючи вхід (перед вставкою в SQL)
  2. Використання підготовлених операторів / параметризованих запитів

Я гадаю, що у кожного є плюси і мінуси, але чому номер 2 зняв і став вважатися більш-менш фактичним способом запобігання ін'єкційним атакам? Це просто безпечніше і менш схильне до помилок чи були інші фактори?

Як я розумію, якщо правильно використовувати # 1 і дотримуватися всіх застережень, він може бути таким же ефективним, як і №2.

Санітизація, фільтрування та кодування

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

Параметризовані запити проти бібліотеки кодування

Є відповіді, де поняття parameterized queriesта до encoding librariesних трактуються взаємозамінно. Виправте мене, якщо я помиляюся, але я маю враження, що вони різні.

Я розумію, що encoding librariesнезалежно від того, наскільки вони хороші, вони завжди можуть змінити SQL "Програму", оскільки вони вносять зміни в сам SQL, перш ніж він буде відправлений в RDBMS.

Parameterized queries з іншого боку, надішліть програму SQL RDBMS, яка потім оптимізує запит, визначає план виконання запитів, вибирає індекси, які слід використовувати тощо, а потім підключає дані, як останній крок всередині RDBMS себе.

Кодування бібліотеки

  data -> (encoding library)
                  |
                  v
SQL -> (SQL + encoded data) -> RDBMS (execution plan defined) -> execute statement

Параметризований запит

                                               data
                                                 |
                                                 v
SQL -> RDBMS (query execution plan defined) -> data -> execute statement

Історичне значення

Деякі відповіді згадують, що історично параметризовані запити (PQ) створювались з міркувань продуктивності, і до нападів ін'єкцій, які націлили націлені проблеми кодування, стали популярними. В якийсь момент стало очевидним, що PQ також досить ефективно проти нападів ін'єкцій. Щоб відповідати духу мого питання, чому PQ залишався методом вибору і чому він процвітав вище більшості інших методів, коли справа стосується запобігання атак SQL-ін'єкції?


1
Коментарі не для розширеного обговорення; ця розмова перенесена в чат .
maple_shaft

23
Підготовлені заяви не є результатом еволюції від атак на ін'єкції SQL. Вони були там з самого початку. Ваше запитання ґрунтується на помилкових передумов.
користувач207421

4
Якщо ви думаєте, що ви розумніші за поганих хлопців, тоді займіться №1
папараццо

1
"чому PQ залишився методом вибору" Тому що це найпростіший і надійний. Плюс вищезазначені переваги в продуктивності перед PQ. Насправді немає недоліків.
Пол Дрейпер

1
Оскільки це правильне рішення проблеми, як робити запити, навіть якщо це не стосувалося ін'єкції SQL у контексті безпеки . Форми, які вимагають уникнути та використовувати вбудовані дані за допомогою команд, завжди є помилкою в дизайні, оскільки вони схильні до помилок, контр-інтуїтивні та погано ламаються при неправильному використанні. Дивіться також: Сценарій оболонок.
Р ..

Відповіді:


147

Проблема полягає в тому, що №1 вимагає ефективного розбору та інтерпретації всього варіанту SQL, проти якого ви працюєте, щоб ви знали, чи робить він щось, чого не повинен. І постійно оновлюйте цей код під час оновлення бази даних. Скрізь, де ви приймаєте вклад для своїх запитів. І не накручуйте.

Так, так, подібні речі зупиняли б нападки ін'єкцій SQL, але реалізувати це абсурдно дорожче.


60
@dennis - Що ж, цитата у вашому варіанті SQL? "? '?"? U + 2018? \ U2018? Чи є хитрощі для розділення виразів? Чи можуть ваші підзапити робити оновлення? Є багато чого врахувати.
Теластин,

7
@Dennis у кожного двигуна DB є свій спосіб робити такі речі, як втеча символів у рядках. Це дуже багато дірок, особливо якщо додатку потрібно працювати з декількома двигунами DB або бути сумісними з майбутніми версіями того ж двигуна, які можуть змінити незначний синтаксис запитів, який може бути використаний.

12
Ще одна користь підготовлених висловлювань - це підвищення продуктивності, який ви отримуєте, коли вам доведеться повторно запускати один і той же запит з різними значеннями. Крім того, підготовлені оператори можуть знати, чи значення означає по- nullсправжньому як , рядок або число і діяти відповідно. Це дуже добре для безпеки. І навіть якщо запустити запит один раз, двигун БД вже оптимізує його для вас. Ще краще, якщо воно буде кешоване!
Ісмаель Мігель

8
@Dennis Г-н Генрі Нулл подякує вам за те, що ви зробили це правильно.
Матьє Гіндон

14
@Dennis ім'я не має значення. Проблема - з прізвищем. Дивіться переповнення стека , Programmers.SE , Fox Sports , Wired , BBC та все інше, що ви можете
з’явити

80

Тому що варіант 1 не є рішенням. Екранування та фільтрування означає відхилення або видалення недійсного введення. Але будь-які дані можуть бути дійсними. Наприклад, апостроф - дійсний символ у назві "О'Маллей". Перед використанням у SQL його потрібно правильно закодувати, саме це і робить підготовлені оператори.


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


2
Це все (і це було відсутністю у двох інших відповідях, тому +1). З огляду на те, як формулюється питання, мова не йде про санітарію введення користувача, але я цитую питання: «фільтруючи вхід (до вставки)». Якщо зараз питання про санітарію введення, то чому б ви це робили самостійно, а не дозволяли бібліотеці це робити (в той же час, втрачаючи можливість, до речі, кешувати плани виконання)?
Арсеній Муренко

8
@Dennis: Санітизація або фільтрація означає видалення інформації. Кодування означає перетворення представлення даних без втрати інформації.
ЖакБ

9
@Dennis: фільтрування означає прийняття або відхилення введення користувача. Наприклад, "Jeff" буде відфільтровано як введення поля "Age of Age", оскільки значення, очевидно, недійсне. Якщо замість фільтрування введення ви починаєте перетворювати його, наприклад, замінюючи символ єдиної цитати, ви робите абсолютно те саме, що і бібліотеки баз даних, де вони використовують параметризовані запити; у цьому випадку ваше запитання просто "Чому я використовую те, що існує і було написане експертами в цій галузі, коли я можу винаходити колесо в кожному проекті?"
Арсеній Муренко

3
@Dennis: O\'Malleyвикористовує косу рису, щоб уникнути цитати для правильної вставки (принаймні, в деяких базах даних). У MS SQL або Access це можна уникнути додатковою цитатою O''Malley. Не дуже портативний, якщо вам доведеться це робити самостійно.
AbraCadaver

5
Я не можу вам сказати, скільки разів система мене відхиляла прямо імені. Іноді я навіть бачив помилки, спричинені інжекцією SQL, лише завдяки використанню мого імені. Чортве, мене колись попросили змінити своє ім’я користувача, оскільки я насправді щось зламав на бекенді.
Олександр О'Мара

60

Якщо ви намагаєтеся обробляти рядок, то ви насправді не генеруєте SQL-запит. Ви генеруєте рядок, який може створити SQL-запит. Існує рівень непрямості, який відкриває багато місця для помилок та помилок. Це насправді дивно, враховуючи, що в більшості контекстів ми раді спілкуватися з чимось програмно. Наприклад, якщо у нас є якась структура списку і хочемо додати елемент, ми зазвичай не робимо:

List<Integer> list = /* a list of 1, 2, 3 */
String strList = list.toString();   /* to get "[1, 2, 3]" */
strList = /* manipulate strList to become "[1, 2, 5, 3]" */
list = parseList(strList);

Якщо хтось пропонує це зробити, ви правильно відповісте, що це досить смішно, і що потрібно просто зробити:

List<Integer> list = /* ... */;
list.add(5, position=2);

Це взаємодіє зі структурою даних на її концептуальному рівні. Він не вводить ніякої залежності від того, як ця структура може бути надрукована або проаналізована. Це цілком ортогональні рішення.

Ваш перший підхід схожий на перший зразок (лише трохи гірше): ви припускаєте, що можете програмно побудувати рядок, який буде правильно проаналізований як потрібний запит. Це залежить від аналізатора і цілого ряду логіки обробки рядків.

Другий підхід використання підготовлених запитів набагато більше схожий на другий зразок. Використовуючи підготовлений запит, ви, по суті, аналізуєте законний псевдо-запит, але в ньому є деякі заповнювачі, а потім використовуєте API, щоб правильно замінити деякі значення там. Ви більше не включаєте процес розбору, і вам не доведеться турбуватися про будь-яку обробку рядків.

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

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

Навіть за допомогою макросів Lisp ви все одно можете генерувати поганий код, оскільки вам не обов'язково знати про структуру, яка повинна бути там. Наприклад, в Lisp, (нехай ((ab)) a) означає "встановити нову лексичну прив'язку змінної a до значення змінної b, а потім повернути значення a", і (нехай (ab) a) означає "встановити нові лексичні прив'язки змінних a і b і ініціалізувати їх як до нуля, а потім повернути значення a." Вони синтаксично правильні, але вони означають різні речі. Щоб уникнути цього питання, ви можете використовувати більш семантично усвідомлені функції та робити щось на кшталт:

Variable a = new Variable("a");
Variable b = new Variable("b");
Let let = new Let();
let.getBindings().add(new LetBinding(a,b));
let.setBody(a);
return let;

Щось подібне неможливо повернути щось синтаксично недійсне, і набагато важче повернути те, що випадково не те, що ви хотіли.


Гарне пояснення!
Майк Партрідж

2
Ви втратили мене при "хорошій аналогії", але я схвалив, виходячи з попереднього пояснення. :)
Wildcard

1
Відмінний приклад! - І ви можете додати: Залежно від типу даних іноді навіть неможливо або неможливо створити проаналізований рядок. - Що робити, якщо одним із моїх параметрів є поле з текстовим текстом, що містить чернетку історії (~ 10 000 символів)? або що робити, якщо один параметр є зображенням JPG ? - Єдиний спосіб - це параметризований запит
Falco

Насправді ні - це досить поганий опис того, чому підготовлені заяви розвивалися як захист від введення струю. Особливо, наприклад, приклад коду є в java, якого не було навколо, коли параметризовані запити, де розвивалися, ймовірно, у часові рамки, де C / C ++, коли вважається найсучаснішим. Бази даних SQL почали використовуватись у перші роки часового періоду 1970-1980 років. ШЛЯХ до мов вищого рівня, де популярні. Чорт забираю, я б сказав, що багатьом з них прийшло полегшити роботу з базами даних (комусь PowerBuilder?)
TomTom

@TomTom насправді я згоден з більшістю вашого вмісту. Я лише неявно торкнувся аспекту безпеки. Щодо того, я відповідаю на безліч запитань SPARQL (мова запитів RDF з деякою подібністю до SQL), і багато людей стикаються з проблемами, оскільки вони об'єднують рядки, а не використовують параметризовані запити. Навіть без ін'єкційних атак, параметризовані запити допомагають уникнути помилок / збоїв, а помилки / збої можуть бути також проблемами безпеки, навіть якщо вони не є ін'єкційними атаками. Тому я б сказав все менше і більше: параметризовані запити хороші, навіть якщо введення SQL не було проблемою, і вони хороші ...
Джошуа Тейлор

21

Це допомагає, що варіант №2, як правило, вважається найкращою практикою, оскільки база даних може кешувати непараметризовану версію запиту. Параметризовані запити передували проблемі ін'єкції SQL протягом декількох років (я вважаю), так буває, що ви можете вбити двох птахів одним каменем.


10
Інжекція SQL є проблемою з часу винайдення SQL. Це не стало проблемою пізніше.
Сервіс

9
@ Сервіс Теоретично так. Практично це стало лише справжньою проблемою, коли наші механізми введення вийшли в Інтернет, представляючи величезну поверхню атаки для кожного, хто б'є.
Ян Догген

8
Таблиці Little Bobby не погоджуються, що для використання інжекції SQL вам потрібен або Інтернет, або велика база користувачів. І звичайно ж мережі до дати SQL, так що це НЕ так, як вам потрібно буде чекати мереж одного разу SQL вийшов. Так, вразливості безпеки є менш вразливими, коли у вашої програми є невелика база користувачів, але вони все ще є вразливими місцями безпеки, і люди їх експлуатують, коли сама база даних має цінні дані (і багато дуже ранніх баз даних мали дуже цінні дані, як лише люди з цінними базами даних могли дозволити собі техніку) ..
Сервіс

5
@ Наскільки мені відомо, динамічний SQL був порівняно пізньою особливістю; початкове використання SQL здебільшого було попередньо скомпільовано / попередньо оброблено з параметрами для значень (як в, так і поза), тому параметри в запитах могли передувати введенням SQL в програмне забезпечення (можливо, не в спеціальних / CLI-запитах).
Марк Ротвевель

6
Вони могли передувати обізнаності про введення SQL.
користувач253751

20

Просто сказали: Вони цього не зробили. Ваша заява:

Чому механізм запобігання ін'єкцій SQL еволюціонував у напрямку використання параметризованих запитів?

є принципово хибним. Параметризовані запити існують набагато довше, ніж SQL Injection, принаймні, широко відомий. Вони, як правило, розроблені як спосіб уникнути стринг-концентації у звичайній програмі LOB (Line of Business) функціональної форми для пошуку. У багатьох - МНОГО - через роки хтось знайшов проблему із безпекою із маніпуляцією з рядком.

Я пам’ятаю, що робив SQL 25 років тому (коли Інтернет НЕ широко використовувався - він тільки починався), і я пам’ятаю, що робив SQL проти IBM DB5 IIRC версії 5 - і вже параметризував запити.


Дякую. Чому виникла потреба уникати конкатенації рядків? Мені здається, що це було б корисною особливістю. У когось із цим виникли проблеми?
Денніс

3
Два фактично. По-перше, це не завжди абсолютно тривіально - навіщо займатися розподілом пам'яті тощо, коли це не потрібно. Але по-друге, у давні часи кешування продуктивності кешування sql зі сторони бази даних було не дуже великим - компіляція SQL була дорогою. Як побічний ефект використання одного готового оператора sql (звідки беруться параметри), плани виконання можуть бути використані повторно. SQL Server запровадив автоматичну параметризацію (для повторного використання планів запитів навіть без параметрів - вони відраховуються та маються на увазі) Я думаю, або 2000, або 2007 - десь посеред, IIRC.
TomTom

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

Так, але, як я вже сказав - до того часу, коли вони були винайдені, динамічний SQL прийшов з цілком пристойним хітом продуктивності;) Навіть сьогодні люди говорять вам, що динамічні плани запитів SQL на сервері sql не використовуються повторно (що неправильно, оскільки - hm - як Я сказав деякий момент між 2000 і 2007 роками - так ДУЖЕ довго). У той час ви дуже хотіли ПІДГОТОВКИ операторів, якщо запускаєте sql кілька разів;)
TomTom

Кешування плану для динамічного SQL було фактично додано до SQL Server 7.0, у 1998 р. - sqlmag.com/database-performance-tuning/…
Майк Діммік

13

Окрім усіх інших хороших відповідей:

Причина, чому №2 краще, полягає в тому, що вона відокремлює ваші дані від вашого коду. У №1 ваші дані є частиною вашого коду, і звідси беруться всі погані речі. Завдяки №1 ви отримуєте запит і вам потрібно виконати додаткові кроки, щоб переконатися, що ваш запит розуміє ваші дані як дані, тоді як у №2 ви отримуєте свій код і його код, а ваші дані - дані.


3
Розмежування коду та даних також означає, що захист від ворожої ін'єкції коду записує та перевіряє постачальник баз даних. Тому якщо щось, що передається як параметр разом із нешкідливим запитом, закінчує руйнування або підрив вашої бази даних, репутація компанії в базі даних стоїть на рядку, і ваш орган може навіть подати в суд на них і виграти. Це також означає, що якщо цей код містить помилку, що експлуатується, шанси досить хороші, що це чужий сайт, де всякий хек ламається, а не ваш. (Просто не ігноруйте
помилки

11

Параметризовані запити, окрім забезпечення захисту від ін'єкцій SQL, часто мають додаткову перевагу: складати лише один раз, а потім виконувати кілька разів з різними параметрами.

З точки бази даних SQL зору select * from employees where last_name = 'Smith'і select * from employees where last_name = 'Fisher'явно відрізняються , і тому вимагають окремого розбору, компіляції та оптимізації. Вони також будуть займати окремі слоти в області пам'яті, призначені для зберігання складених операторів. У сильно завантаженій системі з великою кількістю подібних запитів, які мають різні параметри обчислення та накладних витрат на пам'ять, можуть бути істотними.

Згодом використання параметризованих запитів часто дає основні переваги щодо продуктивності.


Я думаю, що це теорія (заснована на використаних підготовлених операторах для параметризованих запитів). На практиці я сумніваюся, що це дійсно так часто, оскільки більшість реалізацій просто готують-зв'язують-виконують за один виклик, тому використовуйте інший підготовлений оператор для кожного параметризованого запиту, якщо ви не зробите явних кроків для фактичної підготовки операторів (і бібліотеки -рівень prepareчасто сильно відрізняється від фактичного рівня SQL prepare).
jcaron

Наступні запити також відрізняються від аналізатора SQL: SELECT * FROM employees WHERE last_name IN (?, ?)і SELECT * FROM employees WHERE last_name IN (?, ?, ?, ?, ?, ?).
Даміян Єрік

Так, вони мають. Чому МС додав кешування плану запитів ще в 1998 році до SQL Server 7. Як і в: Ваша інформація є поколінням.
TomTom

1
@TomTom - кешування плану запитів не те саме, що автоматична параметризація, на яку, здається, ви натякаєте. Як і раніше, читайте перед публікацією.
mustaccio

@mustaccio Насправді принаймні МС вводили обидва одночасно.
TomTom

5

Зачекайте, але чому?

Варіант 1 означає, що вам доведеться писати санітарні процедури для будь-якого типу введення, тоді як варіант 2 менш схильний до помилок і менше коду для запису / тестування / обслуговування.

Майже напевно "піклуватися про всі застереження" може бути складнішим, ніж ви вважаєте, що це так, і ваша мова (наприклад, Java PreparedStatement) має більше кришки, ніж ви думаєте.

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


1
JDBC не санітують аніт. Протокол має специфічну частину для параметра, а БД просто не інтерпретує ці параметри. Тому ви можете встановити ім'я таблиці з параметра.
talex

1
Чому? якщо параметр не проаналізований або інтерпретований, немає підстав для чогось уникнути.
талекс

11
Я думаю, у вас неправильне зображення того, як працює параметризований запит. Це не просто випадок, коли параметри будуть замінені пізніше, вони ніколи не замінюються . СУБД перетворює будь-який запит у "план", набір кроків, які він повинен виконати, щоб отримати ваш результат; у параметризованому запиті цей план є як функція: він має ряд змінних, які потрібно надати при його виконанні. До моменту надходження змінних рядок SQL був повністю забутий, і план просто виконується із заданими значеннями.
IMSoP

2
@IMSoP Це було моє помилкове уявлення. Хоча я думаю, що це звичайний, як ви бачите, у двох найбільш проголосованих відповідях на це запитання в SO stackoverflow.com/questions/3271249/… . Я читав про це, і ти маєш рацію. Я відредагував відповідь.
Тулен Кордова

3
@TomTom Це чудово для продуктивності , але нічого не робить для безпеки . На той час, коли компрометований фрагмент динамічного SQL складається і кешується, програма вже була змінена . Створення плану з нединамічного параметризованого SQL, а потім передача елементів даних все ще принципово відрізняється від СУБД, де виявляється схожість між двома запитами, представленими до нього, як повні рядки SQL.
IMSoP

1

Давайте уявимо, як виглядав би ідеальний підхід "очистити, відфільтрувати та кодувати".

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

Так що кодування залишає. Ви можете почати з функції, що кодує рядки, додаючи символи втечі, щоб ви могли їх замінити в собі. Оскільки для різних баз даних потрібні різні символи (в деяких базах і те, \'і інше ''є дійсними послідовностями ', а не в інших), цю функцію повинен забезпечити постачальник баз даних.

Але не всі змінні є рядками. Іноді потрібно замінити ціле число або дату. Вони представлені по-різному для рядків, тому вам потрібні різні методи кодування (знову ж таки, вони повинні бути специфічними для постачальника бази даних), і вам потрібно замінити їх у запиті різними способами.

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

На даний момент ми щойно переробили параметризовані запити.

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

Кодування важко зробити правильно, а кодування-зроблено-правильно не відрізняється від параметризації.

Якщо ви дійсно любите інтерполяції рядків як спосіб побудови запитів, є кілька мов (Scala і ES2015 приходять на розум) , які мають підключається інтерполяцію рядки, тому там є бібліотеки , які дозволяють писати параметризрвані запити , які виглядають як рядки інтерполяції, але захищені від ін'єкції SQL - так у синтаксисі ES2015:

import {sql} from 'cool-sql-library'

let result = sql`select *
    from users
    where user_id = ${user_id}
      and password_hash = ${password_hash}`.execute()

console.log(result)

1
"Кодування важко зробити правильно" - хахаха. Це не так. День-два, це все задокументовано. Я написав кодер багато років тому для ORM (оскільки сервер sql має обмеження в параметрах, і тому проблематично вставити 5000-10000 рядків в одне твердження (назад 15 років тому). Я не пам’ятаю, щоб це була великою проблемою.
TomTom

1
Можливо, SQL Server є достатньо регулярним, що це не проблема, але я стикався з проблемами в інших БД - кутових випадках з невідповідними кодуваннями символів, незрозумілими параметрами конфігурації, певними локальними проблемами дати та числа. Усі вирішувані, але потребують принаймні короткого розуміння вигадок БД (я дивлюся на вас, MySQL та Oracle).
James_pic

3
@TomTom Кодування насправді дуже важко отримати правильний раз, коли ви врахуєте час. Що ви робите, коли ваш постачальник баз даних вирішить створити новий стиль коментарів у наступному випуску чи коли барево стає новим ключовим словом у оновленнях? Теоретично ви можете отримати кодування фактично правильно для одного випуску RDBMS і помилитися при наступній редакції. Навіть не приступайте до того, що відбувається, коли ви переключите постачальників на той, хто має умовні коментарі, використовуючи нестандартний синтаксис
Ерік

@Eric, це відверто жахливо. (Я використовую Postgres; якщо на ньому є такі химерні бородавки, я ще не стикаюся з ними.)
Wildcard

0

У варіанті 1 ви працюєте з вхідним набором size = нескінченність, який ви намагаєтеся зіставити на дуже великий розмір виводу. У варіанті 2 ви обмежили свій внесок до того, що ви оберете. Іншими словами:

  1. Ретельно перевіряйте та фільтруючи [ нескінченність ] для [ всіх безпечних запитів SQL ]
  2. Використання [попередньо розглянутих сценаріїв, обмежених вашим обсягом ]

Згідно з іншими відповідями, також існує певна користь від обмеження вашої сфери дії від нескінченності та до чогось керованого.


0

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

Уразливість ін'єкцій SQL є ізоморфною для переповнення буфера на такій мові, як C. Історія показала, що переповнення буфера надзвичайно важко запобігти - навіть надзвичайно критичний код, що підлягає відкритому огляду, часто містить такі вразливості.

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

Отже, якщо вразливість ін'єкцій SQL еквівалентна переповненню буфера, то що таке SQL, еквівалентний біту NX, або сторінкам пам'яті лише для читання? Відповідь: підготовлені заяви , які включають параметризовані запити плюс подібні механізми для запитів без запитів. Підготовлений оператор складається з певних частин, позначених лише для читання, тому зловмисник не може змінювати ті частини програми, а інші частини, позначені як невиконані дані (параметри підготовленого оператора), до яких зловмисник може вводити дані, але який ніколи не буде розглядатися як програмний код, тим самим усуваючи більшість потенційних можливостей для зловживань.

Безумовно, санітарна інформація користувачів корисна, але щоб бути справді безпечним, потрібно бути параноїком (або, що рівнозначно, думати як зловмисник). Це спосіб зробити контрольну поверхню поза текстом програми , і підготовлені оператори забезпечують цю поверхню управління для SQL. Тож не дивно, що підготовлені заяви та, таким чином, параметризовані запити - це підхід, який рекомендує переважна більшість фахівців із безпеки.


Це все приємно і весело, але це не стосується питання відповідно до назви.
TomTom

1
@TomTom: Що ти маєш на увазі? Питання полягає саме в тому, чому параметризовані запити є кращим механізмом запобігання ін'єкції SQL; моя відповідь пояснює, чому параметризовані запити є більш безпечними та надійними, ніж санітарні дані користувачів.
Даніель Приден

Мені шкода, але моє запитання звучить так: "Чому механізм запобігання ін'єкціям SQL розвинувся у напрямку використання параметризованих запитів?". Вони цього не зробили. Йдеться не про зараз, це про історію.
TomTom

0

Я про це пише Alredy тут: https://stackoverflow.com/questions/6786034/can-parameterized-statement-stop-all-sql-injection/33033576#33033576

Але просто для простоти:

Спосіб параметризованих запитів полягає в тому, що sqlQuery надсилається як запит, і база даних точно знає, що буде робити цей запит, і лише після цього він буде вставляти ім'я користувача та паролі лише як значення. Це означає, що вони не можуть здійснити запит, оскільки база даних вже знає, що буде робити. Тож у цьому випадку буде шукати ім'я користувача "Ніхто АБО 1 = 1" - "та порожній пароль, який повинен з’явитися помилковим.

Це не повне рішення, але перевірку введення все ще потрібно зробити, оскільки це не вплине на інші проблеми, такі як XSS-атаки, оскільки ви все ще можете ввести javascript у базу даних. Потім, якщо це зчитується на сторінці, це відображатиметься як звичайний javascript, залежно від перевірки вихідних даних. Тому дійсно найкраще все-таки використовувати перевірку вхідних даних, але використовуючи параметризовані запити або збережені процедури, щоб зупинити будь-які атаки SQL


0

Я ніколи не використовував SQL. Але очевидно, ви чуєте про те, які проблеми мають люди, і у розробників SQL були проблеми з цим "ін'єкцією SQL". Тривалий час я не міг це зрозуміти. І тоді я зрозумів, що люди, де створюються SQL-заяви, справжні текстові висловлювання SQL-джерела, об'єднуючи рядки, з яких деякі куди входять користувачем. І моя перша думка про це усвідомлення була шоком. Тотальний шок. Я подумав: Як хто може бути таким смішно дурним і створювати заяви на будь-якій мові програмування? Для розробників C, або C ++, або Java, або Swift це повне безумство.

З цього приводу, не дуже складно написати функцію C, яка приймає рядок C за свій аргумент, і створює інший рядок, який виглядає точно як літеральний рядок у вихідному коді C, який представляє ту саму рядок. Наприклад, ця функція перекладе abc на "abc", а "abc" в "\" abc \ "" і "\" abc \ "" в "\" \\ "abc \\" \ "". (Ну, якщо це здається вам неправильним, це html. Це було правильно, коли я його набрав, але не тоді, коли він відображається) І коли ця функція C написана, зовсім не складно створити вихідний код C, де текст із поля введення, що надається користувачем, перетворюється на літеральний рядок C. Це не важко зробити безпечним. Чому розробники SQL не використовуватимуть такий підхід як спосіб уникнути ін'єкцій SQL - це не за мене.

"Санітизація" - це цілком хибний підхід. Фатальна вада полягає в тому, що це робить певні введення користувача незаконними. Ви отримуєте базу даних, де загальне текстове поле не може містити такий текст; Таблиця викидання або все, що ви використовуєте для ін'єкції SQL, щоб заподіяти шкоду. Я вважаю це зовсім неприйнятним. Якщо база даних зберігає текст, він повинен мати можливість зберігати будь-який текст. І практичний недолік полягає в тому, що дезінфікуючий засіб, здається, не може зробити це правильно :-(

Звичайно, параметризовані запити - це те, чого очікував би будь-який програміст, що використовує компільовану мову. Це набагато полегшує життя: у вас є деякий рядок, і ви навіть не намагаєтесь перекласти його в рядок SQL, а просто передаєте його як параметр, без жодних шансів, щоб жоден символ у цій рядку не завдав шкоди.

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

(Мені здається, що відповідь Йосипа цікава. Він, в основному, говорить, що за допомогою параметризованих запитів ви можете зупинити будь-яку атаку проти SQL, але тоді ви можете мати текст у вашій базі даних, який використовується для створення інжекції JavaScript :-( Ну, у нас знову така ж проблема , і я не знаю, чи є у Javascript рішення для цього.


-2

Основна проблема полягає в тому, що хакери знайшли способи оточення санітарії, тоді як параметризовані запити були існуючою процедурою, яка прекрасно працювала з додатковими перевагами продуктивності та пам'яті.

Деякі люди спрощують проблему, оскільки "це лише одна і подвійна цитата", але хакери знайшли розумні способи уникнути виявлення, як-от використання різних кодувань або використання функцій бази даних.

У будь-якому випадку вам потрібно було забути лише один рядок, щоб створити катастрофічне порушення даних. Хакери, де вдається автоматизувати сценарії для завантаження повної бази даних із серією або запитами. Якщо програмне забезпечення добре відоме, як набір із відкритим кодом або відомий бізнес-пакет, ви можете просто приєднати таблицю користувачів та паролів.

З іншого боку, просто використання об'єднаних запитів було лише питанням навчитися користуватися і звикати до нього.

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