Mamy tutaj inną dyskusję na temat używania sparametryzowanych zapytań sql w naszym kodzie. Mamy dwie strony dyskusji: ja i kilku innych, którzy twierdzą, że powinniśmy zawsze używać parametrów do ochrony przed wstrzyknięciami sql i innymi facetami, którzy nie uważają, że jest to konieczne. Zamiast tego chcą zastąpić pojedyncze apostrofy dwoma apostrofami we wszystkich ciągach, aby uniknąć iniekcji sql. Wszystkie nasze bazy danych działają pod kontrolą Sql Server 2005 lub 2008, a nasz kod bazowy działa na platformie .NET Framework 2.0.
Podam prosty przykład w C #:
Chcę, żebyśmy to wykorzystali:
string sql = "SELECT * FROM Users WHERE Name=@name";
SqlCommand getUser = new SqlCommand(sql, connection);
getUser.Parameters.AddWithValue("@name", userName);
//... blabla - do something here, this is safe
Podczas gdy inni chcą to zrobić:
string sql = "SELECT * FROM Users WHERE Name=" + SafeDBString(name);
SqlCommand getUser = new SqlCommand(sql, connection);
//... blabla - are we safe now?
Gdzie funkcja SafeDBString jest zdefiniowana w następujący sposób:
string SafeDBString(string inputValue)
{
return "'" + inputValue.Replace("'", "''") + "'";
}
Tak długo, jak używamy SafeDBString na wszystkich wartościach ciągów w naszych zapytaniach, powinniśmy być bezpieczni. Dobrze?
Istnieją dwa powody, dla których warto używać funkcji SafeDBString. Po pierwsze, jest to sposób, w jaki robiono to od wieków kamienia, a po drugie, łatwiej jest debugować instrukcje sql, ponieważ widzisz zapytanie excact, które jest uruchamiane w bazie danych.
A więc wtedy. Moje pytanie brzmi, czy naprawdę wystarczy użyć funkcji SafeDBString, aby uniknąć ataków sql injection. Próbowałem znaleźć przykłady kodu, który łamie ten środek bezpieczeństwa, ale nie mogę znaleźć żadnych przykładów.
Czy jest ktoś, kto może to zepsuć? Jak byś to zrobił?
EDYCJA: podsumowanie dotychczasowych odpowiedzi:
- Nikt nie znalazł jeszcze sposobu na obejście SafeDBString na Sql Server 2005 lub 2008. Myślę, że to dobrze?
- W kilku odpowiedziach wskazano, że podczas korzystania z zapytań parametrycznych uzyskuje się wzrost wydajności. Powodem jest to, że plany kwerend można ponownie wykorzystać.
- Zgadzamy się również, że używanie sparametryzowanych zapytań daje bardziej czytelny kod, który jest łatwiejszy w utrzymaniu
- Ponadto łatwiej jest zawsze używać parametrów niż używać różnych wersji SafeDBString, konwersji ciągów na liczby i konwersji ciągów na datę.
- Używając parametrów, uzyskujesz automatyczną konwersję typów, co jest szczególnie przydatne, gdy pracujemy z datami lub liczbami dziesiętnymi.
- I na koniec: nie próbuj zabezpieczać się, jak napisał JulianR. Dostawcy baz danych poświęcają dużo czasu i pieniędzy na bezpieczeństwo. Nie ma sposobu, abyśmy mogli zrobić to lepiej i nie ma powodu, dla którego powinniśmy próbować wykonywać ich pracę.
Więc chociaż nikt nie był w stanie złamać prostego zabezpieczenia funkcji SafeDBString, otrzymałem wiele innych dobrych argumentów. Dzięki!
źródło
Odpowiedzi:
Myślę, że prawidłowa odpowiedź to:
Nie próbuj sobie zabezpieczać . Zamiast próbować, używaj dowolnej zaufanej biblioteki zgodnej ze standardami branżowymi, która jest dostępna do tego, co próbujesz zrobić robić to samodzielnie. Wszelkie założenia dotyczące bezpieczeństwa mogą być błędne. Chociaż Twoje podejście może wydawać się bezpieczne (i w najlepszym razie wygląda na niepewne), istnieje ryzyko, że coś przeoczysz i czy naprawdę chcesz skorzystać z tej szansy, jeśli chodzi o bezpieczeństwo?
Użyj parametrów.
źródło
A potem ktoś idzie i używa „zamiast”. Parametry są, IMO, jedyną bezpieczną drogą.
Pozwala również uniknąć wielu problemów i18n z datami / liczbami; jaka jest data 01/02/03? Ile kosztuje 123,456? Czy wasze serwery (app-server i db-server) są ze sobą zgodne?
Jeśli czynnik ryzyka nie przekonuje ich, co z wydajnością? RDBMS może ponownie użyć planu zapytań, jeśli używasz parametrów, co poprawia wydajność. Nie może tego zrobić za pomocą samego sznurka.
źródło
Argument nie jest wygrany. Jeśli uda Ci się znaleźć lukę, Twoi współpracownicy po prostu zmienią funkcję SafeDBString, aby ją uwzględnić, a następnie poproszą Cię o ponowne udowodnienie, że jest to niebezpieczne.
Biorąc pod uwagę, że sparametryzowane zapytania są niekwestionowaną najlepszą praktyką programistyczną, ciężar dowodu powinien spoczywać na nich, aby stwierdzić, dlaczego nie używają metody, która jest zarówno bezpieczniejsza, jak i skuteczniejsza.
Jeśli problemem jest przepisanie całego starszego kodu, łatwym kompromisem byłoby użycie sparametryzowanych zapytań w całym nowym kodzie i refaktoryzacja starego kodu w celu użycia ich podczas pracy nad tym kodem.
Domyślam się, że prawdziwym problemem jest duma i upór, a niewiele więcej można z tym zrobić.
źródło
Po pierwsze, Twoja próbka dla wersji „Zamień” jest nieprawidłowa. Musisz umieścić apostrofy wokół tekstu:
Więc to jest jeszcze jedna rzecz, którą parametry robią dla Ciebie: nie musisz się martwić o to, czy wartość musi być ujęta w cudzysłów. Oczywiście możesz to wbudować w funkcję, ale wtedy musisz dodać dużo złożoności do funkcji: jak poznać różnicę między „NULL” jako null a „NULL” jako zwykłym ciągiem lub między liczbą a ciąg, który tak się składa, że zawiera dużo cyfr. To tylko kolejne źródło błędów.
Inną sprawą jest wydajność: sparametryzowane plany zapytań są często buforowane lepiej niż plany połączone, co może oszczędzić serwerowi kroku podczas wykonywania zapytania.
Ponadto unikanie pojedynczych cudzysłowów nie wystarczy. Wiele produktów DB umożliwia alternatywne metody ucieczki przed znakami, z których może skorzystać osoba atakująca. Na przykład w MySQL możesz również zamienić pojedynczy cudzysłów z ukośnikiem odwrotnym. I tak następująca wartość "name" wysadziłaby MySQL za pomocą samej
SafeDBString()
funkcji, ponieważ po podwojeniu pojedynczego cudzysłowu pierwszy znak jest nadal poprzedzony ukośnikiem odwrotnym, pozostawiając drugi jako "aktywny":JulianR przytacza również poniżej ważny punkt: NIGDY nie próbuj samodzielnie wykonywać pracy związanej z ochroną. Tak łatwo jest błędnie zaprogramować bezpieczeństwo w subtelny sposób, który wydaje się działać, nawet po dokładnym przetestowaniu. Potem mija czas i rok później dowiadujesz się, że twój system był złamany sześć miesięcy temu i nawet nie wiedziałeś o tym wcześniej.
Zawsze polegaj w jak największym stopniu na bibliotekach zabezpieczeń dostarczonych dla Twojej platformy. Zostaną napisane przez ludzi, którzy na co dzień zajmują się kodem bezpieczeństwa, znacznie lepiej przetestowane niż to, czym możesz zarządzać, i obsługiwane przez sprzedawcę, jeśli zostanie znaleziona luka w zabezpieczeniach.
źródło
Więc powiedziałbym:
1) Dlaczego próbujesz ponownie zaimplementować coś, co jest wbudowane? jest tam, łatwo dostępny, łatwy w użyciu i już debugowany w skali globalnej. Jeśli zostaną w nim znalezione przyszłe błędy, zostaną one naprawione i dostępne dla wszystkich bardzo szybko, bez konieczności wykonywania jakichkolwiek czynności.
2) Jakie procesy są stosowane, aby zagwarantować , że nigdy nie przegapisz połączenia z SafeDBString? Brak go w jednym miejscu może spowodować wiele problemów. Jak bardzo będziesz się przyglądać tym rzeczom i zastanowić się, jak bardzo zmarnowany jest ten wysiłek, skoro tak łatwo jest znaleźć zaakceptowaną poprawną odpowiedź.
3) Na ile jesteś pewien, że udało Ci się wyeliminować każdy wektor ataku, o którym Microsoft (autor bazy danych i biblioteki dostępu) wie w Twojej implementacji SafeDBString ...
4) Jak łatwo jest odczytać strukturę sql? W przykładzie użyto konkatenacji +, parametry są bardzo podobne do string.Format, co jest bardziej czytelne.
Istnieją również dwa sposoby sprawdzenia, co faktycznie zostało uruchomione - uruchom własną funkcję LogCommand, prostą funkcję bez obaw o bezpieczeństwo , a nawet spójrz na ślad sql, aby dowiedzieć się, co według bazy danych naprawdę się dzieje.
Nasza funkcja LogCommand to po prostu:
Dobrze czy źle, dostarcza nam potrzebnych informacji bez problemów z bezpieczeństwem.
źródło
Sparametryzowane zapytania to coś więcej niż tylko ochrona przed wstrzyknięciem sql. Zyskujesz także lepszy potencjał buforowania planu wykonania. Jeśli korzystasz z profilera zapytań serwera sql, nadal możesz zobaczyć `` dokładny sql, który jest uruchamiany w bazie danych '', więc tak naprawdę nie tracisz niczego, jeśli chodzi o debugowanie instrukcji sql.
źródło
Użyłem obu podejść, aby uniknąć ataków typu SQL injection i zdecydowanie preferuję zapytania sparametryzowane. Kiedy korzystałem z połączonych zapytań, użyłem funkcji bibliotecznej do ucieczki przed zmiennymi (np. Mysql_real_escape_string) i nie byłbym pewien, czy opisałem wszystko w zastrzeżonej implementacji (jak się wydaje).
źródło
Nie jest możliwe łatwe sprawdzenie typu danych wejściowych użytkownika bez użycia parametrów.
Jeśli używasz klas SQLCommand i SQLParameter do wywoływania DB, nadal możesz zobaczyć wykonywane zapytanie SQL. Spójrz na właściwość CommandText SQLCommand.
Zawsze jestem trochę podejrzany o własne podejście do zapobiegania iniekcji SQL, gdy sparametryzowane zapytania są tak łatwe w użyciu. Po drugie, tylko dlatego, że „zawsze było to robione w ten sposób”, nie oznacza, że jest to właściwy sposób.
źródło
Jest to bezpieczne tylko wtedy, gdy masz gwarancję, że zamierzasz przekazać ciąg.
A co, jeśli w pewnym momencie nie przekazujesz ciągu? A co, jeśli podasz tylko liczbę?
Ostatecznie stanie się:
źródło
Używałbym procedur składowanych lub funkcji do wszystkiego, więc pytanie nie powstało.
Tam, gdzie muszę wstawić SQL do kodu, używam parametrów, które są jedyną rzeczą, która ma sens. Przypomnij dysydentom, że są hakerzy mądrzejsi od nich, którzy mają lepszą motywację do złamania kodu, który próbuje ich przechytrzyć. Używając parametrów, jest to po prostu niemożliwe i nie jest to trudne.
źródło
Zdecydowanie zgadzam się z kwestiami bezpieczeństwa.
Innym powodem stosowania parametrów jest efektywność.
Bazy danych zawsze kompilują zapytanie i buforują je, a następnie ponownie wykorzystują zapytanie z pamięci podręcznej (co jest oczywiście szybsze w przypadku kolejnych żądań). Jeśli użyjesz parametrów, to nawet jeśli użyjesz innych parametrów, baza danych ponownie użyje buforowanego zapytania, ponieważ pasuje na podstawie ciągu SQL przed powiązaniem parametrów.
Jeśli jednak nie powiążesz parametrów, ciąg znaków SQL zmienia się przy każdym żądaniu (które ma inne parametry) i nigdy nie będzie pasował do tego, co znajduje się w pamięci podręcznej.
źródło
Z podanych już powodów parametry są bardzo dobrym pomysłem. Ale nienawidzimy ich używać, ponieważ tworzenie parametru i przypisanie jego nazwy zmiennej do późniejszego wykorzystania w zapytaniu to potrójny wrak głowy.
Następująca klasa opakowuje narzędzie do tworzenia ciągów, którego będziesz często używać do tworzenia żądań SQL. Umożliwia pisanie sparametryzowanych zapytań bez konieczności tworzenia parametrów , dzięki czemu można skoncentrować się na SQL. Twój kod będzie wyglądał następująco ...
Czytelność kodu, mam nadzieję, że się zgadzasz, znacznie się poprawiła, a wynikiem jest odpowiednio sparametryzowane zapytanie.
Klasa wygląda tak ...
źródło
Od bardzo krótkiego czasu, kiedy musiałem badać problemy z iniekcją SQL, widzę, że uczynienie wartości `` bezpieczną '' oznacza również, że zamykasz drzwi na sytuacje, w których możesz chcieć apostrofów w swoich danych - a co z czyimś imieniem , np. O'Reilly.
To pozostawia parametry i procedury składowane.
I tak, zawsze powinieneś próbować implementować kod w najlepszy sposób, jaki znasz - nie tylko tak, jak zawsze było to robione.
źródło
''
jako literał'
, więc łańcuch będzie postrzegany wewnętrznie jako sekwencja znakówO'Reilly
. To jest to, co DB będzie przechowywać, pobierać, porównywać itp. Jeśli chcesz pokazać użytkownikowi jego dane po ucieczce, zachowaj kopię niezmienionego ciągu appside.Oto kilka artykułów, które mogą okazać się pomocne w przekonaniu współpracowników.
http://www.sommarskog.se/dynamic_sql.html
http://unixwiz.net/techtips/sql-injection.html
Osobiście wolę nigdy nie dopuścić, aby jakikolwiek kod dynamiczny dotykał mojej bazy danych, wymagając, aby wszystkie kontakty odbywały się za pośrednictwem sps (a nie takiego, który używa dynamicznego SQl). Oznacza to, że nic poza tym, na co dałem użytkownikom pozwolenie, nie może zostać zrobione i że użytkownicy wewnętrzni (z wyjątkiem nielicznych z dostępem produkcyjnym do celów administracyjnych) nie mogą uzyskać bezpośredniego dostępu do moich tabel i siać spustoszenia, kraść dane lub popełniać oszustwa. Jeśli prowadzisz aplikację finansową, jest to najbezpieczniejsza droga.
źródło
Można go zepsuć, jednak środki zależą od dokładnych wersji / poprawek itp.
Jeden, który już został poruszony, to błąd przepełnienia / obcięcia, który można wykorzystać.
Innym przyszłym sposobem byłoby znalezienie błędów podobnych do innych baz danych - na przykład stos MySQL / PHP miał problem z ucieczką, ponieważ niektóre sekwencje UTF8 mogłyby zostać użyte do manipulowania funkcją replace - funkcja replace zostałby oszukana do wprowadzenia znaków iniekcji.
Ostatecznie zastępczy mechanizm zabezpieczeń opiera się na oczekiwanej, ale nie zamierzonej funkcjonalności. Ponieważ funkcjonalność nie była zamierzonym celem kodu, istnieje duże prawdopodobieństwo, że jakieś odkryte dziwactwo zepsuje oczekiwaną funkcjonalność.
Jeśli masz dużo starszego kodu, metoda replace może posłużyć jako zapora, aby uniknąć długiego przepisywania i testowania. Jeśli piszesz nowy kod, nie ma wymówki.
źródło
Tam, gdzie to możliwe, zawsze używaj zapytań parametrycznych. Czasami nawet proste wejście bez użycia jakichkolwiek dziwnych znaków może już utworzyć wstrzyknięcie SQL, jeśli nie zostało zidentyfikowane jako dane wejściowe dla pola w bazie danych.
Po prostu pozwól bazie danych samodzielnie zidentyfikować dane wejściowe, nie wspominając o tym, że oszczędza to również wielu kłopotów, gdy trzeba wstawić dziwne znaki, które w przeciwnym razie zostałyby zmienione lub zmienione. Może nawet zaoszczędzić cenny czas pracy, ponieważ nie trzeba obliczać danych wejściowych.
źródło
Nie widziałem żadnych innych odpowiedzi dotyczących tej strony `` dlaczego robienie tego samemu jest złe '', ale rozważ atak SQL Truncation .
Istnieje również funkcja
QUOTENAME
T-SQL, która może być pomocna, jeśli nie możesz ich przekonać do używania parametrów. Wyłapuje wiele (wszystkie?) Z omijanych problemów związanych z qoute.źródło
Dwa lata później ponownie zdecydowałem ... Każdy, kto znajdzie parametry, może wypróbować moje rozszerzenie VS, QueryFirst . Edytujesz swoje żądanie w prawdziwym pliku .sql (Validation, Intellisense). Aby dodać parametr, wystarczy wpisać go bezpośrednio do kodu SQL, zaczynając od „@”. Gdy zapiszesz plik, QueryFirst wygeneruje klasy opakowania, aby umożliwić uruchomienie zapytania i dostęp do wyników. Wyszuka typ DB parametru i zamapuje go na typ .net, który znajdziesz jako dane wejściowe do wygenerowanych metod Execute ().Nie może być prostsze. Zrobienie tego we właściwy sposób jest radykalnie szybsze i łatwiejsze niż zrobienie tego w jakikolwiek inny sposób, a stworzenie luki w zabezpieczeniach sql injection staje się niemożliwe lub przynajmniej perwersyjnie trudne. Istnieją inne zabójcze zalety, takie jak możliwość usuwania kolumn w bazie danych i natychmiastowego wyświetlania błędów kompilacji w aplikacji.
zastrzeżenie prawne: napisałem QueryFirst
źródło
Oto kilka powodów, dla których warto używać zapytań parametrycznych:
źródło
Wystąpiło kilka luk (nie pamiętam, która to była baza danych) związanych z przepełnieniem bufora instrukcji SQL.
Chcę powiedzieć, że SQL-Injection to coś więcej niż tylko „uniknięcie cudzysłowu” i nie masz pojęcia, co będzie dalej.
źródło
Inną ważną kwestią jest śledzenie danych, które utraciły znaczenie i które nie mają znaczenia. Istnieje mnóstwo aplikacji, zarówno internetowych, jak i innych, które wydają się nie śledzić, kiedy dane są nieprzetworzone w formacie Unicode, kodowane &, sformatowane HTML i tak dalej. Jest oczywiste, że trudno będzie śledzić, które struny są
''
zakodowane, a które nie.Jest to również problem, gdy ostatecznie zmieniasz typ jakiejś zmiennej - być może była to liczba całkowita, ale teraz jest to ciąg. Teraz masz problem.
źródło