Якщо так, чому все ще так багато успішних ін’єкцій SQL? Тільки тому, що деякі розробники занадто тупі, щоб використовувати параметризовані оператори?
Якщо так, чому все ще так багато успішних ін’єкцій SQL? Тільки тому, що деякі розробники занадто тупі, щоб використовувати параметризовані оператори?
Відповіді:
Посилання, які я розмістив у своїх коментарях до запитання, дуже добре пояснюють проблему. Я підсумував свої почуття щодо того, чому проблема зберігається, нижче:
Ті, хто тільки починає, можуть не знати про введення SQL.
Деякі знають про введення SQL, але думають, що втеча є (єдиним?) Рішенням. Якщо ви виконуєте швидкий пошук у Google php mysql query
, першою сторінкою, що з’являється, є mysql_query
сторінка, на якій є приклад, який показує інтерполяцію введеного введеного користувача запиту. Немає згадки (принаймні, що я бачу) використання замість цього підготовлених висловлювань. Як сказали інші, існує стільки підручників, які використовують інтерполяцію параметрів, що насправді не дивно, як часто вона все ще використовується.
Відсутність розуміння того, як працюють параметризовані оператори. Деякі вважають, що це просто вигадливий засіб уникнення цінностей.
Інші знають параметризовані твердження, але не використовують їх, бо чули, що вони занадто повільні. Я підозрюю, що багато людей чули, наскільки неймовірно повільними є параметризовані висловлювання, але насправді не проводили жодного тестування. Як зазначив Білл Карвін у своїй доповіді, різницю в результатах роботи рідко слід використовувати як фактор при розгляді питання використання підготовлених висловлювань. Переваги одноразової підготовки, виконання багатьох , часто видаються забутими, як і вдосконалення безпеки та ремонтопридатності коду.
Деякі використовують параметризовані оператори скрізь, але з інтерполяцією неперевірених значень, таких як імена таблиць і стовпців, ключові слова та умовні оператори. Динамічні пошукові запити, такі як ті, що дозволяють користувачам вказати ряд різних полів пошуку, умови порівняння та порядок сортування, є головними прикладами цього.
Помилкове почуття безпеки при використанні ORM. ORM все ще дозволяють інтерполяцію частин оператора SQL - див. 5.
Програмування - це великий і складний предмет, управління базами даних - великий і складний предмет, безпека - великий і складний предмет. Розробка захищеного додатка бази даних непроста - навіть досвідчені розробники можуть потрапити в очі.
Багато відповідей на stackoverflow не допомагають. Коли люди пишуть запитання, що використовують динамічну SQL та інтерполяцію параметрів, часто не вистачає відповідей, які пропонують використовувати замість них параметризовані оператори. У деяких випадках мені доводилося спростовувати мою пропозицію використовувати підготовлені висловлювання - як правило, через сприйняті неприйнятні результати. Я серйозно сумніваюся, що ті, хто задає більшість із цих питань, знаходяться в положенні, коли зайві кілька мілісекунд, необхідні для підготовки параметризованого висловлення, матимуть катастрофічний вплив на їх застосування.
Коли статті говорять про параметризовані запити, що зупиняють SQL-атаки, вони насправді не пояснюють, чому, це часто буває "Це робить, тому не питайте чому" - можливо, тому, що вони не знають себе. Вірною ознакою поганого вихователя є той, хто не може визнати, що вони чогось не знають. Але я відступаю. Коли я кажу, мені було абсолютно зрозуміло, що плутати це просто. Уявіть динамічний запит SQL
sqlQuery='SELECT * FROM custTable WHERE User=' + Username + ' AND Pass=' + password
отже, простою ін'єкцією sql було б просто поставити ім'я користувача як "АБО 1 = 1 - Це ефективно зробить запит sql:
sqlQuery='SELECT * FROM custTable WHERE User='' OR 1=1-- ' AND PASS=' + password
Це означає, що вибрати всіх клієнтів, де ім'я користувача є порожнім ('') або 1 = 1, що є логічним значенням, що дорівнює істині. Потім він використовує - для коментування решти запиту. Отже, це просто роздрукує всю таблицю клієнтів або зробить із нею все, що завгодно, якщо увійдете в систему, він увійде з правами першого користувача, яким часто може бути адміністратор.
Тепер параметризовані запити роблять це по-різному, з таким кодом:
sqlQuery='SELECT * FROM custTable WHERE User=? AND Pass=?'
parameters.add("User", username)
parameters.add("Pass", password)
де ім'я користувача та пароль - це змінні, що вказують на пов'язані введені ім'я користувача та пароль
Зараз на цьому етапі ви можете думати, це взагалі нічого не змінює. Звичайно, ви все ще можете просто ввести в поле імені користувача щось на зразок Ніхто АБО 1 = 1 '-, ефективно роблячи запит:
sqlQuery='SELECT * FROM custTable WHERE User=Nobody OR 1=1'-- AND Pass=?'
І це здавалося б вагомим аргументом. Але ти помилишся.
Параметризовані запити працюють так, що sqlQuery надсилається як запит, і база даних точно знає, що буде робити цей запит, і лише тоді вона буде вставляти ім’я користувача та паролі просто як значення. Це означає, що вони не можуть впливати на запит, оскільки база даних вже знає, що буде робити запит. Тож у цьому випадку він буде шукати ім’я користувача "Ніхто АБО 1 = 1 '-" та порожній пароль, який повинен бути помилковим.
Це, однак, не є повноцінним рішенням, і перевірку вводу все одно потрібно буде зробити, оскільки це не призведе до інших проблем, таких як атаки XSS, оскільки ви все одно можете помістити javascript у базу даних. Потім, якщо це прочитано на сторінці, воно відображатиметься як звичайний javascript, залежно від будь-якої перевірки вихідних даних. Тож найкраще все-таки використовувати перевірку вводу, але використовувати параметризовані запити або збережені процедури для зупинки будь-яких атак SQL.
Ну добре запитання. Відповідь є скоріше стохастичною, ніж детермінованою, і я спробую пояснити свою думку, використовуючи невеликий приклад.
У мережі є багато посилань, які пропонують нам використовувати параметри в наших запитах або використовувати збережену процедуру з параметрами, щоб уникнути SQL Injection (SQLi). Я покажу вам, що збережені процедури (наприклад) не є магічною палицею проти SQLi. Відповідальність все ще залишається на програмісті.
Розглянемо наступну процедуру зберігання SQL Server, яка отримає рядок користувача з таблиці "Користувачі":
create procedure getUser
@name varchar(20)
,@pass varchar(20)
as
declare @sql as nvarchar(512)
set @sql = 'select usrID, usrUName, usrFullName, usrRoleID '+
'from Users '+
'where usrUName = '''+@name+''' and usrPass = '''+@pass+''''
execute(@sql)
Ви можете отримати результати, передавши в якості параметрів ім'я користувача та пароль. Якщо припустити, що пароль є вільним текстом (просто для простоти цього прикладу), звичайним викликом буде:
DECLARE @RC int
DECLARE @name varchar(20)
DECLARE @pass varchar(20)
EXECUTE @RC = [dbo].[getUser]
@name = 'admin'
,@pass = '!@Th1siSTheP@ssw0rd!!'
GO
Але тут ми маємо погану техніку програмування, що використовується програмістом всередині збереженої процедури, тому зловмисник може виконати наступне:
DECLARE @RC int
DECLARE @name varchar(20)
DECLARE @pass varchar(20)
EXECUTE @RC = [TestDB].[dbo].[getUser]
@name = 'admin'
,@pass = 'any'' OR 1=1 --'
GO
Вищевказані параметри передаються як аргументи до збереженої процедури, а команда SQL, яка нарешті буде виконана, є:
select usrID, usrUName, usrFullName, usrRoleID
from Users
where usrUName = 'admin' and usrPass = 'any' OR 1=1 --'
..который поверне всі рядки від користувачів
Проблема в тому, що навіть ми дотримуємося принципу "Створити збережену процедуру і передати поля для пошуку як параметри", SQLi все ще виконується. Це тому, що ми просто копіюємо нашу погану практику програмування всередині збереженої процедури. Рішення проблеми полягає в тому, щоб переписати нашу збережену процедуру наступним чином:
alter procedure getUser
@name varchar(20)
,@pass varchar(20)
as
select usrID, usrUName, usrFullName, usrRoleID
from Users
where usrUName = @name and usrPass = @pass
Я намагаюся сказати, що розробники повинні спочатку дізнатися, що таке атака SQLi і як її можна виконати, а потім відповідно захистити свій код. Не сліпо слідувати "найкращим практикам" не завжди є більш безпечним способом ... і, можливо, саме тому у нас так багато "найкращих практик" - невдач!
Так, використання підготовлених операторів зупиняє всі введення SQL, принаймні теоретично. На практиці параметризовані оператори можуть не бути реальними підготовленими операторами, наприклад, PDO
PHP емулює їх за замовчуванням, тому він відкритий для крайової атаки .
Якщо ви використовуєте реальні підготовлені заяви, все в безпеці. Ну, принаймні до тих пір, поки ви не об'єднаєте небезпечний SQL у свій запит як реакцію на неможливість підготувати імена таблиць, наприклад.
Якщо так, чому все ще так багато успішних ін’єкцій SQL? Тільки тому, що деякі розробники занадто тупі, щоб використовувати параметризовані оператори?
Так, освіта - це головне, а основи застарілого коду. У багатьох підручниках використовується екранування, і їх, на жаль, неможливо легко видалити з Інтернету.
Я уникаю абсолютів у програмуванні; завжди є виняток. Я настійно рекомендую збережені процедури та об'єкти команд. Більшість моїх попередніх досліджень стосується SQL Server, але я час від часу граю з MySql. Зберігаються процедури мають багато переваг, включаючи кешовані плани запитів; так, це можна зробити за допомогою параметрів та вбудованого SQL, але це відкриває більше можливостей для ін'єкційних атак і не допомагає розділити проблеми. Для мене також набагато простіше захистити базу даних, оскільки мої програми, як правило, мають лише дозвіл на виконання вказаних збережених процедур. Без прямого доступу до таблиці / перегляду набагато складніше ввести щось. Якщо користувач додатків скомпрометований, один має дозвіл виконувати саме те, що було попередньо визначено.
Мої два центи.
Я б не сказав "німий".
Я думаю, що підручники - це проблема. Більшість підручників, книг та будь-якого іншого пояснення SQL із вбудованими значеннями, взагалі не згадуючи параметрів прив'язки. Люди, які навчаються за допомогою цих посібників, не мають можливості вивчити це правильно.
Оскільки більшість кодів не написані з урахуванням безпеки та управління, з огляду на вибір між додаванням функцій (особливо чогось видимого, що можна продати) та безпеки / стабільності / надійності (що набагато складніше продати), вони майже завжди вибиратимуть колишній. Безпека турбує лише тоді, коли це стає проблемою.
Чи може параметризований оператор зупинити всі введення SQL?
Так, поки ваш драйвер бази даних пропонує заповнювач для всіх можливих літералів SQL. Більшість підготовлених драйверів заяв цього не роблять. Скажімо, ви ніколи не знайдете заповнювач для імені поля чи масиву значень. Що змусить розробника знову впорядковувати запит вручну, використовуючи конкатенацію та форматування вручну. З прогнозованим результатом.
Ось чому я зробив свою обгортку Mysql для PHP, яка підтримує більшість літералів, які можна динамічно додавати до запиту, включаючи масиви та ідентифікатори.
Якщо так, чому все ще так багато успішних ін’єкцій SQL? Тільки тому, що деякі розробники занадто тупі, щоб використовувати параметризовані оператори?
Як бачите, насправді просто неможливо параметризувати всі ваші запити, навіть якщо ви не німі.
Спочатку моя відповідь на ваше перше запитання: Так, наскільки мені відомо, за допомогою параметризованих запитів введення SQL більше не буде можливим. Щодо Ваших наступних запитань, я не впевнений і можу надати Вам лише свою думку щодо причин:
Я думаю, що простіше "просто" написати рядок запиту SQL, об'єднавши деякі різні частини (можливо, навіть залежать від деяких логічних перевірок) разом зі значеннями, які потрібно вставити. Це просто створення запиту та його виконання. Ще однією перевагою є те, що ви можете надрукувати (відлуння, вихід або що завгодно) рядок запиту sql, а потім використовувати цей рядок для ручного запиту до механізму бази даних.
Під час роботи з підготовленими виписками у вас завжди є принаймні один крок більше: Ви повинні побудувати свій запит (звичайно, включаючи параметри) Ви повинні підготувати запит на сервері Ви повинні прив’язати параметри до фактичних значень, які ви хочете використовувати для вашого запиту Ви повинні виконати запит.
Це дещо більше роботи (і не так просто програмувати), особливо для деяких "швидких і брудних" робіт, які часто виявляються дуже довгоживучими ...
З найкращими побажаннями,
Коробка
Введення SQL - це підмножина більшої проблеми введення коду, де дані та код надаються за одним і тим же каналом, а дані приймаються за код. Параметризовані запити запобігають цьому, формуючи запит, використовуючи контекст про те, що таке дані, а що код.
У деяких конкретних випадках цього недостатньо. У багатьох СУБД можливо динамічно виконувати SQL із збереженими процедурами, вносячи недолік ін'єкції SQL на рівні СУБД. Виклик такої збереженої процедури за допомогою параметризованих запитів не завадить експлуатувати введення SQL у процедурі. Ще один приклад можна побачити в цій публікації в блозі .
Найчастіше розробники використовують цю функцію неправильно. Зазвичай код виглядає приблизно так, коли все зроблено правильно:
db.parameterize_query("select foo from bar where baz = '?'", user_input)
Деякі розробники об'єднують рядки разом, а потім використовують параметризований запит, який насправді не робить згаданих вище даних / коду, що забезпечує гарантії безпеки, які ми шукаємо:
db.parameterize_query("select foo from bar where baz = '" + user_input + "'")
Правильне використання параметризованих запитів забезпечує дуже сильний, але непроникний захист від атак введення SQL.
Щоб захистити свою програму від ін’єкції SQL, виконайте такі дії:
Крок 1. Обмежте введення. Крок 2. Використовуйте параметри із збереженими процедурами. Крок 3. Використовуйте параметри з динамічним SQL.
Зверніться до http://msdn.microsoft.com/en-us/library/ff648339.aspx
навіть якщо підготовлені оператори належним чином використовуються у власному коді веб-програми, недоліки введення SQL все одно можуть існувати, якщо компоненти коду бази даних будують запити з вводу користувача небезпечним чином. Нижче наведено приклад збереженої процедури, яка вразлива до введення SQL у параметрі @name:
CREATE PROCEDURE show_current_orders
(@name varchar(400) = NULL)
AS
DECLARE @sql nvarchar(4000)
SELECT @sql = ‘SELECT id_num, searchstring FROM searchorders WHERE ‘ +
‘searchstring = ‘’’ + @name + ‘’’’;
EXEC (@sql)
GO
Навіть якщо додаток передає надане користувачем значення імені збереженій процедурі безпечним чином, сама процедура об'єднує це безпосередньо в динамічний запит і тому є вразливою.