Чи запобігають збережені процедури запобігання ін'єкції SQL?


83

Чи правда, що збережені процедури запобігають атакам ін'єкцій SQL проти баз даних PostgreSQL? Я провів невелике дослідження і виявив, що SQL Server, Oracle і MySQL не безпечні проти ін'єкцій SQL, навіть якщо ми використовуємо лише збережені процедури. Однак цієї проблеми не існує в PostgreSQL.

Чи запобігає реалізація збереженої процедури в ядрі PostgreSQL запобігання атакам ін'єкцій SQL чи це щось інше? Або PostgreSQL також сприйнятливий до ін'єкції SQL, навіть якщо ми використовуємо лише збережені процедури? Якщо це так, будь ласка, покажіть мені приклад (наприклад, книга, сайт, папір тощо).


4
Як не дивно, тут найкращі відповіді - це ОТ, що стосуються SQL Server, тоді як питання про Postgres . Ось споріднений відповідь для Postgres: dba.stackexchange.com/questions/49699 / ... . Є ще декілька інших, спробуйте пошук: dba.stackexchange.com/…
Ервін

@ErwinBrandstetter оригінальне запитання не позначалось (ОП) постгресами і було - і досі - згадує кілька інших СУБД. Я думаю, це причина різних відповідей, зосереджених на інших СУБД. Я пропоную вам додати ще один акцент на Postgres.
ypercubeᵀᴹ

@ ypercubeᵀᴹ: Я додам відповідь тут, коли знайду час. Тим часом я оновив dba.stackexchange.com/questions/49699/… на більш чітке та всебічне.
Ервін Брандстеттер

Відповіді:


71

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

Цей код сервера sql:

CREATE PROCEDURE [dbo].[sp_colunmName2]   
    @columnName as nvarchar(30),
    @type as nvarchar(30), 
    @searchText as nvarchar(30)           
AS
BEGIN
    DECLARE @SQLStatement NVARCHAR(4000)
    BEGIN
        SELECT @SQLStatement = 'select * from Stations where ' 
            + @columnName + ' ' + @type + ' ' + '''' + @searchText + '''' 
        EXEC(@SQLStatement)
    END      
END
GO

приблизно еквівалентний postgres:

CREATE or replace FUNCTION public.sp_colunmName2 (
    columnName  varchar(30),
    type varchar(30), 
    searchText  varchar(30) ) RETURNS SETOF stations LANGUAGE plpgsql            
AS
$$
DECLARE SQLStatement VARCHAR(4000);
BEGIN
    SQLStatement = 'select * from Stations where ' 
            || columnName || ' ' || type || ' ' || ''''|| searchText || '''';
    RETURN QUERY EXECUTE  SQLStatement;
END
$$;

Ідея розробника полягала в тому, щоб створити універсальну процедуру пошуку, але результат полягає в тому, що пункт WHERE може містити все, що користувач хоче, що дозволяє відвідувати маленькі таблиці Bobby .

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


46

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

Таким чином, ми отримуємо ситуацію:

userInput = getFromHTML # "Роберт") Студенти викидають таблицю; - "

Запит = "Вибрати * з учнів, де studentName =" + userInput

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

У збереженій процедурі в більшості БД (і програм, не забувайте, що попередньо складені запити вважаються збереженими процедурами) виглядають так:

 

створити Foo зберігання замовлення (
виберіть * від студентів, де StudentName =: 1
);

Потім, коли програма бажає доступу, вона дзвонить foo(userInput)і радісно отримує результат.

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

Ви можете прочитати більше про SQL-ін'єкції:


29

Так, певною мірою.
Самі збережені процедури не завадять ін'єкції SQL.

Дозвольте мені спочатку процитувати SQL Injection від OWASP

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

Вам потрібно санітувати введення користувачів та не об'єднувати оператори SQL, навіть якщо ви використовуєте збережену процедуру.

Джефф Еттвуд пояснив наслідки об'єднання sql у " Надай мені параметризований SQL або дай мені смерть "

Далі йде цікавий мультфільм, який мені спадає на думку щоразу, коли я чую SQL Injection, alt текст я думаю, що ти зрозумів :-)

Погляньте на чіт-лист SQL Injection Prevention , методи профілактики чітко пояснені ...


12

Зв'язок рядків є причиною ін'єкції SQL. Цього уникнути за допомогою параметризації.

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

Отже, ваш код вище викликаний конкатенацією цих рядків

  • exec sp_GetUser '
  • x' AND 1=(SELECT COUNT(*) FROM Client); --
  • ' , '
  • monkey
  • '

Це дає недійсний синтаксис, на щастя

Параметризація це дала б

exec sp_GetUser 'x'' AND 1=(SELECT COUNT(*) FROM Client); --' , 'monkey'

Це означає

  • @UserName = x' AND 1=(SELECT COUNT(*) FROM Client); --
  • @Password = monkey

Тепер у коді вище ви не отримаєте рядків, тому що я вважаю, що у вас немає користувача x' AND 1=(SELECT COUNT(*) FROM Client); --

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

...
SET @sql = 'SELECT userName from users where userName = ''' + 
               @UserName + 
               ''' and userPass = ''' +
               @Password +
               ''''
EXEC (@sql)
....

Отже, як показано, конкатенація рядків є головним ворогом для ін'єкції SQL

Збережені процедури додають інкапсуляцію, обробку транзакцій, зменшення дозволів тощо, але вони все одно можуть бути зловживані для ін'єкції SQL.

Щоб дізнатися більше про параметризацію, ви можете подивитися на переповнення стека


10

«Атака ін'єкції SQL відбувається , коли для користувача введення неправильно закодований. Як правило, введення даних користувача деякі дані користувача відправляються з її запитом, тобто значення в $_GET, $_POST, $_COOKIE, $_REQUESTабо $_SERVERмасиви. Однак, для користувача введення можуть також надходити з безлічі інших такі джерела, як сокети, віддалені веб-сайти, файли тощо. Отже, ви дійсно повинні ставитися до всього, окрім констант (наприклад 'foobar'), як до користувачів . "

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



З YouTube


З Вікіпедії


Від OWASP


З посібника PHP


Від Microsoft та Oracle


Переповнення стеку


Сканер для ін'єкцій SQL


2

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

CREATE OR REPLACE FUNCTION my_func (
  IN in_user_id INT 
)
[snip]
  SELECT user_id, name, address FROM my_table WHERE user_id = in_user_id; --BAM! SQL INJECTION IMMUNE!!
[snip]

Це воно! Проблема виникає лише при формуванні запиту за допомогою конкатенації рядків (тобто динамічного SQL), і навіть у тих випадках ви можете бути в змозі зв’язати! (Залежить від бази даних.)

Як уникнути ін'єкції SQL у своєму динамічному запиті:

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

Крок 2) Дослідження правильного способу встановлення змінної для конкретного RDBMS. Наприклад, Oracle дозволяє зробити наступне (цитуючи їх документи):

sql_stmt := 'UPDATE employees SET salary = salary + :1 WHERE ' 
           || v_column || ' = :2';
EXECUTE IMMEDIATE sql_stmt USING amount, column_value; --INJECTION IMMUNE!!

Тут ви все ще не об'єднуєте вхід. Ви надійно зобов’язуєтесь! Ура!

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

sql_stmt := 'SELECT salary FROM employees WHERE name = ' || quote_literal(in_name);

Таким чином, якщо in_name є чимось хитрим, як-от "[snip] або 1 = 1" (частина "або 1 = 1" означає виділити всі рядки, що дозволяє користувачеві бачити зарплату, якої він не повинен!), А потім quo_literal зберігає ваш зад Створення отриманого рядка:

SELECT salary FROM employees WHERE name = '[snip] or 1=1'

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

У цьому суть! Тепер дозвольте залишити вас посиланням на класичну публікацію гуру Oracle Тома Кейта на тему SQL Injection, щоб перенести справу додому: Linky


Не забудьте згадати quote_ident()- але загалом найпростіший спосіб написати динамічний SQL, захищений від ін'єкцій, - це використовувати format()та використовувати заповнювачі %Iдля ідентифікаторів та %Lдля літералів. Таким чином, SQL набагато читає, ніж еквівалентна версія з використанням ||та quote_....()функціями
a_horse_with_no_name
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.