Jak przygotowane instrukcje mogą chronić przed atakami SQL injection?

Jak prepared statements pomagają nam zapobiegać atakom SQL injection ?

Wikipedia mówi:

Przygotowane instrukcje są odporne na SQL injection, ponieważ wartości parametrów, które są przekazywane później za pomocą innego protokół, nie musi być poprawnie uciekł. Jeżeli oryginalna wypowiedź szablon nie pochodzi z zewnętrznego wejścia, SQL injection nie może występuje.

Nie widzę powodu bardzo dobrze. Co byłoby prostym wyjaśnienie w łatwym języku angielskim i kilka przykładów?

Author: Peter Mortensen, 2011-11-25

9 answers

Pomysł jest bardzo prosty - zapytanie i dane są wysyłane do serwera bazy danych oddzielnie.
To wszystko.

Głównym problemem SQL injection jest mieszanie kodu i danych.

W rzeczywistości nasze zapytanie SQL jest legalnym programem. A taki program tworzymy dynamicznie, dodając pewne dane w locie. Tak więc dane te mogą zakłócać kod programu , a nawet go zmieniać, jak każdy przykład SQL injection pokazuje go (wszystkie przykłady w PHP / Mysql):
$expected_data = 1;
$query = "SELECT * FROM users where id=$expected_data";

Wyświetli zwykłe zapytanie

SELECT * FROM users where id=1

Podczas gdy ten kod

$spoiled_data = "1; DROP TABLE users;"
$query        = "SELECT * FROM users where id=$spoiled_data";

Wytworzy złośliwą sekwencję

SELECT * FROM users where id=1; DROP TABLE users;

Działa, ponieważ dodajemy dane bezpośrednio do ciała programu i stają się one częścią programu, więc dane mogą zmieniać program i w zależności od przekazywanych danych, będziemy mieli albo zwykłe wyjście, albo tabelę users usuniętą.

While w przypadku przygotowanych wypowiedzi nie zmienić nasz program, pozostaje nienaruszony
O to chodzi.

Wysyłamy program najpierw na Serwer

$db->prepare("SELECT * FROM users where id=?");

Gdzie dane są zastępowane przez jakąś zmienną zwaną parametrem lub symbolem zastępczym.

Zauważ, że to samo zapytanie jest wysyłane do serwera, bez żadnych danych w nim! Następnie wysyłamy dane za pomocą Second request, zasadniczo oddzielonego od samego zapytania:

$db->execute($data);

Więc nie może zmienić naszego zaprogramować i wyrządzić jakąkolwiek szkodę.
Całkiem proste-prawda?

Warto jednak zauważyć, że nie za każdym razem kiedy używasz elementu zastępczego, jest on przetwarzany jako przygotowane oświadczenie .

Element Zastępczy jest ogólnym pomysłem na zastąpienie rzeczywistych danych zmienną dla przyszłego przetwarzania (zobacz na przykład printf()), podczas gdy prepared instrukcja jest jedynym podzbiorem tego zbioru.

Są przypadki (szczególnie PDO w PHP może to zrobić), gdy przygotowane oświadczenie można emulować, a zapytanie jest faktycznie złożone wraz z danymi i wysyłane do serwera w jednym żądaniu. Ale ważne jest, aby zrozumieć, że to podejście jest równie bezpieczne, ponieważ każdy bit danych jest odpowiednio sformatowany zgodnie z jego typem i dlatego nic złego nie może się wydarzyć.

Jedyne co muszę dodać, że zawsze pomijane w każdym podręczniku:

Przygotowane instrukcje mogą chronić tylko Dane , ale nie mogą bronić programu itself .
Tak więc, gdy musimy dodać, powiedzmy, dynamiczny identyfikator - nazwa pola, na przykład przygotowane instrukcje nie mogą nam pomóc. Ja wyjaśniła sprawę niedawno, więc nie będę się powtarzać.

 229
Author: Your Common Sense,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-05 07:14:43

Oto SQL do Ustawienia przykładu:

CREATE TABLE employee(name varchar, paymentType varchar, amount bigint);

INSERT INTO employee VALUES('Aaron', 'salary', 100);
INSERT INTO employee VALUES('Aaron', 'bonus', 50);
INSERT INTO employee VALUES('Bob', 'salary', 50);
INSERT INTO employee VALUES('Bob', 'bonus', 0);

Klasa Inject jest podatna na SQL injection. Zapytanie jest dynamicznie wklejane wraz z danymi wejściowymi użytkownika. Celem zapytania było pokazanie informacji o Bobie. Wynagrodzenie lub premia, w zależności od wkładu użytkownika. Ale złośliwy użytkownik manipuluje danymi wejściowymi, uszkadzając zapytanie, naciskając na odpowiednik "or true" do klauzuli where, aby wszystko zostało zwrócone, w tym informacje o Aaronie, który miał ukryj się.

import java.sql.*;

public class Inject {

    public static void main(String[] args) throws SQLException {

        String url = "jdbc:postgresql://localhost/postgres?user=user&password=pwd";
        Connection conn = DriverManager.getConnection(url);

        Statement stmt = conn.createStatement();
        String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='" + args[0] + "'";
        System.out.println(sql);
        ResultSet rs = stmt.executeQuery(sql);

        while (rs.next()) {
            System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
        }
    }
}

Uruchamiając to, pierwszy przypadek jest przy normalnym użyciu, a drugi przy złośliwym wstrzyknięciu:

c:\temp>java Inject salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary'
salary 50

c:\temp>java Inject "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary' OR 'a'!='b'
salary 100
bonus 50
salary 50
bonus 0

Nie powinieneś budować swoich poleceń SQL z ciągiem znaków łączących dane wejściowe użytkownika. Nie tylko jest podatny na iniekcje, ale ma również wpływ na buforowanie na serwerze (Instrukcja zmienia się, więc mniej prawdopodobne jest, że zostanie trafiona pamięć podręczna instrukcji SQL, podczas gdy przykład bind zawsze uruchamia tę samą instrukcję).

Oto przykład wiązania z unikać tego rodzaju iniekcji:

import java.sql.*;

public class Bind {

    public static void main(String[] args) throws SQLException {

        String url = "jdbc:postgresql://localhost/postgres?user=postgres&password=postgres";
        Connection conn = DriverManager.getConnection(url);

        String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?";
        System.out.println(sql);

        PreparedStatement stmt = conn.prepareStatement(sql);
        stmt.setString(1, args[0]);

        ResultSet rs = stmt.executeQuery();

        while (rs.next()) {
            System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
        }
    }
}

Uruchomienie tego z tym samym wejściem co w poprzednim przykładzie pokazuje, że złośliwy kod nie działa, ponieważ nie ma paymentType pasującego do tego ciągu:

c:\temp>java Bind salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?
salary 50

c:\temp>java Bind "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?
 20
Author: Glenn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-14 18:33:16

Zasadniczo, z przygotowanymi instrukcjami dane pochodzące od potencjalnego hakera są traktowane jako dane - i nie ma możliwości, aby mogły być mieszane z SQL aplikacji i/lub być interpretowane jako SQL (co może się zdarzyć, gdy dane przekazywane są umieszczane bezpośrednio w SQL aplikacji).

Dzieje się tak dlatego, że przygotowane instrukcje "przygotowują" najpierw zapytanie SQL, aby znaleźć efektywny plan zapytań i wysłać rzeczywiste wartości, które prawdopodobnie pochodzą z formularza później - w tym czasie zapytanie jest faktycznie stracony.

Więcej informacji tutaj:

Prepared statements and SQL Injection

 13
Author: Jose,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-26 05:41:23

W SQL Server użycie przygotowanej instrukcji jest zdecydowanie odporne na iniekcje, ponieważ parametry wejściowe nie tworzą zapytania. Oznacza to, że wykonane zapytanie nie jest zapytaniem dynamicznym. Przykład instrukcji SQL injection vulnerable.

string sqlquery = "select * from table where username='" + inputusername +"' and password='" + pass + "'";

Teraz, jeśli wartość zmiennej inoutusername jest podobna do' lub 1=1--, to zapytanie staje się:

select * from table where username='a' or 1=1 -- and password=asda

A reszta jest komentowana po --, więc nigdy nie zostanie wykonana i pominięta jako użycie preparowanej instrukcji przykład jak poniżej.

Sqlcommand command = new sqlcommand("select * from table where username = @userinput and password=@pass");
command.Parameters.Add(new SqlParameter("@userinput", 100));
command.Parameters.Add(new SqlParameter("@pass", 100));
command.prepare();

Więc w efekcie nie można wysłać innego parametru, unikając w ten sposób SQL injection...

 4
Author: lloydom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-14 18:39:51

Frazą kluczową jest need not be correctly escaped. Oznacza to, że nie musisz się martwić o ludzi, którzy próbują wrzucić myślniki, apostrofy, cytaty itp...

Wszystko jest załatwione dla Ciebie.
 3
Author: Feisty Mango,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-24 23:19:45

Podczas tworzenia i wysyłania przygotowanej instrukcji do DBMS, jest ona przechowywana jako zapytanie SQL do wykonania.

Później powiązujesz dane z zapytaniem tak, że DBMS używa tych danych jako parametrów zapytania do wykonania (parametryzacji). DBMS nie używa danych, które wiążą się jako uzupełnienie już skompilowanego zapytania SQL; są to po prostu dane.

Oznacza to, że wykonywanie SQL injection jest zasadniczo niemożliwe przy użyciu gotowych instrukcji. Sama natura przygotowanych oświadczenia i ich związek z DBMS uniemożliwia to.

 3
Author: wulfgarpro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-13 12:24:34
ResultSet rs = statement.executeQuery("select * from foo where value = " + httpRequest.getParameter("filter");

Załóżmy, że masz to w Serwletie masz rację. Jeśli złośliwa osoba podała złą wartość "filtra", możesz włamać się do bazy danych.

 2
Author: MeBigFatGuy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-14 18:25:21

[6]}przeczytałem odpowiedzi i nadal czułem potrzebę podkreślenia kluczowego punktu, który oświeca istotę przygotowanych wypowiedzi. Rozważ dwa sposoby odpytywania własnej bazy danych, w której zaangażowane jest wprowadzanie danych przez użytkownika:

Naiwne Podejście

Jeden łączy dane wejściowe użytkownika z częściowym łańcuchem SQL, aby wygenerować instrukcję SQL. W tym przypadku użytkownik może osadzić złośliwe polecenia SQL, które następnie zostaną wysłane do bazy danych w celu wykonania.

String SQLString = "SELECT * FROM CUSTOMERS WHERE NAME='"+userInput+"'"

Na przykład złośliwy użytkownik Wejście Może prowadzić do tego, że SQLString jest równe "SELECT * FROM CUSTOMERS WHERE NAME='James';DROP TABLE CUSTOMERS;'

Ze względu na złośliwego użytkownika, SQLString zawiera 2 wypowiedzi, gdzie 2nd one ("DROP TABLE CUSTOMERS") spowoduje szkodę.

Prepared Statements

W tym przypadku, ze względu na oddzielenie zapytania i danych, Dane wejściowe użytkownika nigdy nie są traktowane jako polecenie SQL, i dlatego nigdy nie są wykonywane. Z tego powodu każdy złośliwy kod SQL wstrzyknięty nie zaszkodzi. Więc "DROP TABLE CUSTOMERS" nigdy nie zostanie wykonana w przypadku powyżej.

W skrócie, z przygotowanymi wypowiedziami złośliwy kod wprowadzony przez użytkownika nie zostanie wykonany!

 1
Author: N.Vegeta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-27 02:04:21

Przyczyna Główna # 1-Problem Ogranicznika

SQL injection jest możliwe, ponieważ używamy cudzysłowów do rozgraniczania łańcuchów, a także jako części łańcuchów, co uniemożliwia ich czasami interpretację. Gdybyśmy mieli ograniczniki, które nie mogłyby być używane w danych łańcuchowych, SQL injection nigdy by się nie stało. Rozwiązanie problemu delimitera eliminuje problem SQL injection. Zapytania struktury tak robią.

Przyczyna #2 - ludzka natura, ludzie są przebiegli i Niektórzy Przebiegli Ludzie Są Złośliwi I Wszyscy Ludzie Popełniają Błędy

Drugą podstawową przyczyną SQL injection jest ludzka natura. Ludzie, w tym Programiści, popełniają błędy. Jeśli popełnisz błąd w ustrukturyzowanym zapytaniu, nie czyni to Twojego systemu podatnym na SQL injection. Jeśli nie używasz zapytań strukturalnych, błędy mogą generować lukę SQL injection.

Jak ustrukturyzowane zapytania rozwiązują podstawowe przyczyny SQL Wstrzyknięcie

Zapytania strukturalne rozwiązują problem ogranicznika, umieszczając polecenia sql w jednej instrukcji i umieszczając dane w oddzielnej instrukcji programowania. Instrukcje programowania tworzą separację potrzebną.

Ustrukturyzowane zapytania pomagają zapobiegać powstawaniu krytycznych luk w zabezpieczeniach przez błędy ludzkie. W odniesieniu do ludzi popełniających błędy, SQL injection nie może się zdarzyć, gdy używane są zapytania o strukturę. Istnieją sposoby zapobiegania SQL injection, które nie wymagają ustrukturyzowane zapytania, ale normalny błąd człowieka w tym podejściu zwykle prowadzi do przynajmniej pewnej ekspozycji na SQL injection. Ustrukturyzowane zapytania są bezpieczne dla błędów z SQL injection. Możesz popełnić wszystkie błędy na świecie, prawie, z zapytaniami strukturalnymi, tak samo jak każdy inny program, ale żaden, który możesz zrobić, nie może być zamieniony w ssstem przejętym przez SQL injection. Dlatego ludzie lubią mówić, że jest to właściwy sposób, aby zapobiec SQL injection.

No to masz, przyczyny sql injection i charakter ustrukturyzowanych zapytań, które uniemożliwiają ich użycie.

 0
Author: DanAllen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-09 16:10:48