Jeśli chodzi o wstrzykiwanie SQL , w pełni rozumiem konieczność parametryzacji string
parametru; to jedna z najstarszych sztuczek w tej książce. Ale kiedy można uzasadnić brak parametryzacji SqlCommand
? Czy jakiekolwiek typy danych są uważane za „bezpieczne”, jeśli nie można ich parametryzować?
Na przykład: nie uważam się nigdzie w pobliżu eksperta w SQL, ale nie mogę myśleć o jakichkolwiek przypadkach, gdzie byłoby potencjalnie narażone na SQL injection zaakceptować bool
albo int
i po prostu złączyć je w prawo w zapytaniu.
Czy moje założenie jest prawidłowe, czy może to potencjalnie pozostawi dużą lukę w zabezpieczeniach mojego programu?
Dla wyjaśnienia, to pytanie zostało oznaczone tagiem do#który jest językiem silnie typizowanym; kiedy mówię „parametr”, pomyśl o czymś takim public int Query(int id)
.
źródło
Odpowiedzi:
Myślę, że to bezpieczne ... technicznie , ale to straszny nawyk. Czy na pewno chcesz pisać takie zapytania?
Naraża Cię również na sytuację, w której typ zmienia się z liczby całkowitej na ciąg znaków (Pomyśl o numerze pracownika, który pomimo swojej nazwy - może zawierać litery).
Więc zmieniliśmy typ EmployeeNumber z
int
nastring
, ale zapomnieliśmy zaktualizować nasze zapytania sql. Ups.źródło
AddWithValue
już przestać używać ? blogs.msmvps.com/jcoehoorn/blog/2014/05/12/…AddWithValue
chyba że masz mapowanie typów baz danych w ramach dynamicznego budowania instrukcji. Zakładam, że masz listę kolumn docelowych, a jako część słownika możesz mieć typy, jeśli chcesz. W przeciwnym razie po prostu przyjmij uderzenie wydajności. Ostatecznie myślę, że to po prostu dobra informacja.AddWithValue
już przestać używać ?” powinno naprawdę brzmieć „jeśli znasz typ, powinieneś powstrzymać się od używaniaAddWithValue
.Podczas korzystania z silnie wpisany platformę na komputerze można sterować (jak serwer WWW), można zapobiec iniekcji kodu dla zapytań tylko
bool
,DateTime
czyint
(i innych numerycznych) wartości. Niepokojące są problemy z wydajnością spowodowane zmuszeniem serwera sql do ponownej kompilacji każdego zapytania i uniemożliwianiem mu uzyskiwania dobrych statystyk dotyczących zapytań wykonywanych z jaką częstotliwością (co szkodzi zarządzaniu pamięcią podręczną).Ale ta część „na komputerze, którym sterujesz” jest ważna, ponieważ w przeciwnym razie użytkownik może zmienić zachowanie używane przez system do generowania ciągów z tych wartości, aby uwzględnić dowolny tekst.
Lubię też myśleć długoterminowo. Co się stanie, gdy dzisiejsza stara i zniszczona silnie typowana baza kodu zostanie przeniesiona przez automatyczne tłumaczenie na dynamiczny język New-Hotness i nagle stracisz sprawdzanie typów, ale nie masz jeszcze wszystkich odpowiednich testów jednostkowych dla kodu dynamicznego ?
Naprawdę nie ma dobrego powodu, aby nie używać parametrów zapytania dla tych wartości. To właściwy sposób . Śmiało i zakoduj na stałe wartości w ciągu sql, gdy naprawdę są one stałymi, ale w przeciwnym razie dlaczego nie użyć po prostu parametru? To nie jest trudne.
Ostatecznie nie nazwałbym tego samym błędem , ale nazwałbym to zapachem : coś, co samo w sobie nie jest błędem, ale jest silną wskazówką, że błędy są w pobliżu lub w końcu będą. Dobry kod pozwala uniknąć pozostawiania zapachów, a każde dobre narzędzie do analizy statycznej to oznaczy.
Dodam, że nie jest to niestety rodzaj sporu, który można wygrać od razu. Wygląda na to, że sytuacja, w której „racja” już nie wystarcza, a nadepnięcie współpracownikom na palce, aby samodzielnie rozwiązać ten problem, nie sprzyja dobrej dynamice zespołu; ostatecznie może bardziej boleć niż pomaga. Lepszym podejściem w tym przypadku może być promowanie użycia narzędzia do analizy statycznej. To dałoby legitymację i wiarygodność wysiłkom zmierzającym do powrotu i naprawienia istniejącego kodu.
źródło
DateTime
orint
W niektórych przypadkach jest możliwe wykonanie ataku SQL injection z niesparametryzowanymi (konkatenowanymi) zmiennymi innymi niż wartości łańcuchowe - zobacz artykuł Jon: http://codeblog.jonskeet.uk/2014/08/08/the-bobbytables -kultura / .
Chodzi o to, że gdy
ToString
jest wywoływana, jakiś niestandardowy dostawca kultury może przekształcić parametr niebędący ciągiem znaków w jego reprezentację w postaci ciągu, który wstrzykuje trochę kodu SQL do zapytania.źródło
int
s?”CultureInfo
trudno wiedzieć, dlaczego i tak potrzebujesz iniekcji SQL.Nie jest to bezpieczne nawet w przypadku typów niebędących ciągami. Zawsze używaj parametrów. Kropka.
Rozważ następujący przykład kodu:
Na pierwszy rzut oka kod wygląda na bezpieczny, ale wszystko się zmienia, jeśli wprowadzisz zmiany w ustawieniach regionalnych systemu Windows i dodasz wtrysk w formacie daty krótkiej:
Teraz wynikowy tekst polecenia wygląda następująco:
To samo można zrobić dla
int
typu, ponieważ użytkownik może zdefiniować niestandardowy znak ujemny, który można łatwo zmienić na iniekcję SQL.Można argumentować, że zamiast bieżącej kultury należy używać niezmiennej kultury, ale widziałem takie konkatenacje ciągów wiele razy i łatwo jest przeoczyć, łącząc ciągi z obiektami przy użyciu
+
.źródło
int
typie?"SELECT * FROM Table1 WHERE Id =" + intVariable.ToString ()
Bezpieczeństwo
Jest OK.
Atakujący nie mogą wstrzyknąć niczego do wpisanej zmiennej int.
Wydajność
Not OK.
Lepiej jest używać parametrów, więc zapytanie zostanie skompilowane raz i zapisane w pamięci podręcznej do następnego użycia. Następnym razem, nawet z różnymi wartościami parametrów, zapytanie jest buforowane i nie musi być kompilowane na serwerze bazy danych.
Styl kodowania
Zła praktyka.
„SELECT * FROM Product WHERE Id =” + TextBox1.Text
Chociaż to nie jest twoje pytanie, ale może przydatne dla przyszłych czytelników:
Katastrofa bezpieczeństwa !
Nawet jeśli
Id
pole jest liczbą całkowitą, zapytanie może podlegać iniekcji SQL. Załóżmy, że masz zapytanie w swojej aplikacji"SELECT * FROM Table1 WHERE Id=" + TextBox1.Text
, osoba atakująca może wstawić do pola tekstowego,1; DELETE Table1
a zapytanie będzie:Jeśli nie chcesz tutaj używać sparametryzowanego zapytania, powinieneś użyć wpisanych wartości:
Twoje pytanie
Myślę, że zmiana tych kodów nie jest stratą czasu. Rzeczywiście zmiana jest zalecana!
jeśli twój współpracownik używa zmiennych int, nie ma to żadnego zagrożenia dla bezpieczeństwa, ale myślę, że zmiana tych kodów nie jest stratą czasu i rzeczywiście zaleca się zmianę tych kodów. Sprawia, że kod jest bardziej czytelny, łatwiejszy w utrzymaniu i przyspiesza wykonywanie.
źródło
.ToString()
jest określane przez element konfiguracji systemu operacyjnego, który można łatwo zmienić w celu uwzględnienia dowolnego tekstu.W rzeczywistości są dwa pytania w jednym. A pytanie z tytułu ma niewiele wspólnego z obawami wyrażonymi przez PO w późniejszych komentarzach.
Chociaż zdaję sobie sprawę, że dla PO liczy się ich konkretny przypadek, dla czytelników pochodzących z Google ważne jest, aby odpowiedzieć na bardziej ogólne pytanie, które można sformułować jako „czy konkatenacja jest tak samo bezpieczna jak przygotowane oświadczenia, jeśli upewnię się że każdy literał, który konkatenuję, jest bezpieczny? ”. Chciałbym więc skoncentrować się na tym ostatnim. A odpowiedź brzmi
Zdecydowanie nie.
Wyjaśnienie nie jest tak bezpośrednie, jak chciałaby większość czytelników, ale postaram się jak najlepiej.
Od dłuższego czasu zastanawiałem się nad tą sprawą, czego efektem był artykuł (choć oparty na środowisku PHP), w którym próbowałem wszystko podsumować. Przyszło mi do głowy, że kwestia ochrony przed wstrzyknięciem SQL często wymyka się niektórym pokrewnym, ale węższym tematom, takim jak ucieczka ciągów, rzutowanie typów i tym podobne. Chociaż niektóre środki można uznać za bezpieczne, gdy są podejmowane samodzielnie, nie ma systemu ani prostej zasady do przestrzegania. To sprawia, że jest to bardzo śliski grunt, zbytnio obciążający uwagę i doświadczenie programisty.
Kwestii iniekcji SQL nie można uprościć do kwestii konkretnego problemu składniowego. Jest szerszy niż zwykły myśleć programista. To także kwestia metodologiczna . Nie chodzi tylko o „Jakie konkretne formatowanie musimy zastosować”, ale także „ Jak to zrobić”.
(Z tego punktu widzenia artykuł Jona Skeeta cytowany w drugiej odpowiedzi radzi sobie raczej źle niż dobrze, ponieważ ponownie szuka dziury w jakimś skrajnym przypadku, koncentrując się na konkretnym problemie składniowym i nie rozwiązując problemu w całości).
Kiedy próbujesz rozwiązać kwestię ochrony nie jako całości, ale jako zestaw różnych problemów składniowych, napotykasz wiele problemów.
W przeciwieństwie do tego bałaganu, przygotowane wypowiedzi są rzeczywiście Świętym Graalem:
(Myśląc dalej, odkryłem, że obecny zestaw symboli zastępczych nie wystarcza do zaspokojenia rzeczywistych potrzeb i musi zostać rozszerzony, zarówno w przypadku złożonych struktur danych, takich jak tablice, jak i nawet słów kluczowych lub identyfikatorów SQL, które czasami trzeba dodawać do zapytania również dynamicznie, ale programista jest nieuzbrojony w takim przypadku i zmuszony do powrotu do konkatenacji ciągów, ale to kwestia innego pytania).
Co ciekawe, kontrowersje związane z tym pytaniem wywołuje bardzo kontrowersyjny charakter Stack Overflow. Ideą serwisu jest wykorzystanie konkretnych pytań od użytkowników, którzy pytają bezpośrednio, aby osiągnąć cel, jakim jest posiadanie bazy odpowiedzi ogólnego przeznaczenia, odpowiedniej dla użytkowników pochodzących z wyszukiwania . Pomysł nie jest zły sam w sobie , ale zawodzi w takiej sytuacji: gdy użytkownik zadaje bardzo wąskie pytanie , szczególnie po to, aby uzyskać argument w sporze z kolegą (lub zdecydować, czy warto refaktoryzować kod). Podczas gdy większość doświadczonych uczestników próbuje napisać odpowiedź, mając na uwadze misję całego Stack Overflow, dzięki czemu ich odpowiedź jest dobra dla jak największej liczby czytelników, a nie tylko dla PO.
źródło
Nie myślmy tylko o kwestiach bezpieczeństwa lub bezpieczeństwa typów.
Powodem używania sparametryzowanych zapytań jest poprawa wydajności na poziomie bazy danych. Z punktu widzenia bazy danych sparametryzowane zapytanie to jedno zapytanie w buforze SQL (używając terminologii Oracle, chociaż wyobrażam sobie, że wszystkie bazy danych mają wewnętrznie podobną koncepcję). Zatem baza danych może przechowywać w pamięci pewną liczbę zapytań, przygotowanych i gotowych do wykonania. Te zapytania nie muszą być analizowane i będą szybsze. Często uruchamiane zapytania zwykle znajdują się w buforze i nie wymagają analizowania za każdym razem, gdy są używane.
CHYBA ŻE
Ktoś nie używa sparametryzowanych zapytań. W tym przypadku bufor jest nieustannie przepuszczany przez strumień prawie identycznych zapytań, z których każde musi zostać przeanalizowane i uruchomione przez silnik bazy danych, a wydajność spada, ponieważ nawet często uruchamiane zapytania są wielokrotnie ponownie analizowane. dzień. Dostroiłem bazy danych na życie i było to jedno z największych źródeł nisko wiszących owoców.
TERAZ
Odpowiadając na pytanie: JEŚLI zapytanie zawiera niewielką liczbę odrębnych wartości liczbowych, prawdopodobnie nie spowoduje to problemów i może w rzeczywistości w nieskończoność zwiększyć wydajność. JEŚLI jednak istnieją potencjalnie setki wartości, a zapytanie jest często wywoływane, masz zamiar wpłynąć na wydajność systemu, więc nie rób tego.
Tak, możesz zwiększyć bufor SQL, ale zawsze ostatecznie odbywa się to kosztem innych, bardziej krytycznych zastosowań pamięci, takich jak buforowanie indeksów lub danych. Morał, używaj sparametryzowanych zapytań dość religijnie, aby zoptymalizować bazę danych i użyć więcej pamięci serwera na rzeczy, które mają znaczenie ...
źródło
Aby dodać informacje do Maćka odpowiedz:
Informacje o kulturze aplikacji innej firmy .NET można łatwo zmienić, wywołując funkcję główną zestawu przez odbicie:
Działa to tylko wtedy, gdy główna funkcja BobbysApp jest publiczna. Jeśli Main nie jest publiczne, możesz wywołać inne funkcje publiczne.
źródło
Moim zdaniem, jeśli możesz zagwarantować, że parametr, z którym pracujesz, nigdy nie będzie zawierał łańcucha, to jest to bezpieczne, ale i tak bym tego nie zrobił. Zauważysz również niewielki spadek wydajności ze względu na fakt, że wykonujesz konkatenację. Pytanie, które zadam, brzmi: dlaczego nie chcesz używać parametrów?
źródło
Jest w porządku, ale nigdy nie jest bezpieczne ... a bezpieczeństwo zawsze zależy od danych wejściowych, na przykład jeśli obiektem wejściowym jest TextBox, atakujący mogą zrobić coś podstępnego, ponieważ pole tekstowe może akceptować ciąg, więc musisz wprowadzić jakiś rodzaj walidacji / konwersji aby móc zapobiec niewłaściwemu wprowadzaniu danych przez użytkowników. Ale chodzi o to, że nie jest to bezpieczne. Tak proste.
źródło
Nie, w ten sposób można uzyskać atak polegający na iniekcji SQL. Napisałem stary artykuł po turecku, który pokazuje, jak tutaj . Przykład artykułu w PHP i MySQL, ale koncepcja działa tak samo w C # i SQL Server.
Zasadniczo atakujesz podążając drogą. Rozważmy, że masz stronę, która pokazuje informacje według wartości identyfikatora całkowitego. Nie sparametryzowałeś tego w wartości, jak poniżej.
Okej, zakładam, że używasz MySQL i atakuję w następujący sposób.
Zauważ, że tutaj wprowadzona wartość nie jest ciągiem. Zmieniamy wartość char na int używając funkcji ASCII. Możesz osiągnąć to samo w SQL Server używając "CAST (YourVarcharCol AS INT)".
Następnie używam funkcji length i substring, aby znaleźć nazwę bazy danych.
Następnie używając nazwy bazy danych, zaczynasz pobierać nazwy tabel w bazie danych.
Oczywiście musisz zautomatyzować ten proces, ponieważ otrzymujesz tylko JEDEN znak na zapytanie. Ale możesz to łatwo zautomatyzować. Mój artykuł pokazuje jeden przykład w watir . Korzystanie tylko z jednej strony i nie sparametryzowanej wartości identyfikatora. Mogę nauczyć się każdej nazwy tabeli w twojej bazie danych. Potem mogę poszukać ważnych stolików. To zajmie trochę czasu, ale jest wykonalne.
źródło
public void QuerySomething(int id) { // this will only accept an integer }
Cóż ... jedno jest pewne: bezpieczeństwo NIE jest w porządku, gdy łączysz ciąg (pobierany przez użytkownika) z ciągiem poleceń SQL. Nie ma znaczenia, czy klauzula where odnosi się do liczby całkowitej lub dowolnego typu; mogą wystąpić zastrzyki.
W SQL Injection liczy się typ danych zmiennej, która służyła do pobierania wartości od użytkownika.
Przypuśćmy, że w klauzuli where mamy liczbę całkowitą i:
zmienną użytkownika jest ciąg. W takim razie ok, nie jest to łatwe do wstrzyknięcia (używając UNION), ale bardzo łatwo jest ominąć za pomocą 'OR 1 = 1' - jak ataki ...
Jeśli zmienna użytkownika jest liczbą całkowitą. Z drugiej strony możemy `` przetestować '' siłę systemu, przechodząc nietypowe testy dużych liczb pod kątem awarii systemu, a nawet ukrytego przepełnienia bufora (na ostatnim ciągu) ...
Może parametry zapytań lub (jeszcze lepiej - imo) procedur składowanych nie są w 100% bezpieczne dla zagrożeń, ale są najmniej wymaganym środkiem (lub podstawowym, jeśli wolisz), aby je zminimalizować.
źródło