Wiem, że PreparedStatements unikają / zapobiegają iniekcji SQL. Jak to się dzieje? Czy kwerenda w postaci ostatecznej, która została utworzona przy użyciu PreparedStatements, będzie ciągiem znaków, czy w inny sposób?
Wiem, że PreparedStatements unikają / zapobiegają iniekcji SQL. Jak to się dzieje? Czy kwerenda w postaci ostatecznej, która została utworzona przy użyciu PreparedStatements, będzie ciągiem znaków, czy w inny sposób?
Odpowiedzi:
Problem z iniekcją SQL polega na tym, że dane wejściowe użytkownika są używane jako część instrukcji SQL. Używając przygotowanych instrukcji, możesz wymusić, aby dane wejściowe użytkownika były traktowane jako zawartość parametru (a nie jako część polecenia SQL).
Ale jeśli nie używasz danych wejściowych użytkownika jako parametru dla przygotowanej instrukcji, ale zamiast tego zbudujesz polecenie SQL, łącząc ze sobą ciągi znaków, nadal jesteś podatny na iniekcje SQL, nawet jeśli używasz przygotowanych instrukcji.
Rozważ dwa sposoby zrobienia tego samego:
PreparedStatement stmt = conn.createStatement("INSERT INTO students VALUES('" + user + "')");
stmt.execute();
Lub
PreparedStatement stmt = conn.prepareStatement("INSERT INTO student VALUES(?)");
stmt.setString(1, user);
stmt.execute();
Jeśli „użytkownik” pochodzi z danych wejściowych użytkownika, a dane wejściowe użytkownika to
Robert'); DROP TABLE students; --
Wtedy, w pierwszej kolejności, zostaniesz uwięziony. Po drugie, będziesz bezpieczny i Little Bobby Tables będzie zarejestrowany w Twojej szkole.
Aby zrozumieć, w jaki sposób PreparedStatement zapobiega iniekcji SQL, musimy zrozumieć fazy wykonywania zapytania SQL.
1. Faza kompilacji. 2. Faza wykonania.
Za każdym razem, gdy silnik serwera SQL otrzymuje zapytanie, musi przejść przez poniższe fazy,
Faza analizowania i normalizacji: W tej fazie zapytanie jest sprawdzane pod kątem składni i semantyki. Sprawdza, czy tabela odwołań i kolumny używane w zapytaniu istnieją, czy nie. Ma też wiele innych zadań do wykonania, ale nie omawiajmy szczegółów.
Faza kompilacji: W tej fazie słowa kluczowe używane w zapytaniu, takie jak wybierz, skąd itp. Są konwertowane na format zrozumiały dla maszyny. Jest to faza, w której zapytanie jest interpretowane i podejmowane są odpowiednie działania. Ma też wiele innych zadań do wykonania, ale nie omawiajmy szczegółów.
Plan optymalizacji zapytań: w tej fazie tworzone jest drzewo decyzyjne w celu znalezienia sposobów wykonania zapytania. Dowiaduje się, ile sposobów można wykonać zapytanie oraz koszt związany z każdym sposobem wykonania zapytania. Wybiera najlepszy plan wykonania zapytania.
Pamięć podręczna: najlepszy plan wybrany w planie optymalizacji zapytań jest przechowywany w pamięci podręcznej, dzięki czemu za każdym razem, gdy pojawi się to samo zapytanie, nie musi ponownie przechodzić przez fazę 1, fazę 2 i fazę 3. Kiedy następnym razem pojawi się zapytanie, zostanie sprawdzone bezpośrednio w pamięci podręcznej i stamtąd pobrane do wykonania.
Faza wykonania: w
tej fazie dostarczone zapytanie zostaje wykonane, a dane są zwracane do użytkownika jako ResultSet
obiekt.
PreparedStatements nie są kompletnymi zapytaniami SQL i zawierają symbole zastępcze, które w czasie wykonywania są zastępowane przez rzeczywiste dane podane przez użytkownika.
Ilekroć jakikolwiek PreparedStatment zawierający symbole zastępcze jest przekazywany do silnika SQL Server, przechodzi przez poniższe fazy
UPDATE user set nazwa_użytkownika =? i hasło =? GDZIE id =?
Powyższe zapytanie zostanie przeanalizowane, skompilowane z symbolami zastępczymi w ramach specjalnego traktowania, zoptymalizowane i zbuforowane. Zapytanie na tym etapie jest już kompilowane i konwertowane w formacie zrozumiałym dla komputera. Możemy więc powiedzieć, że zapytanie przechowywane w pamięci podręcznej jest wstępnie skompilowane i tylko symbole zastępcze muszą zostać zastąpione danymi dostarczonymi przez użytkownika.
Teraz w czasie wykonywania, gdy przychodzą dane dostarczone przez użytkownika, wstępnie skompilowane zapytanie jest pobierane z pamięci podręcznej, a symbole zastępcze są zastępowane danymi dostarczonymi przez użytkownika.
(Pamiętaj, że po zastąpieniu znaków miejsca danymi użytkownika ostateczne zapytanie nie jest ponownie kompilowane / interpretowane, a silnik SQL Server traktuje dane użytkownika jako czyste dane, a nie SQL, który musi być ponownie przeanalizowany lub skompilowany; to jest piękno PreparedStatement. )
Jeśli zapytanie nie musi ponownie przechodzić przez fazę kompilacji, wówczas wszelkie dane zastąpione w elementach zastępczych są traktowane jako czyste dane i nie mają znaczenia dla silnika SQL Server i bezpośrednio wykonuje zapytanie.
Uwaga: Jest to faza kompilacji po fazie analizy, która rozumie / interpretuje strukturę zapytania i nadaje jej sensowne zachowanie. W przypadku PreparedStatement zapytanie jest kompilowane tylko raz, a buforowane zapytanie skompilowane jest cały czas pobierane w celu zastąpienia danych użytkownika i wykonania.
Dzięki jednorazowej funkcji kompilacji programu PreparedStatement jest on wolny od ataku SQL Injection.
Możesz uzyskać szczegółowe wyjaśnienie z przykładem tutaj: https://javabypatel.blogspot.com/2015/09/how-prepared-statement-in-java-prevents-sql-injection.html
Kod SQL używany w PreparedStatement jest prekompilowany w sterowniku. Od tego momentu parametry są wysyłane do sterownika jako wartości literalne, a nie wykonywalne fragmenty kodu SQL; dlatego nie można wstrzyknąć kodu SQL za pomocą parametru. Innym korzystnym efektem ubocznym PreparedStatements (prekompilacja + wysyłanie tylko parametrów) jest poprawiona wydajność podczas wielokrotnego uruchamiania instrukcji, nawet z różnymi wartościami parametrów (przy założeniu, że sterownik obsługuje PreparedStatements), ponieważ sterownik nie musi wykonywać analizy SQL i kompilacji każdego z nich czas zmiany parametrów.
Myślę, że to będzie sznurek. Ale parametry wejściowe zostaną przesłane do bazy danych i odpowiednie rzutowanie / konwersje zostaną zastosowane przed utworzeniem rzeczywistej instrukcji SQL.
Aby dać ci przykład, może spróbować sprawdzić, czy CAST / Conversion działa.
Jeśli to zadziała, może stworzyć z tego końcowe oświadczenie.
SELECT * From MyTable WHERE param = CAST('10; DROP TABLE Other' AS varchar(30))
Wypróbuj przykład z instrukcją SQL akceptującą parametr numeryczny.
Teraz spróbuj przekazać zmienną łańcuchową (z zawartością liczbową, którą można zaakceptować jako parametr numeryczny). Czy powoduje to jakiś błąd?
Teraz spróbuj przekazać zmienną łańcuchową (z treścią, której nie można zaakceptować jako parametr numeryczny). Zobacz co się dzieje?
Przygotowane zestawienie jest bezpieczniejsze. Konwertuje parametr na określony typ.
Na przykład stmt.setString(1, user);
przekonwertuje user
parametr na ciąg.
Załóżmy, że parametr zawiera ciąg SQL zawierający wykonywalne polecenie : użycie przygotowanej instrukcji na to nie pozwoli.
Dodaje do tego metaznak (czyli automatyczną konwersję).
To sprawia, że jest bezpieczniejszy.
Wstrzyknięcie SQL: gdy użytkownik ma szansę wprowadzić coś, co mogłoby być częścią instrukcji sql
Na przykład:
Zapytanie ciągowe = „WSTAW WARTOŚCI DO uczniów ('” + użytkownik + „')”
gdy użytkownik wprowadzi „Robert”); DROP TABLE studenci; - ”jako wejście powoduje iniekcję SQL
Jak przygotowane oświadczenie temu zapobiega?
Zapytanie ciągowe = „WSTAW DO uczniów WARTOŚCI ('” + „: nazwa” + „')”
parameters.addValue („nazwa”, użytkownik);
=> gdy użytkownik ponownie wprowadzi „Robert”); DROP TABLE studenci; - „, ciąg wejściowy jest prekompilowany w sterowniku jako wartości dosłowne i myślę, że może być rzutowany na przykład:
CAST („Robert”); DROP TABLE studenci; - „AS varchar (30))
Na końcu ciąg zostanie dosłownie wstawiony jako nazwa do tabeli.
http://blog.linguiming.com/index.php/2018/01/10/why-prepared-statement-avoids-sql-injection/
CAST(‘Robert’);
z CAST(‘Robert’); DROP TABLE students; –‘ AS varchar(30))
pękłaby, a następnie spadłby stół, gdyby tak było. Zatrzymuje zastrzyk, więc uważam, że przykład nie jest wystarczająco kompletny, aby wyjaśnić scenariusz.
Przygotowane oświadczenie:
1) Wstępna kompilacja i buforowanie instrukcji SQL po stronie DB prowadzi do ogólnego szybszego wykonywania i możliwości ponownego użycia tej samej instrukcji SQL w partiach.
2) Automatyczne zapobieganie atakom polegającym na iniekcji SQL poprzez wbudowane znaki cytowania i inne znaki specjalne. Zauważ, że wymaga to użycia dowolnej metody PreparedStatement setXxx () do ustawienia wartości.
Jak wyjaśniono w tym poście , PreparedStatement
samo to nie pomoże, jeśli nadal łączysz ciągi znaków.
Na przykład jeden nieuczciwy napastnik nadal może wykonać następujące czynności:
Nie tylko SQL, ale nawet JPQL lub HQL mogą zostać naruszone, jeśli nie używasz parametrów wiązania.
Podsumowując, nigdy nie należy używać konkatenacji ciągów podczas tworzenia instrukcji SQL. Skorzystaj w tym celu z dedykowanego API:
W przygotowanych zestawieniach użytkownik jest zmuszony do wprowadzania danych jako parametrów. Jeśli użytkownik wprowadzi wrażliwe instrukcje, takie jak DROP TABLE lub SELECT * FROM USERS, nie wpłynie to na dane, ponieważ będą one traktowane jako parametry instrukcji SQL