Czy procedury przechowywane zapobiegają iniekcji SQL?


83

Czy to prawda, że ​​procedury przechowywane zapobiegają atakom typu SQL injection na bazy danych PostgreSQL? Zrobiłem trochę badań i odkryłem, że SQL Server, Oracle i MySQL nie są bezpieczne przed iniekcją SQL, nawet jeśli używamy tylko procedur przechowywanych. Jednak ten problem nie występuje w PostgreSQL.

Czy implementacja procedury składowanej w rdzeniu PostgreSQL zapobiega atakom typu SQL injection, czy jest to coś innego? Czy też PostgreSQL jest również podatny na wstrzykiwanie SQL, nawet jeśli używamy tylko procedur przechowywanych? Jeśli tak, pokaż mi przykład (np. Książka, strona, papier itp.).


4
O dziwo, najważniejsze odpowiedzi tutaj to głównie OT zajmujące się SQL Server, podczas gdy pytanie dotyczy Postgres . Oto powiązany odpowiedź dla PostgreSQL: dba.stackexchange.com/questions/49699/... . Jest jeszcze kilka innych, spróbuj wyszukać: dba.stackexchange.com/…
Erwin Brandstetter

@ErwinBrandstetter oryginalne pytanie nie zostało otagowane (przez OP) postgres i było - i nadal - wspomina o kilku innych DBMS. Myślę, że to jest powód różnych odpowiedzi skupiających się na innych DBMS. Sugeruję dodanie jeszcze jednego skoncentrowanego na Postgresie.
ypercubeᵀᴹ

@ ypercubeᵀᴹ: Dodam odpowiedź, kiedy znajdę czas. W międzyczasie zaktualizowałem dba.stackexchange.com/questions/49699/... do bardziej przejrzystych i kompleksowych.
Erwin Brandstetter

Odpowiedzi:


71

Nie, procedury składowane nie zapobiegają iniekcji SQL. Oto rzeczywisty przykład (z aplikacji wewnętrznej stworzonej przeze mnie, gdzie pracuję) procedury składowanej, która niestety pozwala na wstrzyknięcie SQL:

Ten kod serwera 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

mniej więcej odpowiednik postgresu:

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

Pomysł dewelopera polegał na stworzeniu wszechstronnej procedury wyszukiwania, ale wynik jest taki, że klauzula WHERE może zawierać wszystko, czego chce użytkownik, umożliwiając odwiedziny z niewielkich tabel Bobby'ego .

To, czy używasz instrukcji SQL, czy procedury przechowywanej, nie ma znaczenia. Liczy się to, czy Twój SQL używa parametrów lub połączonych ciągów. Parametry zapobiegają iniekcji SQL; połączone łańcuchy umożliwiają wstrzyknięcie SQL.


46

Ataki typu SQL-Injection to takie, w których niezaufane dane wejściowe są bezpośrednio dołączane do zapytań, umożliwiając użytkownikowi skuteczne wykonanie dowolnego kodu, jak pokazano w tym kanonicznym komiksie XKCD.

W ten sposób otrzymujemy sytuację:

userInput = getFromHTML # "Robert ') Upuść uczniów tabeli; -"

Zapytanie = „Wybierz * spośród studentów, gdzie studentName =” + userInput

Procedury przechowywane są ogólnie dobrą ochroną przed atakami iniekcyjnymi SQL, ponieważ przychodzące parametry nigdy nie są analizowane.

W procedurze przechowywanej w większości baz danych (i programów nie zapominaj, że wstępnie skompilowane zapytania liczą się jako procedury składowane) wyglądają następująco:

 

utwórz Zapisaną procedurę foo (
wybierz * spośród studentów, gdzie studentName =: 1
);

Następnie, gdy program chce uzyskać dostęp, wywołuje foo(userInput)i szczęśliwie pobiera wynik.

Procedura przechowywana nie jest magiczną obroną przed SQL-Injection, ponieważ ludzie są w stanie pisać złe procedury przechowywane. Jednak wstępnie skompilowane zapytania, niezależnie od tego, czy są przechowywane w bazie danych, czy w programie, są znacznie trudniejsze do otwarcia luk bezpieczeństwa, jeśli zrozumiesz, jak działa SQL-Injection.

Możesz przeczytać więcej o SQL-Injection:


29

Tak, do pewnego stopnia.
Same przechowywane procedury nie zapobiegną wstrzyknięciu SQL.

Pozwól mi najpierw zacytować O SQL Injection z OWASP

Atak polegający na wstrzyknięciu SQL polega na wstawieniu lub „wstrzyknięciu” zapytania SQL za pośrednictwem danych wejściowych od klienta do aplikacji. Udany exploit iniekcji SQL może odczytywać poufne dane z bazy danych, modyfikować dane bazy danych (Wstaw / Aktualizuj / Usuń), wykonywać operacje administracyjne na bazie danych (takie jak zamknięcie DBMS), odzyskać zawartość danego pliku obecnego w pliku DBMS system, aw niektórych przypadkach wydaje polecenia do systemu operacyjnego. Ataki polegające na wstrzykiwaniu SQL są rodzajem ataku polegającego na wstrzykiwaniu, w którym polecenia SQL są wstrzykiwane do danych wejściowych płaszczyzny danych w celu wykonania określonych poleceń SQL.

Musisz oczyścić dane wejściowe użytkownika i nie łączyć instrukcji SQL, nawet jeśli korzystasz z procedury składowanej.

Jeff Attwood wyjaśnił konsekwencje konkatenacji sql w „ Daj mi sparametryzowany SQL lub daj mi śmierć

Oto ciekawa kreskówka, która przychodzi mi do głowy, gdy słyszę SQL Injection. alternatywny tekst Myślę, że masz rację :-)

Zajrzyj do ściągawki SQL Injection Prevention Cheat Sheet , metody zapobiegania są starannie wyjaśnione ...


12

Łączenie ciągów jest przyczyną wstrzyknięcia SQL. Można tego uniknąć za pomocą parametryzacji.

Procedury przechowywane dodają dodatkową warstwę bezpieczeństwa, wymuszając niepoprawną składnię podczas konkatenacji, ale nie są „bezpieczniejsze”, jeśli użyjesz w nich, na przykład, dynamicznego SQL.

Tak więc powyższy kod jest spowodowany łączeniem tych ciągów

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

Na szczęście daje to niepoprawną składnię

Parametryzacja dałaby

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

To znaczy

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

Teraz w powyższym kodzie nie będzie żadnych wierszy, ponieważ zakładam, że nie masz użytkownika x' AND 1=(SELECT COUNT(*) FROM Client); --

Jeśli przechowywany proc wyglądał tak (przy użyciu skonkatowanego dynamicznego SQL ), wówczas sparametryzowane przechowywane wywołanie proc nadal pozwoli na wstrzyknięcie SQL

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

Tak więc, jak wykazano, konkatenacja łańcuchów jest głównym wrogiem dla wstrzykiwania SQL

Procedury przechowywane dodają enkapsulację, obsługę transakcji, zmniejszone uprawnienia itp., Ale nadal mogą być nadużywane do wstrzykiwania SQL.

Możesz sprawdzić Przepełnienie stosu, aby uzyskać więcej informacji na temat parametryzacji


10

„Ataki SQL injection zdarzyć, gdy dane wejściowe użytkownika jest niewłaściwie zakodowany. Zazwyczaj wejście użytkownik niektóre dane użytkownik wysyła ze swojego zapytania, czyli wartości w $_GET, $_POST, $_COOKIE, $_REQUEST, lub $_SERVERtablic. Jednak wejście użytkownik może również pochodzić z różnych innych źródła, takie jak gniazda, zdalne strony internetowe, pliki itp. Dlatego naprawdę powinieneś traktować wszystko oprócz stałych (jak 'foobar') jako dane wprowadzane przez użytkownika . ”

Niedawno dokładnie badałem ten temat i chciałbym podzielić się z innymi dość interesującym materiałem, dzięki czemu ten post jest bardziej kompletny i pouczający dla wszystkich.



Z YouTube


Z Wikipedii


Od OWASP


Z podręcznika PHP


Od Microsoft i Oracle


Przepełnienie stosu


Skaner wtrysku SQL


2

Procedury przechowywane nie magicznie zapobiegają iniekcji SQL, ale znacznie ułatwiają zapobieganie. Wszystko co musisz zrobić, to coś takiego (przykład 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]

Otóż ​​to! Problem pojawia się tylko podczas tworzenia zapytania poprzez konkatenację ciągów (tj. Dynamiczny SQL), a nawet w takich przypadkach możesz być w stanie powiązać! (Zależy od bazy danych.)

Jak uniknąć wstrzykiwania SQL w zapytaniu dynamicznym:

Krok 1) Zadaj sobie pytanie, czy naprawdę potrzebujesz dynamicznego zapytania. Jeśli sklejasz ciągi znaków, aby ustawić dane wejściowe, prawdopodobnie robisz to źle. (Istnieją wyjątki od tej reguły - jeden wyjątek dotyczy raportowania zapytań w niektórych bazach danych, możesz mieć problemy z wydajnością, jeśli nie zmusisz go do kompilacji nowego zapytania przy każdym wykonaniu. Ale zbadaj ten problem, zanim w niego wejdziesz. )

Krok 2) Zbadaj właściwy sposób ustawienia zmiennej dla określonego RDBMS. Na przykład Oracle pozwala wykonać następujące czynności (cytowanie z ich dokumentów):

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

Tutaj nadal nie łączysz danych wejściowych. Jesteś bezpiecznie wiążący! Brawo!

Jeśli twoja baza danych nie obsługuje czegoś takiego jak powyższe (mam nadzieję, że żadna z nich nie jest jeszcze taka zła, ale nie byłbym zaskoczony) - lub jeśli nadal naprawdę musisz połączyć swoje dane wejściowe (jak w przypadku „czasami” zgłaszania zapytań jako Wskazałem powyżej), musisz użyć właściwej funkcji ucieczki. Nie pisz tego sam. Na przykład postgres udostępnia funkcję quote_literal (). Więc biegniesz:

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

W ten sposób, jeśli in_name jest czymś przebiegłym, np. „[Snip] lub 1 = 1” (część „lub 1 = 1” oznacza zaznaczenie wszystkich wierszy, pozwalając użytkownikowi zobaczyć pensje, których nie powinien!), To quote_literal zapisuje tyłek przez utworzenie wynikowego ciągu:

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

Nie zostaną znalezione żadne wyniki (chyba że masz pracowników o naprawdę dziwnych nazwiskach).

To jest sedno tego! Teraz pozwólcie, że zostawię wam link do klasycznego postu guru Oracle, Toma Kyte'a na temat SQL Injection, aby doprowadzić do sedna sprawy: Linky


Nie zapomnij wspomnieć quote_ident()- ale generalnie najłatwiejszym sposobem napisania odpornego na wstrzyknięcia dynamicznego SQL jest użycie format()i użycie symboli zastępczych %Idla identyfikatorów i %Lliterałów. W ten sposób SQL jest znacznie bardziej czytelny niż odpowiednik używający wersji ||i quote_....()funkcji
a_horse_w_na_name
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.