zapobieganie zastrzykom SQL w PHP (i innym lukom)
jeśli od jakiegoś czasu zajmujesz się tworzeniem stron internetowych, prawie na pewno słyszałeś termin „SQL injection” i przerażające historie na ten temat.
PHP, podobnie jak wiele innych języków, nie jest odporne na tego typu zagrożenia, które mogą być bardzo niebezpieczne. Ale na szczęście Ochrona witryn przed SQL injection i innymi podobnymi zagrożeniami jest czymś, w czym możesz podjąć konkretne kroki.
w tym poście dowiesz się, czym jest SQL injection, jakie są konsekwencje udanego ataku, w jaki sposób atakujący może wykorzystać podatny kod PHP, co możesz zrobić, aby temu zapobiec i jakich narzędzi możesz użyć do wykrycia części kodu, które mogą być przedmiotem tego rodzaju zagrożenia. Ponadto dowiesz się o kilku innych typowych lukach, o których powinieneś wiedzieć, aby tworzyć bezpieczniejsze aplikacje.
jak działa SQL injection?
pierwszą rzeczą, którą musisz wiedzieć, aby chronić swój kod przed SQL injection, jest zrozumienie, w jaki sposób może on być wykorzystany przez atakującego.
idea exploita jest dość prosta: atakujący uruchamia złośliwy kod SQL w bazie danych za pośrednictwem aplikacji.
jak można to osiągnąć? Nadużywając danych wejściowych Twojej aplikacji.
spójrzmy na prosty przykład. Załóżmy, że masz aplikację pokazującą listę nazw użytkowników, gdzie każdy użytkownik ma link do swoich danych w ten sposób:
a następnie w pliku user_details.php
masz to:
<?php $sql = "SELECT * FROM user WHERE id = ".$_GET;
jeśli użytkownik działa zgodnie z oczekiwaniami i kliknie nazwę Użytkownika, adres URL żądania będzie wyglądał następująco: http://domain.com/user_details.php?id=8
.
co w tym złego?
wartością $sql
będzie łańcuch "SELECT * FROM user WHERE id = 8"
, a zapytanie będzie działać poprawnie.
problem tkwi w pochylonej części tego zdania: jeśli użytkownik działa zgodnie z oczekiwaniami i kliknie nazwę Użytkownika, adres URL żądania będzie wyglądał tak:
http://domain.com/user_details.php?id=8
.
co jeśli użytkownik nie działa zgodnie z oczekiwaniami?
co by było, gdyby ktoś wytworzył URL wysyłający coś innego niż numer jako ID?
wytworzone adresy URL
jeśli adres URL byłby podobny do http://domain.com/user_details.php?id=Peanuts
, wynikowy ciąg SQL byłby następujący: "SELECT * FROM user WHERE id = Peanuts"
.
w tym przypadku problem byłby dla użytkownika. Zapytanie po prostu zawiedzie, a w najgorszym przypadku użytkownik zobaczy jakąś dziwną wiadomość.
ale co jeśli URL wyglądał bardziej tak:
http://domain.com/user_details.php?id=1+OR+1%3B
? (Jest to wynik urlencode('1 OR 1;')
, nawiasem mówiąc.)
w tym przypadku wynikowy SQL byłby "SELECT * FROM user WHERE id = 1 OR 1;"
. Oznacza to, że wynikiem zapytania będzie każdy użytkownik w bazie danych, a nie tylko użytkownik, którego ID jest równe 1. AUĆ.
ale i tak nie jest tak źle, prawda?
spróbujmy teraz bardziej ekstremalnego przykładu.
Załóżmy, że URL był czymś w rodzaju
http://domain.com/user_details.php?id=1%27%3B+DROP+TABLE+users%3B--+
, co przekłada się na id=1; DROP TABLE users;--
, efektywnie generując to polecenie SQL:
"SELECT * FROM users WHERE id = 1; DROP TABLE users;-- "
. I jeśli uruchomisz to zapytanie…wielkie ouch.
w przypadku, gdy nie jesteś w 100% zaznajomiony ze składnią SQL, DROP TABLE
całkowicie usunie tabelę. Jakby nigdy nie istniał.
oczywiście niedawna kopia zapasowa pomogłaby zmniejszyć obrażenia, ale mimo to. Jest źle.
jeśli chcesz dowiedzieć się więcej o tego rodzaju ataku, jest świetny post tutaj.
ale to chyba wystarczy. Przejdźmy do bardziej wesołego miejsca: jak można zapobiec tego typu zastrzykom SQL w aplikacjach PHP.
jak zapobiec zastrzykom SQL w PHP
jak widziałeś, problem polega na tym, że atakujący ma możliwość uruchomienia kodu SQL bez Twojej wiedzy o tym.
ta sytuacja przedstawia się jako konsekwencja tworzenia zapytań w locie za pomocą konkatenacji łańcuchów i uwzględniania danych zebranych z zewnętrznych wejść (Zwykle wejście użytkownika, ale także inne zewnętrzne źródła, takie jak pliki, odpowiedzi na wywołania API itp.).
Tak więc, aby zapobiec tej luce w kodzie, musisz naprawić źródła potencjalnych problemów.
sprawdzanie poprawności danych wejściowych
prostym sposobem na naprawienie luki w powyższym przykładzie byłoby sprawdzenie, czy parametr ID jest tym, czego się od niego oczekuje (np., dodatnia liczba całkowita):
innym sposobem na tego rodzaju walidację jest wykorzystanie wbudowanych filtrów PHP:
<?php
$id = filter_input( INPUT_GET, 'id', FILTER_VALIDATE_INT);
weryfikując dane wejściowe, uniemożliwiasz atakującym wykonywanie złośliwego kodu obok Twojego.
nie ma łatwego sposobu, aby zapobiec próbom, ale tak długo, jak ich próby nie powiodą się, możesz iść.
użyj prepared statements
innym sposobem ochrony kodu przed wstrzyknięciami SQL jest użycie prepared statements. Przygotowane instrukcje są prekompilowanymi poleceniami SQL.
mogą być używane z konkretną biblioteką dostępu do bazy danych (taką jak mysqli) lub z bardziej generyczną biblioteką PDO.
spójrzmy na przykład używając mysqli:
jeśli spróbujesz tego kodu (oczywiście zastępując poświadczenia bazy danych), zobaczysz, że jeśli uzyskasz dostęp za pomocą legalnego adresu URL, takiego jak http://domain.com/user_details.php?id=1
, otrzymasz ten sam wynik, co którykolwiek ze złośliwych adresów URL powyżej (czyli informacje o użytkowniku powiązanym z ID 1 i nic więcej).
jeśli wolisz używać PDO, kod będzie wyglądał mniej więcej tak:
nie ma dużej różnicy, prawda?
podsumowując, przygotowane instrukcje oferują świetny sposób na zapobieganie zastrzykom SQL. Ponadto zwykle działają lepiej niż on-the-fly SQL.
teraz, gdy znasz narzędzie, po prostu musisz go użyć.
jak wykryć kod PHP podatny na zastrzyki SQL
po wykryciu luki, naprawienie jej nie jest tak naprawdę skomplikowane. Ale jak znaleźć tego typu luki w kodzie w pierwszej kolejności?
cóż, jeśli masz szczęście i twój kod jest wystarczająco mały, wystarczy prosty przegląd kodu.
ale jeśli Twoja aplikacja jest średniej wielkości do dużej, będziesz potrzebował dodatkowej pomocy.
jednym z bardzo popularnych (i open-source) narzędzi, których możesz użyć do tego celu, jest sqlmap, prosty skrypt Pythona, który spróbuje zaatakować dowolny adres URL, który do niego podasz i zgłosi jego wyniki.
oto przykładowy wynik z uruchomienia mojej strony opartej na PHP:
innym narzędziem open-source, którego możesz użyć, jest arachni, bardziej wyrafinowane narzędzie, które zawiera web GUI i zostało napisane w Ruby.
oto Przykładowe wyjście z uruchomienia go przez CLI:
oprócz przeglądu i testowania kodu, należy rozważyć użycie rozwiązania zgrzyt, takiego jak Sqreen, do monitorowania środowisk produkcyjnych, aby zatrzymać wykonywanie wszelkich exploitów, które przechodzą przez etapy przeglądu i testowania.
jakie inne luki powinieneś wiedzieć?
zastrzyki kodu
SQL injection to tylko jeden członek większej rodziny luk: code injection.
idea różnych technik wtrysku kodu jest zawsze taka sama. Chodzi o oszukanie niewinnej aplikacji do uruchamiania złośliwego kodu.
można to zrobić na kilka sposobów.
w PHP istnieją funkcje zaprojektowane po prostu do wydawania poleceń bezpośrednio do systemu operacyjnego, takie jak funkcja exec
.
Oto prosty przykład jak można to wykorzystać:
<?php
exec( 'cp uploads/'.$_GET.' /secure/');
gdyby ktoś zażądał http://domain.com/exec.php?file=a.txt%3B+rm+-Rf+%2A+%23
, wynikiem byłoby wykonanie tego polecenia:
cp uploads/a.txt; rm -Rf * # /secure/
i to może wymazać cały dysk na serwerze-niezaliczone wejście uderza ponownie!
Cross-Site scripting
inną bardzo częstą luką jest cross-site scripting (XSS). W takim przypadku ofiarą jest Użytkownik odwiedzający Twoją witrynę.
XSS jest wtedy, gdy atakujący wstrzykuje złośliwy kod JavaScript do zwykłego formularza HTML, który będzie później renderowany przez przeglądarkę innego użytkownika.
spójrz na ten skrypt PHP:
<?php
echo "<html><p>Hello {$_GET}!</p></html>";
oczywiście intencją jest tutaj stworzenie prostej strony, która wita użytkownika.
ale kiedy do niego dotrą, to co im przeszkadza w żądaniu
http://domain.com/greet.php?name=Mauro%3Cscript+language%3D%22javascript%22%3Ealert%28%22You+are+hacked%21%22%29%3B%3C%2Fscript%3E
? To żądanie wygeneruje następujący kod HTML jako wyjście:
<html><p>Hello Mauro<script language="javascript">alert("You are hacked!");</script></p></html>
jest to oczywiście bardzo naiwny exploit-w końcu, co jest złego w pokazaniu komuś prostego ” jesteś zhakowany!”wiadomość? Ale przy odrobinie wyobraźni sprawy mogą być poważne. Pamiętaj tylko, że każde żądanie wysłane do serwera zawiera plik cookie sesji, więc ta luka może być winowajcą porwania sesji Twoich użytkowników.
z drugiej strony, podczas gdy problemy wyglądają brzydko, poprawki są naprawdę proste: chodzi o walidację i dezynfekcję.
w przypadku XSS wystarczy proste wywołanie htmlentities
przed wygenerowaniem rzeczywistego wyjścia.
naprawianie a Wykrywanie luk w zabezpieczeniach
jak widzieliście w tym poście, naprawianie luk w zabezpieczeniach-zarówno SQL Injection, jak i innych typów—nie jest głównym wyzwaniem. Zdając sobie sprawę, że Twój kod jest podatny na zagrożenia i gdzie można go wykorzystać, jest. Masz kilka opcji tutaj:
- miej bardzo silną dyscyplinę kodowania
- dokładnie Przetestuj swoje aplikacje pod kątem problemów z bezpieczeństwem
- wykorzystaj narzędzia monitorowania i ochrony, aby zapobiegać exploitom i otrzymywać powiadomienia w odpowiednim czasie
który z nich wybierzesz, zależy od Ciebie. Ważne jest, aby zwracać uwagę na te problemy i działać na tej wiedzy. Uważaj na siebie.