Dlaczego wstrzyknięcie SQL nie występuje w przypadku tego zapytania w procedurze przechowywanej?

18

Wykonałem następującą procedurę przechowywaną:

ALTER PROCEDURE usp_actorBirthdays (@nameString nvarchar(100), @actorgender nvarchar(100))
AS
SELECT ActorDOB, ActorName FROM tblActor
WHERE ActorName LIKE '%' + @nameString + '%'
AND ActorGender = @actorgender

Teraz próbowałem zrobić coś takiego. Może robię to źle, ale chcę mieć pewność, że taka procedura może zapobiec iniekcji SQL:

EXEC usp_actorBirthdays 'Tom', 'Male; DROP TABLE tblActor'

Poniższy obraz pokazuje, że powyższy kod SQL jest wykonywany w SSMS, a wyniki są wyświetlane poprawnie zamiast błędu:

wprowadź opis zdjęcia tutaj

Przy okazji dodałem tę część po średniku po wykonaniu zapytania. Następnie wykonałem go ponownie, ale kiedy sprawdziłem, czy tabela tblActor istnieje, czy nie, wciąż tam była. czy robię coś źle? Czy to naprawdę jest odporne na wstrzyknięcia? Myślę, że to, o co tutaj pytam, to czy taka procedura przechowywana jest bezpieczna? Dziękuję Ci.

Ravi
źródło
Próbowałeś już tegoEXEC usp_actorBirthdays 'Tom', 'Male''; DROP TABLE tblActor'
MarmiK,

Odpowiedzi:

38

Ten kod działa poprawnie, ponieważ jest:

  1. Sparametryzowane oraz
  2. Nie robienie dynamicznego SQL

Aby SQL Injection działało, musisz zbudować ciąg zapytania (czego nie robisz) i nie tłumaczyć pojedynczych apostrofów ( ') na escaped-apostrophes ( '') (są one zmieniane za pomocą parametrów wejściowych).

W próbie przekazania „skompromitowanej” wartości 'Male; DROP TABLE tblActor'ciąg jest po prostu zwykłym ciągiem.

Teraz, jeśli robisz coś w stylu:

DECLARE @SQL NVARCHAR(MAX);

SET @SQL = N'SELECT fields FROM table WHERE field23 = '
          + @InputParam;

EXEC(@SQL);

Następnie , który byłby podatny na SQL zastrzyku, z powodu , że zapytania nie jest prąd, wstępnie Parsed kontekście; to zapytanie jest w tej chwili tylko kolejnym ciągiem. Tak więc wartość @InputParammoże być '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;i może stanowić problem, ponieważ to zapytanie zostanie zrenderowane i wykonane jako:

SELECT fields FROM table WHERE field23 = '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;

Jest to jeden (z kilku) głównych powodów, dla których warto stosować Procedury składowane: z natury bardziej bezpieczne (tak długo, jak nie obejdziesz tego bezpieczeństwa, budując zapytania, jak pokazałem powyżej, bez sprawdzania poprawności wartości użytych parametrów). Chociaż jeśli chcesz zbudować Dynamiczny SQL, preferowanym sposobem jest sparametryzowanie go również za pomocą sp_executesql:

DECLARE @SQL NVARCHAR(MAX);

SET @SQL = N'SELECT fields FROM table WHERE field23 = @SomeDate_tmp';

EXEC sp_executesql
  @SQL,
  N'SomeDate_tmp DATETIME',
  @SomeDate_tmp = @InputParam;

Stosując to podejście, ktoś próbuje przekazać w '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;dla DATETIMEparametru wejściowego będzie się błąd podczas wykonywania procedury przechowywanej. Lub nawet jeśli procedura przechowywana zostanie zaakceptowana @InputParameterjako NVARCHAR(100), musiałaby zostać przekonwertowana na a DATETIME, aby przejść do tego sp_executesqlwywołania. I nawet jeśli parametr w dynamicznym SQL jest typem ciągowym, w pierwszej kolejności w procedurze przechowywanej każdy pojedynczy apostrof automatycznie uciekłby do podwójnego apostrofu.

Istnieje mniej znany rodzaj ataku, w którym atakujący próbuje wypełnić pole wejściowe apostrofami, tak że ciąg wewnątrz procedury składowanej, który zostałby użyty do skonstruowania dynamicznego SQL, ale zadeklarowany jako zbyt mały, nie zmieści się we wszystkim i wypycha końcowy apostrof i jakoś kończy z prawidłową liczbą apostrofów, aby nie było już „ucieczki” w ciągu. Nazywa się to Obcinaniem SQL i zostało wspomniane w artykule magazynu MSDN zatytułowanym „Nowe ataki obcięcia SQL i jak ich unikać” autorstwa Bala Neerumalla, ale artykuł nie jest już dostępny online. Wydanie zawierające ten artykuł - wydanie MSDN Magazine z listopada 2006 r. - jest dostępne tylko jako plik Pomocy systemu Windows (w .chmformat). Jeśli go pobierzesz, może się nie otworzyć z powodu domyślnych ustawień zabezpieczeń. Jeśli tak się stanie, kliknij prawym przyciskiem myszy plik MSDNMagazineNovember2006en-us.chm i wybierz „Właściwości”. Na jednej z tych kart będzie opcja „Ufaj temu typowi pliku” (lub coś w tym rodzaju), którą należy sprawdzić / włączyć. Kliknij przycisk „OK”, a następnie spróbuj ponownie otworzyć plik .chm .

Inną odmianą ataku Obcinanie jest założenie, że zmienna lokalna jest używana do przechowywania „bezpiecznej” wartości dostarczonej przez użytkownika, ponieważ zawierała ona podwójne cudzysłowy, aby można było je uciec, aby wypełnić zmienną lokalną i umieścić cudzysłów na końcu. Chodzi o to, że jeśli zmienna lokalna nie ma odpowiedniego rozmiaru, na końcu nie będzie wystarczającej ilości miejsca na drugi pojedynczy cytat, pozostaw zmienną kończącą się pojedynczym cytatem, który następnie łączy się z pojedynczym cytatem, który kończy wartość literału w dynamicznym SQL, zamieniając ten kończący pojedynczy cudzysłów w osadzony znak ucieczki pojedynczego cudzysłowu, a literał ciągów w dynamicznym SQL następnie kończy się kolejnym pojedynczym cudzysłowiem, który miał rozpocząć następny ciąg literału. Na przykład:

-- Parameters:
DECLARE @UserID      INT = 37,
        @NewPassword NVARCHAR(15) = N'Any Value ....''',
        @OldPassword NVARCHAR(15) = N';Injected SQL--';

-- Stored Proc:
DECLARE @SQL NVARCHAR(MAX),
        @NewPassword_fixed NVARCHAR(15) = REPLACE(@NewPassword, N'''', N''''''),
        @OldPassword_fixed NVARCHAR(15) = REPLACE(@OldPassword, N'''', N'''''');

SELECT @NewPassword AS [@NewPassword],
       REPLACE(@NewPassword, N'''', N'''''') AS [REPLACE output],
       @NewPassword_fixed AS [@NewPassword_fixed];
/*
@NewPassword          REPLACE output          @NewPassword_fixed
Any Value ....'       Any Value ....''        Any Value ....'
*/

SELECT @OldPassword AS [@OldPassword],
       REPLACE(@OldPassword, N'''', N'''''') AS [REPLACE output],
       @OldPassword_fixed AS [@OldPassword_fixed];
/*
@OldPassword          REPLACE output          @OldPassword_fixed
;Injected SQL--       ;Injected SQL--         ;Injected SQL--
*/

SET @SQL = N'UPDATE dbo.TableName SET [Password] = N'''
           + @NewPassword_fixed + N''' WHERE [TableNameID] = '
           + CONVERT(NVARCHAR(10), @UserID) + N' AND [Password] = N'''
           + @OldPassword_fixed + N''';';

SELECT @SQL AS [Injected];

Tutaj dynamiczny SQL do wykonania to teraz:

UPDATE dbo.TableName SET [Password] = N'Any Value ....'' WHERE [TableNameID] = 37 AND [Password] = N';Injected SQL--';

Ten sam dynamiczny SQL w bardziej czytelnym formacie to:

UPDATE dbo.TableName SET [Password] = N'Any Value ....'' WHERE [TableNameID] = 37 AND [Password] = N';

Injected SQL--';

Naprawienie tego jest łatwe. Po prostu wykonaj jedną z następujących czynności:

  1. NIE UŻYWAJ DYNAMICZNEGO SQL, JEŚLI ABSOLUTNIE KONIECZNIE! (Wymieniam to jako pierwsze, ponieważ to naprawdę powinno być pierwszą rzeczą do rozważenia).
  2. Prawidłowo zwymiaruj zmienną lokalną (tzn. Powinna być dwa razy większa niż parametr wejściowy, na wypadek, gdyby wszystkie przekazane znaki były cudzysłowami).
  3. Nie używaj zmiennej lokalnej do przechowywania wartości „stałej”; wystarczy umieścić REPLACE()bezpośrednio w tworzeniu Dynamicznego SQL:

    SET @SQL = N'UPDATE dbo.TableName SET [Password] = N'''
               + REPLACE(@NewPassword, N'''', N'''''') + N''' WHERE [TableNameID] = '
               + CONVERT(NVARCHAR(10), @UserID) + N' AND [Password] = N'''
               + REPLACE(@OldPassword, N'''', N'''''') + N''';';
    
    SELECT @SQL AS [No SQL Injection here];

    Dynamiczny SQL nie jest już zagrożony:

    UPDATE dbo.TableName SET [Password] = N'Any Value ....''' WHERE [TableNameID] = 37 AND [Password] = N';Injected SQL--';

Uwagi na temat powyższego przykładu Trunction:

  1. Tak, to bardzo wymyślony przykład. Wstrzyknięcie nie ma wiele do zrobienia za pomocą zaledwie 15 znaków. Pewnie, może DELETE tableNamebyć destrukcyjny, ale rzadziej doda użytkownika back-door lub zmieni hasło administratora.
  2. Ten rodzaj ataku prawdopodobnie wymaga znajomości kodu, nazw tabel itp. Mniej prawdopodobne jest, że zostanie to wykonane przez przypadkowego nieznajomego / skrypciarza, ale pracowałem w miejscu, które zostało zaatakowane przez raczej zdenerwowanego byłego pracownika, który wiedział o luce na jednej konkretnej stronie internetowej, o której nikt inny nie wiedział. Oznacza to, że czasami atakujący mają dogłębną wiedzę o systemie.
  3. Pewnie zresetowanie hasła wszystkich osób może zostać zbadane, co może poinformować firmę, że ma miejsce atak, ale wciąż może zapewnić wystarczającą ilość czasu na wstrzyknięcie użytkownikowi back-door lub może uzyskać dodatkowe informacje do wykorzystania / wykorzystania później.
  4. Nawet jeśli ten scenariusz jest w większości akademicki (tzn. Prawdopodobnie nie wydarzy się w prawdziwym świecie), nadal nie jest to niemożliwe.

Aby uzyskać bardziej szczegółowe informacje dotyczące SQL Injection (obejmujące różne RDBMS i scenariusze), zobacz następujące informacje z Open Web Application Security Project (OWASP):
Testowanie SQL Injection

Powiązana odpowiedź dotycząca przepełnienia stosu podczas wstrzykiwania SQL i obcinania SQL:
Jak bezpieczny jest T-SQL po zamianie znaku zmiany znaczenia?

Solomon Rutzky
źródło
2
Och, dziękuję bardzo, to świetna odpowiedź. Teraz rozumiem. Naprawdę chciałbym zobaczyć technikę, o której wspomniałeś na końcu, w której atakujący próbuje wypełnić pole wejściowe apostrofami, jeśli możesz je znaleźć. Z góry dziękuję. Mam zamiar pozostać otwartym, na wypadek, gdybyście go nie znaleźli, wybiorę to jako odpowiedź.
Ravi,
1
@Ravi Znalazłem link, ale nie przechodzi on już do artykułu, ponieważ wszystkie są teraz zarchiwizowane. Ale dodałem trochę informacji i użytecznych linków i nadal próbuję znaleźć artykuł w tych archiwach.
Solomon Rutzky,
1
Dziękuję śrutzsky, przeczytam artykuł OWASP i testy wstrzyknięć. Jeśli dobrze pamiętam, „mutillidae”, podatna na ataki aplikacja internetowa do testowania bezpieczeństwa, ma wstrzyknięcie SQL, które wykonałam na studiach przy użyciu ciągu „OR 1 = 1”, co w mutillidae spowodowało, że zalogowałem się do aplikacji jako administrator I myśleć. Wtedy po raz pierwszy zapoznałem się z iniekcją SQL.
Ravi,
1
Nie mogę również wyświetlić pliku .chm, ale dziękuję za tę idealną odpowiedź i za wszystkie pomocne linki, w tym ten z stackoverflow i ten z OWASP. Przeczytałem to dzisiaj i wiele się z tego nauczyłem.
Ravi
2

Prostą sprawą jest to, że w ogóle nie mylisz danych z poleceniami. Wartości parametrów nigdy nie są traktowane jako część polecenia i dlatego nigdy nie są wykonywane.

Napisałem o tym na blogu: http://blogs.lobsterpot.com.au/2015/02/10/sql-injection-the-golden-rule/

Rob Farley
źródło
Dziękuję Rob, mam tę zakładkę do przeczytania później w ciągu dnia.
Ravi,