Unikanie iniekcji SQL bez parametrów

109

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!

Rune Grimstad
źródło
16
Twoi koledzy są daleko poza bazą. Rzuć im wyzwanie, aby znaleźli jedną literaturę na poparcie ich stanowiska. Argument ex neolithos jest śmieszny, rzeczy się zmieniają, tylko osoba tkwiąca w epoce kamienia łupanego nie dostosuje się.
annakata
1
Cóż, przynajmniej twoi koledzy chronią przed JEDNĄ z różnych form włamań ... Czy są pewni, że wszystkie sparametryzowane zapytania tak działają? (Nie jestem ...)
Arjan Einbu
1
Żadna luka w zabezpieczeniach ich nie przekona. Jeśli przyniesiesz kilka luk w zabezpieczeniach (o co prosisz) i inne problemy i wskażesz jeden po drugim, że parametry rozwiążą ten problem i że Twój zespół będzie musiał napisać góry kodu, aby zapewnić ułamek funkcjonalności, możesz przekonać ich. Powodzenia.
Robert Gowland
3
Nawet bez cudzysłowów nadal możesz złamać kod za pomocą logiki. Spróbuj użyć nazwy użytkownika „test LUB 1 = 1” - zwrócone zostaną wszystkie wiersze, a nie tylko jeden z testem nazwy użytkownika!
Most
1
Westchnienie. Naprawdę nie rozumiem, jak jako branża udaje nam się nadal tolerować tego rodzaju nieprofesjonalne zachowanie.
jeroenh

Odpowiedzi:

83

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.

JulianR
źródło
Re „Użyj dowolnej zaufanej biblioteki zgodnej ze standardami branżowymi” - czy możesz polecić jakąś bibliotekę dla .NET? Może więcej niż jeden w zależności od bazy danych: SQLServer, MySQL, PostgreSQL? Szukałem SQL-sanitizer, ale bez większego szczęścia, więc zostałem zmuszony do zaimplementowania własnego, najlepiej jak potrafię (co jest bez wątpienia dalekie od niezawodności).
PSU
72

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.

Marc Gravell
źródło
Wypróbowałem argumenty dotyczące formatowania i wydajności, ale nadal nie są one przekonane.
Rune Grimstad
5
W rzeczywistości serwer sql może ponownie użyć planu zapytań, niezależnie od tego, czy używasz parametrów, czy nie. Zgadzam się z innymi argumentami, ale w większości przypadków argument wydajności dla sparametryzowanego sql już nie leci.
tnyfst
1
@tnyfst: może ponownie wykorzystać plan wykonania, gdy ciąg zapytania zmienia się dla każdej kombinacji wartości parametrów? Nie sądziłem, że to możliwe.
John Saunders
4
Plan zapytania zostanie ponownie wykorzystany, jeśli tekst zapytania jest IDENTYCZNY względem wcześniejszego tekstu zapytania. Więc jeśli wyślesz DOKŁADNIE TO SAMO zapytanie dwa razy, zostanie ono ponownie użyte. Jeśli jednak zmienisz choćby tylko spację, przecinek lub coś, trzeba będzie określić nowy plan zapytania.
marc_s
1
@Marc: Nie jestem pewien, czy masz całkowitą rację. Hueristics buforujące serwery SQL są trochę dziwne. Parser jest w stanie zidentyfikować stałe w tekście i może sztucznie przekonwertować ciąg SQL na jeden z parametrów. Następnie może wstawić do pamięci podręcznej tekst nowego sparametryzowanego zapytania. Późniejszy podobny SQL może znaleźć swoją sparametryzowaną wersję dopasowaną w pamięci podręcznej. Jednak wersje sparametryzowane nie zawsze są używane, a oryginalne wersje SQL są buforowane. Podejrzewam, że SQL ma zillion powodów związanych z wydajnością, aby wybrać między tymi dwoma podejściami.
AnthonyWJones
27

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ć.

Matthew Christensen
źródło
19

Po pierwsze, Twoja próbka dla wersji „Zamień” jest nieprawidłowa. Musisz umieścić apostrofy wokół tekstu:

string sql = "SELECT * FROM Users WHERE Name='" + SafeDBString(name) & "'";
SqlCommand getUser = new SqlCommand(sql, connection);

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":

x \ 'LUB 1 = 1; -


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.

Joel Coehoorn
źródło
5
Funkcja zamiany dodaje apostrofy
Rune Grimstad
5
Wtedy jest to jeszcze jedno źródło błędów. Skąd rozpoznaje różnicę między NULL jako wartością null a NULL jako ciągiem tekstowym? Albo między wprowadzeniem liczbowym a ciągiem, który po prostu zawiera cyfry?
Joel Coehoorn
Słuszna uwaga. Powinieneś używać tej funkcji tylko dla łańcuchów i prawdopodobnie dat, więc musisz być ostrożny. To jeszcze jeden powód, aby używać parametrów! Yay!
Rune Grimstad
10

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:

    string LogCommand(SqlCommand cmd)
    {
        StringBuilder sb = new StringBuilder();
        sb.AppendLine(cmd.CommandText);
        foreach (SqlParameter param in cmd.Parameters)
        {
            sb.Append(param.ToString());
            sb.Append(" = \"");
            sb.Append(param.Value.ToString());
            sb.AppendLine("\"");
        }
        return sb.ToString();
    }

Dobrze czy źle, dostarcza nam potrzebnych informacji bez problemów z bezpieczeństwem.

Jim T.
źródło
1
Prawdopodobnie będzie miał do czynienia z grupą starych programistów VBSCRIPT, którzy są przyzwyczajeni do robienia wszystkiego, łącznie z XML i SQL, poprzez konkatenację ciągów. Będą to osoby, które boją się korzystania z API. Nic nie można z nimi zrobić, a przynajmniej nic humanitarnego.
John Saunders
1
+1 za przedmiot nr 2, z tym wyjątkiem, że nie ma też możliwości narzucenia rzeczywistych parametrów.
Joel Coehoorn
7

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.

Steve Willcock
źródło
MySQL rejestruje również sparametryzowane zapytania z interpolowanymi do nich wartościami parametrów.
Bill Karwin
5

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).

RedBlueThing
źródło
2
+1, ponieważ mysql_real_escape_string () ucieka \ x00, \ x1a, \ n \ r 'i ". Obsługuje także problemy z zestawem znaków. Naiwna funkcja współpracowników OP nic z tego nie robi!
Bill Karwin
4

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.

Tim Scarborough
źródło
3

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ę?

http://www.mywebsite.com/profile/?id=7;DROP DATABASE DB

Ostatecznie stanie się:

SELECT * FROM DB WHERE Id = 7;DROP DATABASE DB
joshcomley
źródło
Jest to ciąg lub liczba. Ciąg jest chroniony za pomocą SafeDbString. Liczba to Int32 i nie może usuwać baz danych.
Andomar
Liczby są łatwiejsze w obsłudze. Po prostu konwertujesz parametr na int / float / cokolwiek przed użyciem go w zapytaniu. Problem polega na tym, że musisz zaakceptować dane łańcuchowe.
Rune Grimstad
Andomar - jeśli tylko ręcznie konstruujesz instrukcję SQL, to jej zamierzony „typ” nie ma znaczenia, możesz bardzo, bardzo łatwo wstrzyknąć SQL za pomocą liczby. Rune - myślę, że jest to zbyt duże poleganie na indywidualnym deweloperze, aby zapamiętać wszystkie niuanse ręcznego rozwiązywania iniekcji SQL. Jeśli powiesz po prostu „użyj parametrów”, jest to bardzo proste i nie mogą się one pomylić.
joshcomley
@Andomar: A co z NULL? Albo ciągi, które wyglądają jak cyfry?
Joel Coehoorn
2

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.

John Saunders
źródło
Ok, jak wykonać iniekcję SQL za pomocą parametrów?
John Saunders
@Saunders: Krok 1 polega na znalezieniu błędu przepełnienia bufora w funkcji obsługi parametrów Twojej bazy danych.
Brian
2
Znalazłeś już jeden? W komercyjnej bazie danych, którą codziennie atakują setki tysięcy hakerów? Jeden wykonany przez firmę programistyczną, o której wiadomo, że ma bardzo głębokie kieszenie? Byłbyś w stanie zacytować pozew z nazwiska, gdyby to było możliwe.
John Saunders
1
Oczywiście, jeśli SPROC używa konkatenacji i EXEC (zamiast sp_ExecuteSQL), to znowu masz kłopoty ... (Widziałem, że zrobiono to źle zbyt wiele razy, żeby to zdyskontować ...)
Marc Gravell
2

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.

Darren Greaves
źródło
2

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 ...

var bldr = new SqlBuilder( myCommand );
bldr.Append("SELECT * FROM CUSTOMERS WHERE ID = ").Value(myId, SqlDbType.Int);
//or
bldr.Append("SELECT * FROM CUSTOMERS WHERE NAME LIKE ").FuzzyValue(myName, SqlDbType.NVarChar);
myCommand.CommandText = bldr.ToString();

Czytelność kodu, mam nadzieję, że się zgadzasz, znacznie się poprawiła, a wynikiem jest odpowiednio sparametryzowane zapytanie.

Klasa wygląda tak ...

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;

namespace myNamespace
{
    /// <summary>
    /// Pour le confort et le bonheur, cette classe remplace StringBuilder pour la construction
    /// des requêtes SQL, avec l'avantage qu'elle gère la création des paramètres via la méthode
    /// Value().
    /// </summary>
    public class SqlBuilder
    {
        private StringBuilder _rq;
        private SqlCommand _cmd;
        private int _seq;
        public SqlBuilder(SqlCommand cmd)
        {
            _rq = new StringBuilder();
            _cmd = cmd;
            _seq = 0;
        }
        //Les autres surcharges de StringBuilder peuvent être implémenté ici de la même façon, au besoin.
        public SqlBuilder Append(String str)
        {
            _rq.Append(str);
            return this;
        }
        /// <summary>
        /// Ajoute une valeur runtime à la requête, via un paramètre.
        /// </summary>
        /// <param name="value">La valeur à renseigner dans la requête</param>
        /// <param name="type">Le DBType à utiliser pour la création du paramètre. Se référer au type de la colonne cible.</param>
        public SqlBuilder Value(Object value, SqlDbType type)
        {
            //get param name
            string paramName = "@SqlBuilderParam" + _seq++;
            //append condition to query
            _rq.Append(paramName);
            _cmd.Parameters.Add(paramName, type).Value = value;
            return this;
        }
        public SqlBuilder FuzzyValue(Object value, SqlDbType type)
        {
            //get param name
            string paramName = "@SqlBuilderParam" + _seq++;
            //append condition to query
            _rq.Append("'%' + " + paramName + " + '%'");
            _cmd.Parameters.Add(paramName, type).Value = value;
            return this; 
        }

        public override string ToString()
        {
            return _rq.ToString();
        }
    }
}
bbsimonbb
źródło
1

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.

quamrana
źródło
Podwójne apostrofy zostaną przetłumaczone przez serwer sql na pojedynczy apostrof, więc O'Reilly zostanie przetłumaczone na Name = 'O''Reilly'
Rune Grimstad
Czy jest więc odpowiednia funkcja do usuwania apostrofów, gdy użytkownik chce zobaczyć swoje dane?
quamrana
Nie ma potrzeby. Sekwencja ucieczki umożliwia parserowi zobaczenie pojedynczego cudzysłowu zamiast końca ciągu. Podczas analizowania traktuje go ''jako literał ', więc łańcuch będzie postrzegany wewnętrznie jako sekwencja znaków O'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.
cHao,
1

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.

HLGEM
źródło
1

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.

David
źródło
1

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.

VulstaR
źródło
1

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 QUOTENAMET-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.

JasonRShaver
źródło
1

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

bbsimonbb
źródło
0

Oto kilka powodów, dla których warto używać zapytań parametrycznych:

  1. Bezpieczeństwo - warstwa dostępu do bazy danych wie, jak usuwać elementy, które nie są dozwolone w danych, lub zmieniać ich znaczenie.
  2. Rozdzielenie obaw - mój kod nie jest odpowiedzialny za przekształcenie danych do formatu, który lubi baza danych.
  3. Brak redundancji - nie muszę dołączać zestawu lub klasy do każdego projektu, który wykonuje to formatowanie / ucieczki bazy danych; jest wbudowany w bibliotekę klas.
Władca
źródło
0

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.

Dennis C
źródło
0

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.

Paul Fisher
źródło