Jeśli tak, dlaczego wciąż jest tak wiele udanych wstrzyknięć SQL? Tylko dlatego, że niektórzy programiści są zbyt głupi, aby używać sparametryzowanych instrukcji?
Jeśli tak, dlaczego wciąż jest tak wiele udanych wstrzyknięć SQL? Tylko dlatego, że niektórzy programiści są zbyt głupi, aby używać sparametryzowanych instrukcji?
Odpowiedzi:
Linki, które zamieściłem w komentarzach do pytania, bardzo dobrze wyjaśniają problem. Poniżej podsumowałem swoje odczucia dotyczące przyczyny problemu:
Ci, którzy dopiero zaczynają, mogą nie mieć świadomości iniekcji SQL.
Niektórzy zdają sobie sprawę z iniekcji SQL, ale uważają, że ucieczka jest (jedynym?) Rozwiązaniem. Jeśli przeprowadzisz szybkie wyszukiwanie w Google php mysql query
, pierwszą wyświetloną stroną jest mysql_query
strona, na której znajduje się przykład pokazujący interpolację wprowadzonych przez użytkownika znaków ucieczki w zapytaniu. Nie ma wzmianki (przynajmniej tego nie widzę) o używaniu zamiast tego przygotowanych instrukcji. Jak powiedzieli inni, istnieje tak wiele samouczków, które używają interpolacji parametrów, że nie jest zaskakujące, jak często jest ona nadal używana.
Brak zrozumienia działania sparametryzowanych instrukcji. Niektórzy uważają, że to tylko wymyślny sposób na ucieczkę od wartości.
Inni są świadomi sparametryzowanych instrukcji, ale nie używają ich, ponieważ słyszeli, że są zbyt wolne. Podejrzewam, że wiele osób słyszało, jak niewiarygodnie powolne są sparametryzowane oświadczenia, ale tak naprawdę nie przeprowadzili żadnych własnych testów. Jak zauważył Bill Karwin w swoim wystąpieniu, różnica w wykonaniu rzadko powinna być brana pod uwagę jako czynnik przy rozważaniu wykorzystania przygotowanych wypowiedzi. Korzyści płynące z jednorazowego przygotowania, wykonania wielu , często wydają się zapomniane, podobnie jak ulepszenia w zakresie bezpieczeństwa i łatwości utrzymania kodu.
Niektórzy używają sparametryzowanych instrukcji wszędzie, ale z interpolacją niesprawdzonych wartości, takich jak nazwy tabel i kolumn, słowa kluczowe i operatory warunkowe. Wyszukiwania dynamiczne, takie jak te, które pozwalają użytkownikom na określenie wielu różnych pól wyszukiwania, warunków porównania i porządku sortowania, są tego najlepszym przykładem.
Fałszywe poczucie bezpieczeństwa podczas korzystania z ORM. ORM nadal umożliwiają interpolację części instrukcji SQL - patrz 5.
Programowanie to duży i złożony temat, zarządzanie bazą danych to duży i złożony temat, bezpieczeństwo to duży i złożony temat. Tworzenie bezpiecznej aplikacji bazodanowej nie jest łatwe - nawet doświadczeni programiści mogą dać się złapać.
Wiele odpowiedzi na stackoverflow nie pomaga. Kiedy ludzie piszą pytania, które wykorzystują dynamiczny SQL i interpolację parametrów, często brakuje odpowiedzi sugerujących użycie zamiast tego sparametryzowanych instrukcji. Przy kilku okazjach ludzie odrzucali moją sugestię użycia przygotowanych stwierdzeń - zwykle z powodu postrzeganego niedopuszczalnego narzutu wydajności. Poważnie wątpię, czy osoby zadające większość z tych pytań są w sytuacji, w której kilka dodatkowych milisekund potrzebnych na przygotowanie sparametryzowanej instrukcji będzie miało katastrofalny wpływ na ich zastosowanie.
Kiedy artykuły mówią o sparametryzowanych zapytaniach zatrzymujących ataki SQL, tak naprawdę nie wyjaśniają dlaczego. Często jest to przypadek „Tak, więc nie pytaj dlaczego” - prawdopodobnie dlatego, że sami nie wiedzą. Pewnym znakiem złego nauczyciela jest taki, który nie może przyznać się, że czegoś nie wie. Ale błądzę. Kiedy mówię, że jest to całkowicie zrozumiałe, bycie zdezorientowanym jest proste. Wyobraź sobie dynamiczne zapytanie SQL
sqlQuery='SELECT * FROM custTable WHERE User=' + Username + ' AND Pass=' + password
więc proste wstrzyknięcie sql byłoby po prostu wstawieniem nazwy użytkownika jako `` LUB 1 = 1 - to skutecznie spowodowałoby, że zapytanie sql '':
sqlQuery='SELECT * FROM custTable WHERE User='' OR 1=1-- ' AND PASS=' + password
Oznacza to, że wybierz wszystkich klientów, których nazwa użytkownika jest pusta („”) lub 1 = 1, co jest wartością logiczną równą prawdzie. Następnie używa - do skomentowania pozostałej części zapytania. Więc to po prostu wydrukuje całą tabelę klientów lub zrobi z nią co chcesz, jeśli się zalogujesz, zaloguje się z uprawnieniami pierwszego użytkownika, którym często może być administrator.
Teraz sparametryzowane zapytania robią to inaczej, z kodem takim jak:
sqlQuery='SELECT * FROM custTable WHERE User=? AND Pass=?'
parameters.add("User", username)
parameters.add("Pass", password)
gdzie nazwa użytkownika i hasło to zmienne wskazujące na skojarzoną, wprowadzoną nazwę użytkownika i hasło
W tym momencie możesz pomyśleć, że to niczego nie zmienia. Z pewnością nadal możesz po prostu wpisać w pole nazwy użytkownika coś takiego jak Nikt LUB 1 = 1 '-, skutecznie tworząc zapytanie:
sqlQuery='SELECT * FROM custTable WHERE User=Nobody OR 1=1'-- AND Pass=?'
Wydaje się, że to ważny argument. Ale byłbyś w błędzie.
Sposób działania zapytań sparametryzowanych polega na tym, że sqlQuery jest wysyłane jako zapytanie, a baza danych dokładnie wie, co zrobi to zapytanie, i dopiero wtedy wstawi nazwę użytkownika i hasła jedynie jako wartości. Oznacza to, że nie mogą wykonać zapytania, ponieważ baza danych już wie, co zrobi zapytanie. Więc w tym przypadku szukałby nazwy użytkownika „Nikt LUB 1 = 1 '-” i pustego hasła, co powinno być fałszywe.
Nie jest to jednak kompletne rozwiązanie i nadal trzeba będzie przeprowadzić walidację danych wejściowych, ponieważ nie wpłynie to na inne problemy, takie jak ataki XSS, ponieważ nadal można umieścić javascript w bazie danych. Następnie, jeśli zostanie to odczytane na stronie, wyświetli to jako normalny skrypt javascript, w zależności od walidacji wyjścia. Więc naprawdę najlepszą rzeczą do zrobienia jest nadal sprawdzanie poprawności danych wejściowych, ale używanie sparametryzowanych zapytań lub procedur składowanych w celu powstrzymania wszelkich ataków SQL.
Cóż, dobre pytanie. Odpowiedź jest bardziej stochastyczna niż deterministyczna i spróbuję wyjaśnić swój pogląd na małym przykładzie.
W sieci jest wiele odniesień, które sugerują użycie parametrów w naszych zapytaniach lub użycie procedury składowanej z parametrami w celu uniknięcia iniekcji SQL (SQLi). Pokażę ci, że procedury składowane (na przykład) nie są magicznym kijem przeciwko SQLi. Odpowiedzialność nadal spoczywa na programiście.
Rozważ następującą procedurę składowaną SQL Server, która pobierze wiersz użytkownika z tabeli „Użytkownicy”:
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)
Możesz uzyskać wyniki, przekazując jako parametry nazwę użytkownika i hasło. Zakładając, że hasło jest w postaci dowolnego tekstu (tylko dla uproszczenia tego przykładu), normalne połączenie wyglądałoby tak:
DECLARE @RC int
DECLARE @name varchar(20)
DECLARE @pass varchar(20)
EXECUTE @RC = [dbo].[getUser]
@name = 'admin'
,@pass = '!@Th1siSTheP@ssw0rd!!'
GO
Ale tutaj mamy złą technikę programowania używaną przez programistę wewnątrz procedury składowanej, więc osoba atakująca może wykonać następujące czynności:
DECLARE @RC int
DECLARE @name varchar(20)
DECLARE @pass varchar(20)
EXECUTE @RC = [TestDB].[dbo].[getUser]
@name = 'admin'
,@pass = 'any'' OR 1=1 --'
GO
Powyższe parametry zostaną przekazane jako argumenty do procedury składowanej, a polecenie SQL, które ostatecznie zostanie wykonane, to:
select usrID, usrUName, usrFullName, usrRoleID
from Users
where usrUName = 'admin' and usrPass = 'any' OR 1=1 --'
.. co spowoduje odzyskanie wszystkich wierszy od użytkowników
Problem polega na tym, że nawet my przestrzegamy zasady „Utwórz procedurę składowaną i przekaż pola do wyszukiwania jako parametry”, SQLi jest nadal wykonywany. Dzieje się tak, ponieważ po prostu kopiujemy nasze złe praktyki programistyczne do procedury składowanej. Rozwiązaniem problemu jest przepisanie naszej procedury składowanej w następujący sposób:
alter procedure getUser
@name varchar(20)
,@pass varchar(20)
as
select usrID, usrUName, usrFullName, usrRoleID
from Users
where usrUName = @name and usrPass = @pass
Próbuję powiedzieć, że programiści muszą najpierw dowiedzieć się, czym jest atak SQLi i jak można go wykonać, a następnie odpowiednio zabezpieczyć swój kod. Ślepe podążanie za „najlepszymi praktykami” nie zawsze jest bezpieczniejszym sposobem… i może dlatego mamy tak wiele „najlepszych praktyk” - niepowodzeń!
Tak, użycie przygotowanych instrukcji zatrzymuje wszystkie wstrzyknięcia SQL, przynajmniej w teorii. W praktyce sparametryzowane instrukcje mogą nie być faktycznie przygotowanymi instrukcjami, np. PDO
W PHP domyślnie je emuluje, więc jest otwarty na atak typu edge case .
Jeśli używasz naprawdę przygotowanych wyciągów, wszystko jest bezpieczne. Cóż, przynajmniej tak długo, jak nie dołączasz niebezpiecznego kodu SQL do swojego zapytania, na przykład w reakcji na brak możliwości przygotowania nazw tabel.
Jeśli tak, dlaczego wciąż jest tak wiele udanych wstrzyknięć SQL? Tylko dlatego, że niektórzy programiści są zbyt głupi, aby używać sparametryzowanych instrukcji?
Tak, edukacja jest tutaj głównym punktem i starsze podstawy kodu. Wiele tutoriali używa ucieczki i niestety nie można ich łatwo usunąć z sieci.
Unikam absolutów w programowaniu; zawsze jest wyjątek. Bardzo polecam procedury składowane i obiekty poleceń. Większość mojego zaplecza to SQL Server, ale od czasu do czasu gram w MySql. Procedury składowane mają wiele zalet, w tym plany zapytań w pamięci podręcznej; tak, można to osiągnąć za pomocą parametrów i wbudowanego kodu SQL, ale otwiera to więcej możliwości ataków iniekcyjnych i nie pomaga w oddzieleniu problemów. Dla mnie znacznie łatwiej jest również zabezpieczyć bazę danych, ponieważ moje aplikacje generalnie mają uprawnienia do wykonywania tylko tych procedur składowanych. Bez bezpośredniego dostępu do tabeli / widoku znacznie trudniej jest cokolwiek wstrzyknąć. Jeśli użytkownik aplikacji jest zagrożony, ma się tylko pozwolenie na wykonanie dokładnie tego, co zostało wstępnie zdefiniowane.
Moje dwa centy.
Nie powiedziałbym „głupi”.
Myślę, że samouczki są problemem. Większość samouczków SQL, książek, cokolwiek wyjaśnia SQL za pomocą wbudowanych wartości, nie wspominając w ogóle o parametrach wiązania. Osoby uczące się z tych samouczków nie mają szansy nauczyć się tego poprawnie.
Ponieważ większość kodu nie jest napisana z myślą o bezpieczeństwie i zarządzaniu, biorąc pod uwagę możliwość wyboru między dodawaniem funkcji (zwłaszcza czymś widocznym, co można sprzedać) a bezpieczeństwem / stabilnością / niezawodnością (co jest znacznie trudniejsze do sprzedania), prawie zawsze będą wybierać były. Bezpieczeństwo jest problemem tylko wtedy, gdy staje się problemem.
Czy sparametryzowana instrukcja może zatrzymać wszystkie wstrzyknięcia SQL?
Tak, o ile sterownik bazy danych oferuje symbol zastępczy dla każdego możliwego literału SQL. Większość przygotowanych kierowców oświadczeń tego nie robi. Powiedzmy, że nigdy nie znajdziesz symbolu zastępczego dla nazwy pola lub tablicy wartości. Co zmusi programistę do powrotu do ręcznego dostosowywania zapytania przy użyciu konkatenacji i ręcznego formatowania. Z przewidywanym wynikiem.
Dlatego stworzyłem mój wrapper MySQL dla PHP, który obsługuje większość literałów, które można dynamicznie dodawać do zapytania, w tym tablice i identyfikatory.
Jeśli tak, dlaczego wciąż jest tak wiele udanych wstrzyknięć SQL? Tylko dlatego, że niektórzy programiści są zbyt głupi, aby używać sparametryzowanych instrukcji?
Jak widać, w rzeczywistości niemożliwe jest sparametryzowanie wszystkich zapytań, nawet jeśli nie jesteś głupi.
Najpierw moja odpowiedź na Twoje pierwsze pytanie: Tak, z tego co wiem, przy użyciu zapytań parametrycznych nie będzie już możliwe wstrzykiwanie SQL. Co do poniższych pytań, nie jestem pewien i mogę jedynie wyrazić swoją opinię na temat powodów:
Myślę, że łatwiej jest „po prostu” napisać ciąg zapytania SQL, łącząc różne części (może nawet zależne od pewnych logicznych kontroli) wraz z wartościami do wstawienia. Po prostu tworzy zapytanie i wykonuje je. Kolejną zaletą jest to, że można wydrukować (echo, wyjście lub cokolwiek innego) ciąg zapytania sql, a następnie użyć tego ciągu do ręcznego zapytania do silnika bazy danych.
Pracując z przygotowanymi zestawieniami zawsze masz co najmniej jeden krok więcej: Musisz zbudować swoje zapytanie (w tym oczywiście parametry) Musisz przygotować zapytanie na serwerze Musisz przypisać parametry do rzeczywistych wartości, które chcesz użyć do zapytania Musisz wykonać zapytanie.
To trochę więcej pracy (i nie tak łatwe do zaprogramowania), zwłaszcza w przypadku niektórych „szybkich i brudnych” prac, które często okazują się bardzo długotrwałe ...
Z poważaniem,
Pudełko
Wstrzykiwanie SQL jest podzbiorem większego problemu związanego z wstrzykiwaniem kodu, w którym dane i kod są dostarczane przez ten sam kanał, a dane są mylone z kodem. Zapytania sparametryzowane zapobiegają temu, tworząc zapytanie przy użyciu kontekstu dotyczącego tego, czym są dane, a co kod.
W niektórych przypadkach to nie wystarczy. W wielu systemach DBMS możliwe jest dynamiczne wykonywanie kodu SQL za pomocą procedur składowanych, co wprowadza błąd iniekcji SQL na poziomie DBMS. Wywołanie takiej procedury składowanej przy użyciu zapytań sparametryzowanych nie zapobiegnie wykorzystaniu iniekcji SQL w procedurze. Inny przykład można zobaczyć w tym poście na blogu .
Częściej programiści używają tej funkcji nieprawidłowo. Zwykle kod wygląda mniej więcej tak, gdy jest wykonany poprawnie:
db.parameterize_query("select foo from bar where baz = '?'", user_input)
Niektórzy programiści łączą ze sobą ciągi znaków, a następnie używają sparametryzowanego zapytania, co w rzeczywistości nie czyni wspomnianego wyżej rozróżnienia między danymi a kodem, który zapewnia gwarancję bezpieczeństwa, której szukamy:
db.parameterize_query("select foo from bar where baz = '" + user_input + "'")
Prawidłowe użycie sparametryzowanych zapytań zapewnia bardzo silną, ale nie nieprzeniknioną ochronę przed atakami typu SQL injection.
Aby zabezpieczyć aplikację przed wstrzyknięciem SQL, wykonaj następujące czynności:
Krok 1. Ogranicz wprowadzanie. Krok 2. Użyj parametrów w procedurach składowanych. Krok 3. Użyj parametrów z dynamicznym SQL.
Zobacz http://msdn.microsoft.com/en-us/library/ff648339.aspx
Nawet jeśli przygotowane instrukcje są właściwie używane w całym kodzie aplikacji internetowej, błędy iniekcji SQL mogą nadal występować, jeśli komponenty kodu bazy danych konstruują zapytania na podstawie danych wejściowych użytkownika w niebezpieczny sposób. Poniżej znajduje się przykład procedury składowanej, która jest podatna na wstrzyknięcie SQL w parametrze @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
Nawet jeśli aplikacja przekazuje wartość nazwy podaną przez użytkownika do procedury składowanej w bezpieczny sposób, sama procedura łączy ją bezpośrednio z zapytaniem dynamicznym i dlatego jest podatna na atak.