Używaj przygotowanych instrukcji i sparametryzowanych zapytań. Są to instrukcje SQL wysyłane i analizowane przez serwer bazy danych niezależnie od jakichkolwiek parametrów. W ten sposób atakujący nie może wstrzyknąć złośliwego kodu SQL.
Zasadniczo masz dwie opcje, aby to osiągnąć:
Za pomocą PDO (dla dowolnego obsługiwanego sterownika bazy danych):
$stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
$stmt->execute([ 'name' => $name ]);
foreach ($stmt as $row) {
// Do something with $row
}
Za pomocą MySQLi (dla MySQL):
$stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
$stmt->bind_param('s', $name); // 's' specifies the variable type => 'string'
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
// Do something with $row
}
Jeśli łączysz się z bazą danych innych niż MySQL, istnieje druga opcja kierowca specyficzne, które mogą odnosić się do (na przykład, pg_prepare()
i pg_execute()
PostgreSQL). ChNP jest opcją uniwersalną.
Prawidłowe skonfigurowanie połączenia
Zauważ, że podczas PDO
uzyskiwania dostępu do bazy danych MySQL rzeczywiste przygotowane instrukcje nie są domyślnie używane . Aby to naprawić, musisz wyłączyć emulację przygotowanych instrukcji. Przykładem utworzenia połączenia za pomocą PDO jest:
$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'password');
$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
W powyższym przykładzie tryb błędu nie jest absolutnie konieczny, ale zaleca się jego dodanie . W ten sposób skrypt nie zatrzyma się, Fatal Error
gdy coś pójdzie nie tak. I daje to deweloperowi szansę na catch
każdy błąd (błędy), które są throw
równe n PDOException
.
Obowiązkowa jest jednak pierwsza setAttribute()
linia, która mówi PDO o wyłączeniu emulowanych przygotowanych instrukcji i użyciu rzeczywistych przygotowanych instrukcji. Dzięki temu instrukcja i wartości nie są analizowane przez PHP przed wysłaniem ich na serwer MySQL (nie dając potencjalnemu napastnikowi szansy na wstrzyknięcie złośliwego SQL).
Chociaż możesz ustawić charset
opcje w konstruktorze, ważne jest, aby pamiętać, że „starsze” wersje PHP (przed 5.3.6) cicho ignorowały parametr charset w DSN.
Wyjaśnienie
Przekazana instrukcja SQL prepare
jest analizowana i kompilowana przez serwer bazy danych. Określając parametry (albo ?
parametr nazwany, jak :name
w powyższym przykładzie), informujesz aparat bazy danych, gdzie chcesz filtrować. Następnie podczas połączenia execute
przygotowana instrukcja jest łączona z określonymi wartościami parametrów.
Ważne jest tutaj to, że wartości parametrów są łączone ze skompilowaną instrukcją, a nie ciągiem SQL. Wstrzykiwanie SQL polega na oszukiwaniu skryptu w celu uwzględnienia złośliwych ciągów podczas tworzenia kodu SQL w celu wysłania go do bazy danych. Wysyłając rzeczywisty kod SQL oddzielnie od parametrów, ograniczasz ryzyko, że skończysz z czymś, czego nie zamierzałeś.
Wszelkie parametry wysyłane podczas korzystania z przygotowanej instrukcji będą traktowane jak łańcuchy (chociaż silnik bazy danych może przeprowadzić pewną optymalizację, więc parametry mogą oczywiście również zostać przekształcone w liczby). W powyższym przykładzie, jeśli $name
zmienna zawiera 'Sarah'; DELETE FROM employees
wynik, byłoby po prostu wyszukiwaniem łańcucha "'Sarah'; DELETE FROM employees"
i nie skończy się pustą tabelą .
Kolejną zaletą korzystania z przygotowanych instrukcji jest to, że jeśli wykonasz tę samą instrukcję wiele razy w tej samej sesji, zostanie ona przeanalizowana i skompilowana tylko raz, co da ci pewne przyspieszenie.
Aha, a skoro zapytałeś, jak to zrobić dla wstawki, oto przykład (przy użyciu PDO):
$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');
$preparedStatement->execute([ 'column' => $unsafeValue ]);
Czy przygotowane zapytania mogą być używane do zapytań dynamicznych?
Chociaż nadal można używać przygotowanych instrukcji dla parametrów zapytania, nie można sparametryzować samej struktury zapytania dynamicznego i nie można sparametryzować niektórych funkcji zapytania.
W tych konkretnych scenariuszach najlepiej jest użyć filtru białej listy, który ogranicza możliwe wartości.
// Value whitelist
// $dir can only be 'DESC', otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
$dir = 'ASC';
}
Jeśli używasz najnowszej wersji PHP,
mysql_real_escape_string
opcja opisana poniżej nie będzie już dostępna (choćmysqli::escape_string
jest nowoczesnym odpowiednikiem). Obecniemysql_real_escape_string
opcja ta miałaby sens tylko w przypadku starszych wersji kodu PHP.Masz dwie opcje - unikanie znaków specjalnych
unsafe_variable
lub użycie sparametryzowanego zapytania. Oba chroniłyby cię przed iniekcją SQL. Sparametryzowane zapytanie jest uważane za lepszą praktykę, ale będzie wymagać zmiany na nowsze rozszerzenie MySQL w PHP, zanim będzie można go użyć.Najpierw zakryjemy strunę udarową, która ucieka przed nią.
Zobacz także szczegóły
mysql_real_escape_string
funkcji.Aby użyć sparametryzowanego zapytania, musisz użyć MySQLi zamiast funkcji MySQL . Aby przepisać twój przykład, potrzebowalibyśmy czegoś takiego.
Oto kluczowa funkcja, którą chcesz przeczytać
mysqli::prepare
.Ponadto, jak sugerują inni, może okazać się użyteczne / łatwiejsze zwiększenie warstwy abstrakcji za pomocą czegoś takiego jak PDO .
Pamiętaj, że sprawa, o którą pytałeś, jest dość prosta i że bardziej złożone sprawy mogą wymagać bardziej skomplikowanego podejścia. W szczególności:
mysql_real_escape_string
. W takim przypadku lepiej byłoby przekazać dane użytkownika przez białą listę, aby zapewnić, że dozwolone są tylko „bezpieczne” wartości.mysql_real_escape_string
zastosujesz podejście, będziesz cierpieć z powodu problemu opisanego przez Polynomial w komentarzach poniżej. Ten przypadek jest trudniejszy, ponieważ liczby całkowite nie będą otoczone cudzysłowami, więc można sobie z tym poradzić, sprawdzając, czy dane wejściowe użytkownika zawierają tylko cyfry.źródło
mysql_real_escape_string
wystarczy czy muszę używać parametryzowane też?htmlentities
na przykładmysql_real_escape_string()
kompletności, ale nie jestem fanem wymieniania najpierw najbardziej podatnego na błędy podejścia. Czytelnik może po prostu szybko złapać pierwszy przykład. Dobrze, że teraz jest przestarzałe :)mysql_*
funkcje są przestarzałe. Zostały one zastąpione podobnymimysqli_*
funkcjami, takimi jakmysqli_real_escape_string
.Każda odpowiedź tutaj obejmuje tylko część problemu. W rzeczywistości istnieją cztery różne części zapytania, które możemy dynamicznie dodawać do SQL: -
A przygotowane oświadczenia obejmują tylko dwa z nich.
Ale czasami musimy uczynić nasze zapytanie jeszcze bardziej dynamicznym, dodając również operatory lub identyfikatory. Będziemy potrzebować różnych technik ochrony.
Ogólnie rzecz biorąc, takie podejście do ochrony opiera się na białej liście .
W takim przypadku każdy parametr dynamiczny powinien być zakodowany na stałe w skrypcie i wybrany z tego zestawu. Na przykład, aby wykonać dynamiczne porządkowanie:
Aby ułatwić ten proces, napisałem funkcję pomocnika na białej liście, która wykonuje całą pracę w jednym wierszu:
Istnieje inny sposób zabezpieczenia identyfikatorów - ucieczka, ale raczej trzymam się białej listy jako bardziej niezawodnego i wyraźnego podejścia. Jednak dopóki masz podany identyfikator, możesz uciec od znaku cytatu, aby był bezpieczny. Na przykład domyślnie dla mysql musisz podwoić znak cudzysłowu, aby go uciec . W przypadku innych innych reguł ucieczki DBMS byłyby inne.
Mimo to, istnieje problem ze składni SQL słów kluczowych (takich jak
AND
,DESC
i takie), ale biało-wymieniając tylko podejście wydaje się w tym przypadku.Tak więc ogólne zalecenie można sformułować jako
Aktualizacja
Chociaż istnieje ogólna zgoda co do najlepszych praktyk dotyczących ochrony przed wstrzykiwaniem SQL, nadal istnieje również wiele złych praktyk. A niektóre z nich są zbyt głęboko zakorzenione w umysłach użytkowników PHP. Na przykład na tej samej stronie znajduje się (choć niewidoczna dla większości odwiedzających) ponad 80 usuniętych odpowiedzi - wszystkie usunięte przez społeczność z powodu złej jakości lub promujące złe i nieaktualne praktyki. Co gorsza, niektóre złe odpowiedzi nie są usuwane, ale mają się dobrze.
Na przykład (1) jest (2) wciąż (3) wiele (4) odpowiedzi (5) , w tym druga najbardziej pozytywna odpowiedź sugerująca ręczne ucieczkę łańcucha - przestarzałe podejście, które okazało się niepewne.
Lub istnieje nieco lepsza odpowiedź, która sugeruje tylko inną metodę formatowania łańcucha, a nawet chwali się nią jako ostatecznym panaceum. Oczywiście tak nie jest. Ta metoda nie jest lepsza niż zwykłe formatowanie ciągów, ale zachowuje wszystkie swoje wady: ma zastosowanie tylko do ciągów i, jak każde inne ręczne formatowanie, jest zasadniczo opcjonalna, nieobowiązkowa, podatna na wszelkiego rodzaju błędy ludzkie.
Myślę, że wszystko to z powodu jednego bardzo starego przesądu, wspieranego przez takie autorytety, jak OWASP lub podręcznik PHP , który głosi równość między „ucieczką” a ochroną przed wstrzyknięciami SQL.
Niezależnie od tego, co od dawna mówi PHP,
*_escape_string
w żadnym wypadku nie zapewnia bezpieczeństwa danych i nigdy nie było zamierzone. Oprócz tego, że jest bezużyteczny dla jakiejkolwiek części SQL innej niż łańcuch, ręczne zmiany znaczenia są niepoprawne, ponieważ są ręczne, w przeciwieństwie do automatycznych.A OWASP czyni go jeszcze gorszym, kładąc nacisk na ucieczkę od danych wejściowych użytkownika, co jest całkowitym nonsensem: nie powinno być takich słów w kontekście ochrony przed wstrzyknięciem. Każda zmienna jest potencjalnie niebezpieczna - bez względu na źródło! Innymi słowy - każda zmienna musi być odpowiednio sformatowana, aby mogła zostać wprowadzona do zapytania - bez względu na źródło ponownie. Liczy się cel. W momencie, gdy programista zaczyna oddzielać owce od kóz (zastanawiając się, czy jakaś konkretna zmienna jest „bezpieczna”, czy nie), stawia swój pierwszy krok w kierunku katastrofy. Nie wspominając już o tym, że nawet sformułowanie sugeruje masową ucieczkę w punkcie wejścia, przypominając bardzo magiczną funkcję cytatów - już pogardzaną, przestarzałą i usuniętą.
Tak więc, w przeciwieństwie do „uciekających”, przygotowanych instrukcji jest miarą, która rzeczywiście chroni przed wstrzyknięciem SQL (jeśli dotyczy).
źródło
Polecam używanie PDO (PHP Data Objects) do uruchamiania sparametryzowanych zapytań SQL.
Nie tylko chroni to przed iniekcją SQL, ale także przyspiesza zapytania.
I za pomocą PDO zamiast
mysql_
,mysqli_
ipgsql_
funkcji, należy złożyć trochę bardziej oderwane od bazy danych, w związku z rzadkim występowaniem, że masz do baz danych dostawców przełączników.źródło
Użyj
PDO
i przygotowane zapytania.(
$conn
jestPDO
przedmiotem)źródło
Jak widać, ludzie sugerują, abyś używał co najwyżej przygotowanych wypowiedzi. Nie jest to złe, ale gdy zapytanie jest wykonywane tylko raz na proces, może wystąpić niewielka utrata wydajności.
Miałem do czynienia z tym problemem, ale myślę, że rozwiązałem go w bardzo wyrafinowany sposób - w sposób, w jaki hakerzy używają, aby uniknąć używania cudzysłowów. Użyłem tego w połączeniu z emulowanymi przygotowanymi oświadczeniami. Używam go, aby zapobiegać wszelkim możliwym atakom typu SQL injection.
Moje podejście:
Jeśli spodziewasz się, że wejście będzie liczbą całkowitą, upewnij się, że naprawdę jest liczbą całkowitą. W języku zmiennym, takim jak PHP, jest to bardzo ważne. Możesz na przykład użyć tego bardzo prostego, ale potężnego rozwiązania:
sprintf("SELECT 1,2,3 FROM table WHERE 4 = %u", $input);
Jeśli oczekujesz czegoś innego od liczby całkowitej, użyj heksa . Jeśli przeklniesz go, całkowicie unikniesz wprowadzania danych. W C / C ++ istnieje funkcja o nazwie
mysql_hex_string()
, w PHP można użyćbin2hex()
.Nie martw się, że łańcuch znaków będzie miał rozmiar 2x oryginalnej długości, ponieważ nawet jeśli użyjesz
mysql_real_escape_string
, PHP musi przydzielić taką samą pojemność((2*input_length)+1)
, która jest taka sama.Ta metoda heksadecymalna jest często używana podczas przesyłania danych binarnych, ale nie widzę powodu, dla którego miałaby nie używać jej do wszystkich danych, aby zapobiec atakom iniekcyjnym SQL. Pamiętaj, że musisz dodać dane za
0x
pomocą funkcji MySQL lubUNHEX
zamiast tego.Na przykład zapytanie:
Stanie się:
lub
Hex to idealna ucieczka. Nie ma możliwości wstrzyknięcia.
Różnica między funkcją UNHEX a prefiksem 0x
W komentarzach była dyskusja, więc w końcu chcę to wyjaśnić. Te dwa podejścia są bardzo podobne, ale pod pewnymi względami są nieco inne:
Prefiksu ** 0x ** można używać tylko w przypadku kolumn danych, takich jak char, varchar, text, block, binary itp .
Również jego użycie jest nieco skomplikowane, jeśli masz zamiar wstawić pusty ciąg. Musisz całkowicie go zastąpić
''
, w przeciwnym razie pojawi się błąd.UNHEX () działa na dowolnej kolumnie; nie musisz się martwić o pusty ciąg.
Metody szesnastkowe są często używane jako ataki
Zauważ, że ta metoda heksadecymalna jest często używana jako atak iniekcyjny SQL, w którym liczby całkowite są jak łańcuchy i są poprzedzane znakami ucieczki
mysql_real_escape_string
. Następnie możesz uniknąć stosowania cytatów.Na przykład, jeśli po prostu zrobisz coś takiego:
atak może cię bardzo łatwo wstrzyknąć . Rozważ następujący wstrzyknięty kod zwrócony ze skryptu:
a teraz po prostu wyodrębnij strukturę tabeli:
A następnie wybierz dane, które chcesz. Czy to nie fajne?
Ale jeśli koder witryny do wstrzykiwania by ją przeklął, nie byłoby to możliwe, ponieważ zapytanie wyglądałoby tak:
SELECT ... WHERE id = UNHEX('2d312075...3635')
źródło
+
ale zCONCAT
. I do wydajności: nie sądzę, że wpływa to na wydajność, ponieważ mysql musi analizować dane i nie ma znaczenia, czy pochodzenie jest łańcuchem, czy szesnastkiem'root'
lub możesz go przekreślić,0x726f6f74
ALE jeśli chcesz numer i wysłać go jako ciąg, prawdopodobnie napiszesz „42”, a nie CHAR (42 ) ... '42' w hex byłoby0x3432
nie0x42
SELECT title FROM article WHERE id = UNHEX(' . bin2hex($_GET["id"]) . ')
Zapobieganie wstrzyknięciom - mysql_real_escape_string ()
PHP ma specjalnie przygotowaną funkcję, która zapobiega takim atakom. Wszystko, co musisz zrobić, to użyć łyk z funkcji
mysql_real_escape_string
.mysql_real_escape_string
pobiera ciąg znaków, który będzie używany w zapytaniu MySQL i zwraca ten sam ciąg przy bezpiecznej ucieczce wszystkich prób wstrzyknięcia SQL. Zasadniczo zastąpi te kłopotliwe cytaty ('), które użytkownik może wprowadzić, zastępując je cytatem bezpiecznym dla MySQL, cytatem \.UWAGA: aby korzystać z tej funkcji, musisz być podłączony do bazy danych!
// Połącz się z MySQL
Więcej informacji można znaleźć w MySQL - Zapobieganie iniekcji SQL .
źródło
mysql_real_escape_string
celem jest umożliwienie zbudowania poprawnego zapytania SQL dla każdego wejściowego ciągu danych. Zapobieganie iniekcji sql jest efektem ubocznym tej funkcji.mysql_real_escape_string()
nie jest nieomylny .mysql_real_escape_string
jest teraz przestarzałe, więc nie jest już realną opcją. W przyszłości zostanie usunięty z PHP. Najlepiej przejść do tego, co polecają ludzie PHP lub MySQL.Możesz zrobić coś takiego podstawowego:
To nie rozwiąże każdego problemu, ale jest to bardzo dobry odskocznia. Pominąłem oczywiste elementy, takie jak sprawdzenie istnienia zmiennej, jej formatu (cyfry, litery itp.).
źródło
$q = "SELECT col FROM tbl WHERE x = $safe_var";
na przykład. Ustawienie$safe_var
do1 UNION SELECT password FROM users
prac w tym przypadku, ze względu na brak cytatów. Możliwe jest również wstrzyknięcie ciągów do zapytania za pomocąCONCAT
iCHR
.mysql_real_escape_string()
nie jest nieomylny .mysql_real_escape_string
jest teraz przestarzałe, więc nie jest już realną opcją. W przyszłości zostanie usunięty z PHP. Najlepiej przejść do tego, co polecają ludzie PHP lub MySQL.Cokolwiek zrobisz, skorzystaj z tego, upewnij się, że sprawdziłeś, czy dane wejściowe nie zostały już zniekształcone przez
magic_quotes
inne śmieci o dobrym znaczeniu, i jeśli to konieczne, przejrzyj jestripslashes
lub cokolwiek, aby je zdezynfekować.źródło
Sparametryzowane zapytanie ORAZ sprawdzanie poprawności danych wejściowych jest właściwą drogą. Istnieje wiele scenariuszy, w których może wystąpić zastrzyk SQL, mimo że
mysql_real_escape_string()
został użyty.Te przykłady są podatne na wstrzyknięcie SQL:
lub
W obu przypadkach nie można użyć
'
do ochrony enkapsulacji.Źródło : Nieoczekiwany zastrzyk SQL (gdy ucieczka nie jest wystarczająca)
źródło
Moim zdaniem najlepszym sposobem, aby ogólnie zapobiec iniekcji SQL w twojej aplikacji PHP (lub dowolnej innej aplikacji internetowej) jest przemyślenie architektury aplikacji. Jeśli jedynym sposobem ochrony przed wstrzykiwaniem SQL jest pamiętanie o zastosowaniu specjalnej metody lub funkcji, która wykonuje Prawidłowe działanie za każdym razem, gdy rozmawiasz z bazą danych, robisz to źle. W ten sposób kwestia czasu zajmuje zapomnienie o prawidłowym sformatowaniu zapytania w pewnym momencie kodu.
Przyjęcie wzorca MVC i frameworka, takiego jak CakePHP lub CodeIgniter, jest prawdopodobnie właściwą drogą: typowe zadania, takie jak tworzenie bezpiecznych zapytań do bazy danych zostały rozwiązane i centralnie zaimplementowane w takich frameworkach. Pomagają one zorganizować aplikację internetową w rozsądny sposób i sprawiają, że myślisz więcej o ładowaniu i zapisywaniu obiektów niż o bezpiecznym tworzeniu pojedynczych zapytań SQL.
źródło
Preferuję procedury przechowywane ( MySQL obsługuje procedury przechowywane od 5.0 ) z punktu widzenia bezpieczeństwa - zalety to -
Wady to -
źródło
Istnieje wiele sposobów zapobiegania zastrzykom SQL i innym włamaniom SQL. Możesz go łatwo znaleźć w Internecie (wyszukiwarka Google). Oczywiście PDO jest jednym z dobrych rozwiązań. Ale chciałbym zasugerować dobre zapobieganie linkom przed wstrzyknięciem SQL.
Co to jest wstrzyknięcie SQL i jak temu zapobiec
Podręcznik PHP do wstrzykiwania SQL
Wyjaśnienie Microsoft iniekcji SQL i zapobiegania w PHP
I niektóre inne, takie jak Zapobieganie iniekcji SQL z MySQL i PHP .
Teraz, dlaczego trzeba zrobić, aby zapobiec zapytanie z SQL injection?
Chciałbym poinformować: Dlaczego staramy się zapobiegać wstrzykiwaniu SQL za pomocą krótkiego przykładu poniżej:
Zapytanie dotyczące uwierzytelnienia logowania:
Teraz, jeśli ktoś (haker) umieści
i hasło cokolwiek ....
Zapytanie zostanie przeanalizowane w systemie tylko do:
Druga część zostanie odrzucona. Co się stanie? Nieautoryzowany użytkownik (haker) będzie mógł zalogować się jako administrator bez hasła. Teraz on / ona może zrobić wszystko, co administrator / osoba e-mail może zrobić. Zobacz, to bardzo niebezpieczne, jeśli nie zapobiega się iniekcji SQL.
źródło
Myślę, że jeśli ktoś chce użyć PHP i MySQL lub innego serwera dataBase:
(int)$foo
. Przeczytaj więcej o typie zmiennych w PHP tutaj . Jeśli korzystasz z bibliotek takich jak PDO lub MySQLi, zawsze używaj PDO :: quote () i mysqli_real_escape_string () .Przykłady bibliotek:
---- ChNP
--- MySQLi
PS :
PDO wygrywa tę bitwę z łatwością. Dzięki obsłudze dwunastu różnych sterowników baz danych i nazwanych parametrów możemy zignorować niewielki spadek wydajności i przyzwyczaić się do jego interfejsu API. Z punktu widzenia bezpieczeństwa oba są bezpieczne, o ile deweloper używa ich tak, jak powinny
Ale podczas gdy zarówno PDO, jak i MySQLi są dość szybkie, MySQLi działa nieznacznie szybciej w testach porównawczych - ~ 2,5% w przypadku nieprzygotowanych instrukcji i ~ 6,5% w przypadku przygotowanych instrukcji.
I proszę przetestować każde zapytanie do bazy danych - to lepszy sposób, aby zapobiec wstrzyknięciu.
źródło
Jeśli to możliwe, rzutuj typy swoich parametrów. Ale działa tylko na prostych typach, takich jak int, bool i float.
źródło
Jeśli chcesz skorzystać z mechanizmów pamięci podręcznej, takich jak Redis lub Memcached , być może DALMP może być wyborem. Używa czystego MySQLi . Sprawdź to: Warstwa abstrakcji bazy danych DALMP dla MySQL przy użyciu PHP.
Ponadto można „przygotować” argumenty przed przygotowaniem zapytania, aby można było tworzyć zapytania dynamiczne, a na końcu mieć w pełni przygotowane zapytanie. Warstwa abstrakcji bazy danych DALMP dla MySQL przy użyciu PHP.
źródło
Dla tych, którzy nie są pewni, jak używać PDO (pochodzącego z
mysql_
funkcji), stworzyłem bardzo, bardzo proste opakowanie PDO, które jest pojedynczym plikiem. Istnieje, aby pokazać, jak łatwo jest wykonać wszystkie typowe czynności, które muszą być wykonane przez aplikacje. Współpracuje z PostgreSQL, MySQL i SQLite.Zasadniczo, przeczytaj ją podczas czytania instrukcji , aby zobaczyć, jak umieścić funkcje PDO do wykorzystania w prawdziwym życiu, aby to proste do przechowywania i pobierania wartości w formacie ty chcesz.
źródło
Za pomocą tej funkcji PHP
mysql_escape_string()
można szybko uzyskać dobre zapobieganie.Na przykład:
mysql_escape_string
- Ucieka ciąg znaków do użycia w mysql_queryAby więcej zapobiegać, możesz dodać na końcu ...
Wreszcie otrzymujesz:
źródło
Kilka wskazówek dotyczących zmiany znaczenia znaków specjalnych w instrukcjach SQL.
Nie używaj MySQL . To rozszerzenie jest przestarzałe. Zamiast tego użyj MySQLi lub PDO .
MySQLi
Do ręcznego zmiany znaków specjalnych w ciągu można użyć funkcji mysqli_real_escape_string . Ta funkcja nie będzie działać poprawnie, chyba że w zestawie mysqli_set_charset zostanie ustawiony prawidłowy zestaw znaków .
Przykład:
Do automatycznego zmiany wartości przygotowanych instrukcji użyj mysqli_prepare i mysqli_stmt_bind_param, gdzie należy podać typy odpowiednich zmiennych powiązania dla odpowiedniej konwersji:
Przykład:
Bez względu na to, czy używasz przygotowanych instrukcji
mysqli_real_escape_string
, czy zawsze musisz znać typ danych wejściowych, z którymi pracujesz.Więc jeśli używasz przygotowanej instrukcji, musisz określić typy zmiennych dla
mysqli_stmt_bind_param
funkcji.I używa
mysqli_real_escape_string
się go, jak sama nazwa wskazuje, do unikania znaków specjalnych w ciągu, więc nie zapewni to bezpieczeństwa liczb całkowitych. Celem tej funkcji jest zapobieganie zerwaniu ciągów w instrukcjach SQL i uszkodzeniu bazy danych, które mogłoby to spowodować.mysqli_real_escape_string
jest użyteczną funkcją, jeśli jest właściwie używana, szczególnie w połączeniu zsprintf
.Przykład:
źródło
Prostą alternatywę dla tego problemu można rozwiązać, przyznając odpowiednie uprawnienia w samej bazie danych. Na przykład: jeśli korzystasz z bazy danych MySQL, wejdź do bazy danych za pośrednictwem terminalu lub interfejsu użytkownika i wykonaj następujące polecenie:
Ograniczy to użytkownika do ograniczenia tylko do określonego zapytania. Usuń uprawnienie do usuwania, aby dane nigdy nie zostały usunięte z zapytania uruchomionego ze strony PHP. Drugą rzeczą do zrobienia jest opróżnienie uprawnień, aby MySQL odświeżył uprawnienia i aktualizacje.
więcej informacji na temat koloru .
Aby zobaczyć bieżące uprawnienia użytkownika, uruchom następujące zapytanie.
Dowiedz się więcej o GRANT .
źródło
Jeśli chodzi o wiele przydatnych odpowiedzi, mam nadzieję, że dodam trochę wartości temu wątkowi.
Wstrzyknięcie SQL to atak, który można wykonać za pomocą danych wejściowych użytkownika (danych wejściowych, które użytkownik wypełnił, a następnie wykorzystał w zapytaniach). Wzorce wstrzykiwania SQL są poprawną składnią zapytań, którą możemy nazwać: złe zapytania z niewłaściwych powodów i zakładamy, że może być zła osoba, która próbuje uzyskać tajne informacje (omijając kontrolę dostępu), które wpływają na trzy zasady bezpieczeństwa (poufność) , integralność i dostępność).
Teraz naszym celem jest zapobieganie zagrożeniom bezpieczeństwa, takim jak ataki polegające na wstrzykiwaniu SQL, zadawanie pytań (jak zapobiegać atakom polegającym na wstrzykiwaniu SQL za pomocą PHP), być bardziej realistycznym, filtrowanie lub usuwanie danych wejściowych ma miejsce w przypadku korzystania z danych wprowadzanych przez użytkownika wewnątrz takie zapytanie, użycie PHP lub innego języka programowania nie jest prawdą lub, jak zaleca więcej osób, aby korzystać z nowoczesnych technologii, takich jak przygotowane oświadczenie lub inne narzędzia, które obecnie obsługują zapobieganie wstrzykiwaniu SQL, uważają, że te narzędzia nie są już dostępne? Jak zabezpieczyć aplikację?
Moje podejście przeciwko iniekcji SQL to: czyszczenie danych wejściowych użytkownika przed wysłaniem ich do bazy danych (przed użyciem ich w dowolnym zapytaniu).
Filtrowanie danych (konwersja niebezpiecznych danych na bezpieczne dane)
Weź pod uwagę, że PDO i MySQLi nie są dostępne. Jak możesz zabezpieczyć swoją aplikację? Czy zmuszasz mnie do korzystania z nich? Co z innymi językami innymi niż PHP? Wolę przedstawiać ogólne pomysły, ponieważ można je wykorzystać dla szerszych granic, a nie tylko dla konkretnego języka.
ZASADA: nie twórz jednego użytkownika bazy danych dla wszystkich uprawnień. Dla wszystkich operacji SQL możesz utworzyć swój schemat, taki jak (deluser, selectuser, updateuser) jako nazwy użytkownika dla łatwego użycia.
Zobacz zasadę najmniejszych uprawnień .
Filtrowanie danych: przed zbudowaniem dowolnego zapytania użytkownika należy je zweryfikować i przefiltrować. Dla programistów ważne jest zdefiniowanie niektórych właściwości dla każdej zmiennej wprowadzanej przez użytkownika: typ danych, wzorzec danych i długość danych . Pole, które jest liczbą pomiędzy (xiy), musi być dokładnie sprawdzone przy użyciu ścisłej reguły, a dla pola, które jest łańcuchem (tekstem): przypadek ma wzór, na przykład nazwa użytkownika musi zawierać tylko niektóre znaki, powiedz [a-zA-Z0-9_-.]. Długość waha się między (x i n), gdzie x i n (liczby całkowite, x <= n). Reguła: tworzenie dokładnych filtrów i reguł sprawdzania poprawności to dla mnie najlepsze praktyki.
Użyj innych narzędzi: tutaj również zgodzę się z tobą, że przygotowane oświadczenie (sparametryzowane zapytanie) i procedury składowane. Wadą jest to, że te sposoby wymagają zaawansowanych umiejętności, które nie istnieją dla większości użytkowników. Podstawową ideą jest tutaj rozróżnienie między zapytaniem SQL a danymi używanymi w środku. Oba podejścia można zastosować nawet w przypadku niebezpiecznych danych, ponieważ dane wprowadzone przez użytkownika tutaj nie dodają niczego do pierwotnego zapytania, takiego jak (dowolny lub x = x).
Aby uzyskać więcej informacji, przeczytaj OWASP SQL Injection Prevention Cheat Sheet .
Teraz, jeśli jesteś zaawansowanym użytkownikiem, zacznij używać tej obrony, jak chcesz, ale dla początkujących, jeśli nie mogą szybko wdrożyć procedury składowanej i przygotowali instrukcję, lepiej filtrować dane wejściowe, na ile mogą.
Wreszcie, rozważmy, że użytkownik wysyła ten tekst poniżej, zamiast wpisywać swoją nazwę użytkownika:
Dane wejściowe można sprawdzić wcześniej bez przygotowanej instrukcji i procedur przechowywanych, ale dla pewności korzystanie z nich rozpoczyna się po filtrowaniu i weryfikacji danych użytkownika.
Ostatnim punktem jest wykrycie nieoczekiwanego zachowania, które wymaga większego wysiłku i złożoności; nie jest to zalecane w przypadku zwykłych aplikacji internetowych.
Nieoczekiwane zachowanie w powyższych danych wejściowych użytkownika to SELECT, UNION, IF, SUBSTRING, BENCHMARK, SHA i root. Po wykryciu tych słów możesz uniknąć wprowadzania danych.
AKTUALIZACJA 1:
Użytkownik skomentował, że ten post jest bezużyteczny, OK! Oto, co zapewnił OWASP.ORG :
Jak zapewne wiesz, roszczenie do artykułu powinno być poparte ważnym argumentem, przynajmniej jednym odniesieniem! W przeciwnym razie jest to uważane za atak i złe roszczenie!
Aktualizacja 2:
Z podręcznika PHP, PHP: Przygotowane instrukcje - Podręcznik :
Aktualizacja 3:
Utworzyłem przypadki testowe, aby dowiedzieć się, w jaki sposób PDO i MySQLi wysyłają zapytanie do serwera MySQL podczas korzystania z przygotowanej instrukcji:
ChNP:
Dziennik zapytań:
MySQLi:
Dziennik zapytań:
Oczywiste jest, że przygotowane oświadczenie ucieka również od danych, nic więcej.
Jak wspomniano również w powyższym oświadczeniu,
Dlatego dowodzi to, że sprawdzanie poprawności danych, takie jak,
intval()
jest dobrym pomysłem na wartości całkowite przed wysłaniem dowolnego zapytania. Ponadto zapobieganie złośliwym użytkownikom przed wysłaniem zapytania jest poprawnym i prawidłowym podejściem .Proszę zobaczyć to pytanie, aby uzyskać więcej szczegółów: PDO wysyła surowe zapytanie do MySQL, podczas gdy Mysqli wysyła przygotowane zapytanie, oba dają ten sam wynik
Bibliografia:
źródło
Używam trzech różnych sposobów, aby zapobiec podatności mojej aplikacji internetowej na wstrzykiwanie SQL.
mysql_real_escape_string()
, które jest z góry określone funkcje w PHP , a to code Dodaj backslashe do następujących znaków:\x00
,\n
,\r
,\
,'
,"
i\x1a
. Przekaż wartości wejściowe jako parametry, aby zminimalizować ryzyko wstrzyknięcia SQL.Mam nadzieję, że to Ci pomoże.
Rozważ następujące zapytanie:
$iId = mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";
mysql_real_escape_string () nie chroni tutaj. Jeśli użyjesz pojedynczych cudzysłowów ('') wokół zmiennych w zapytaniu, to cię przed tym chroni. Oto rozwiązanie tego problemu:
$iId = (int) mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";
To pytanie ma na to dobre odpowiedzi.
Sugeruję, że używanie PDO jest najlepszą opcją.
Edytować:
mysql_real_escape_string()
jest przestarzałe od PHP 5.5.0. Użyj mysqli lub PDO.Alternatywą dla mysql_real_escape_string () jest
Przykład:
źródło
Prostym sposobem byłoby użycie frameworka PHP, takiego jak CodeIgniter lub Laravel, który ma wbudowane funkcje, takie jak filtrowanie i aktywny zapis, dzięki czemu nie musisz się martwić o te niuanse.
źródło
Ostrzeżenie: podejście opisane w tej odpowiedzi dotyczy tylko bardzo specyficznych scenariuszy i nie jest bezpieczne, ponieważ ataki polegające na wstrzykiwaniu SQL polegają nie tylko na możliwości wstrzykiwania
X=Y
.Jeśli atakujący próbują włamać się do formularza za pomocą
$_GET
zmiennej PHP lub ciągu zapytania adresu URL, będziesz w stanie je złapać, jeśli nie będą bezpieczne.Ponieważ
1=1
,2=2
,1=2
,2=1
,1+1=2
, itp ... są wspólne pytania do bazy danych SQL atakującego. Być może jest także używany przez wiele aplikacji hakerskich.Musisz jednak uważać, aby nie przepisać bezpiecznego zapytania ze swojej witryny. Powyższy kod daje ci wskazówkę, aby przepisać lub przekierować (zależnie od ciebie) ten dynamiczny ciąg zapytania specyficzny dla hakowania na stronie, która będzie przechowywać adres IP osoby atakującej lub NAWET ICH PLIKI COOKIE, historię, przeglądarkę lub dowolną inną wrażliwą informacje, abyś mógł zająć się nimi później, blokując ich konto lub kontaktując się z władzami.
źródło
1-1=0
? :)([0-9\-]+)=([0-9]+)
.Istnieje wiele odpowiedzi na PHP i MySQL , ale oto kod dla PHP i Oracle, aby zapobiec wstrzykiwaniu SQL, a także regularnemu używaniu sterowników oci8:
źródło
Dobrym pomysłem jest użycie odwzorowania obiektowo-relacyjnego, takiego jak Idiorm :
Oszczędza to nie tylko zastrzyków SQL, ale również błędów składniowych! Obsługuje również kolekcje modeli z łączeniem metod w celu filtrowania lub stosowania działań do wielu wyników jednocześnie i wielu połączeń.
źródło
Używanie PDO i MYSQLi jest dobrą praktyką, aby zapobiec wstrzykiwaniu SQL, ale jeśli naprawdę chcesz pracować z funkcjami i zapytaniami MySQL, lepiej byłoby użyć
mysql_real_escape_string
Jest więcej możliwości, aby temu zapobiec: na przykład identyfikacja - jeśli dane wejściowe to ciąg, liczba, znak lub tablica, istnieje tak wiele wbudowanych funkcji do wykrywania tego. Lepiej byłoby również użyć tych funkcji do sprawdzenia danych wejściowych.
is_string
is_numeric
I o wiele lepiej jest używać tych funkcji do sprawdzania danych wejściowych
mysql_real_escape_string
.źródło
mysql_real_escape_string()
nie jest nieomylny .mysql_real_escape_string
jest teraz przestarzałe, więc nie jest już realną opcją. W przyszłości zostanie usunięty z PHP. Najlepiej przejść do tego, co polecają ludzie PHP lub MySQL.Kilka lat temu napisałem tę małą funkcję:
Pozwala to na uruchamianie instrukcji w jednowierszowym łańcuchu C # -ish. Formatowanie takie jak:
Ucieka, biorąc pod uwagę typ zmiennej. Jeśli spróbujesz sparametryzować nazwy tabel, kolumn, nie powiedzie się, ponieważ umieszcza każdy ciąg w cudzysłowach, co jest niepoprawną składnią.
AKTUALIZACJA BEZPIECZEŃSTWA: Poprzednia
str_replace
wersja pozwalała na zastrzyki, dodając {#} tokeny do danych użytkownika. Tapreg_replace_callback
wersja nie powoduje problemów, jeśli zamiennik zawiera te tokeny.źródło