eRIZ's weblog

PHP, webdesign, Linux, Windows i inne, bo nie samym chlebem człowiek żyje

Zabezpieczanie skryptów/serwera

Pozwoliłem sobie rozpocząć pewien cykl nt. różnych problemów, na które mogą się natknąć użytkownicy Apache’a (i to niekoniecznie administratorzy). Tym razem skupię się na podstawowym zabezpieczaniu stron serwowanych przez Indianina.

Zwiększanie poziomu bezpieczeństwa stron można podzielić w zasadzie na kilka części. Wszak nie jest to tylko zadanie administratora naszego serwera, lecz również nasze, osób tworzących aplikacje „webowe”. Nie chodzi tylko o zapobieżenie destabilizacji działania serwisu, lecz również o ochronę danych naszych gości, co nakazuje nam ustawa o ochronie danych osobowych.

Zaczniemy od samej konfiguracji serwera. W zależności od tego, czy jesteśmy administratorami serwera czy też tylko użytkownikami będziemy mogli dokonać większych bądź mniejszych zmian.

Czy koniecznie Apache?

Tytuł trochę paradoksalny, ale… Wbrew pozorom, Apache nie jest jedynym serwerem. Dostępnych jest wiele innych programów służących do serwowania stron. Dosyć prężnie rozwijającą się alternatywą jest LightTPD. Jest dużo szybszy niż Indianin (zwłaszcza w przypadku generowania stron przez PHP) i na razie, nie wykryto w nim wielu dziur. Jednak posiada on kilka wad; największą chyba jest brak obsługi plików .htaccess.

Serwer

Zacznijmy od modułów. Po domyślnej instalacji serwera ładowanych jest wiele modułów, nie zawsze potrzebnych. Pamiętaj: KAŻDY ZAŁADOWANY MODUŁ, TO POTENCJALNE RYZYKO OTWARCIA KOLEJNEJ DZIURY W ZABEZPIECZENIACH SERWERA, więc korzystajmy tylko z tych, które są nam naprawdę potrzebne.

Należy również pamiętać o uruchamianiu usługi serwera na specjalnym, ograniczonym koncie z odpowiednio ustawionymi uprawnieniami. Absolutnie NIEDOPUSZCZALNE jest uruchamianie demona serwera na koncie roota/z uprawnieniami administracyjnymi! Otwiera to drogę do przejęcia kontroli nad systemem za pośrednictwem procesu serwera w przypadku wykorzystania luki. IMHO, najlepszym wyjściem jest skorzystanie z modułu suPHP.

Proces serwera musi mieć również nałożone ograniczenia na poziomie systemu plików. Umożliwiamy mu dostęp TYLKO do plików, na których może operować. W systemach Uniksowych nie stanowi to większego problemu, ponieważ od początków ich istnienia duży nacisk kładzie się na zabezpieczenia na poziomie systemu plików i samego systemu. W systemach Windows jest już dużo gorzej (swoją drogą, stawianie serwera na Oknach uważam za co najmniej poroniony pomysł). Jednak, jeśli ktoś się upiera, to niech przynajmniej sformatuje partycję z danymi serwera pod NTFS i niech to nie będzie domyślny katalog (C:\program files\apache group\apache2.2\htdocs).

Interpretery języków skryptowych również powinny mieć nałożone ograniczenia, np. zablokowane wykonywanie poleceń systemowych czy też limity wykorzystywanej pamięci. Limitowanie czasu wykonywania skryptu nie jest już tak bardzo priorytetowe, ale jeśli mamy wyjątkowo chamskich użytkowników, którzy chcą sprawdzić jak zabezpieczony jest serwer bądź żerują na jego zasobach, to ostatecznie można narzucić jakiś limit czasowy. Niekoniecznie dla wszystkich (albo w ogóle: patrz suPHP, wyżej; wówczas „prawomocna” jest konfiguracja dla konkretnego użytkownika). Można również precyzować indywidualną konfigurację i zmniejszyć mu dostępne zasoby pamięciowo-czasowe nie narażając na utrudnienia innych użytkowników. Jest to jednak bardzo obszerny temat, zapraszam więc do lektury manuala Apache’a.

W przypadku samych interpreterów mamy również do wyboru dwa tryby pracy: jako moduł serwera i w trybie CGI. Różnice: wydajność i bezpieczeństwo. O ile w trybie modułu dokumenty są generowane szybciej niż w przypadku wykorzystywania (Fast)CGI oraz możemy skorzystać z kilku dodatkowych funkcji, to jeśli chodzi o bezpieczeństwo – sprawa zmienia się dosyć radykalnie. (Fast)CGI do przeparsowania uruchamia osobny proces, który możemy z łatwością zabić, w przeciwieństwie do generowania w trybie modułu ładowanego przez serwer. Wówczas, spreparowany dokument może spowodować niestabilność serwera/inne niepożądane skutki, co w konsekwencji zmusi do zrestartowania całego demona.

Włącz logi dla serwera i od czasu do czasu je przeglądaj. Nie dość, że pomagają w wykrywaniu usterek, to często są w stanie wykazać próby włamów. Chyba nie muszę wspominać, że w przypadku nadużyć są jednym z głównych materiałów dowodowych.

Trzeba się jeszcze odnieść do konfiguracji interpreterów. Najwięcej mam do czynienia z PHP, więc do niego się ustosunkuje. Co powinniśmy zrobic? Wyłączyć register_globals. Wiele osób zawodowo projektujących aplikacje sieciowe twierdzi, iż jest to bez znaczenia. Owszem. W ich przypadku. Ale zapominają o osobach, które kupują/załatwiają sobie konto na serwerze tylko po to, aby odpalać jakieś proste skrypty. Ci klienci nie mają zazwyczaj zielonego pojęcia o np. singletonie, a może być przyczyną problemów (z bezpieczeństwem, rzecz jasna)…

iptables Twoim przyjacielem. Na Świat otwieraj tylko niezbędne porty. Np. dostęp do SQL-a niech ma wyłącznie serwer. Czasem jednak może taka opcja być przydatna, ale to tylko przykład. Zezwól na dostęp z zewnątrz tylko przez porty 80, 21, itp. PS. Pingi blokuj (nie tylko z powodu PoD, ale również przed ping-floodem). Wręcz idealny byłby sprzętowy firewall.

Dbaj o najnowsze wersje pakietów. Nic nie jest doskonałe. Szczególnie to tyczy się oprogramowania. Pilnuj, aby paczki były w najnowszych dostępnych wersjach, a jeśli to możliwe – włącz automatyczne aktualizowanie składników systemu. Załatany kernel też jest ważny! Jeśli system nie będzie odpowiednio zabezpieczony, nie ma o czym dalej mówić…

Staraj się być na bieżąco z odkrytymi lukami w zabezpieczeniach. Polecam regularnie odwiedzać serwisy zajmujące się bezpieczeństwem aplikacji, np. Secunia lub Security Vulnerabilities. Plusem tych serwisów są kanały RSS, które pozwolą na znaczne skrócenie ewentualnej reakcji. Pamiętaj, że z tych serwisów odwiedzają również włamywacze, którzy liczą na opieszałość ludzi takich jak Ty – administratorów.

Nie bagatelizuj ryzyka włamania! Każdy serwer, komputer podłączony do Internetu, to potencjalny cel hakera/crackera. Nie ma tu znaczenia, czy jest to wielki klaster składający się z kilkuset serwerów czy jeden mały „piecyk”. Bądź przygotowany na to, że w każdej chwili (nawet teraz) ktoś może się włamać i wyciągnąć/zniszczyć wszystkie dane.

Dobre hasło, to podstawa. paweł123, dzioenek412. Czy Twoje hasła nie są podobne? Jak tak, to coś Ci powiem: SĄ BEZNADZIEJNE! Ich złamanie, to tylko kwestia czasu. Kiedy ostatni raz zmieniałeś(aś) swoje hasło? Wczoraj czy pół roku temu? A może wszędzie jest ono takie samo? Duża część wtargnięć do systemów komputerowych (o ile nie większość) spowodowana jest używaniem kiepskich haseł. Jakie powinno być? Jest wiele recept. Osobiście, sugeruję długie, losowo wygenerowane hasła, takie jak: oxs*ine!I2oWK2epQ0y+*gso@i)x!:aZeqDE7f@(.Kbr&0Gn[?UM. Takie hasło pozwoli nam spać odrobinę spokojniej. Samo hasło jednak nie wystarczy – ważna jest również polityka stosowania – unikaj tego samego ciągu znaków w kilku usługach/kontach oraz regularnie zmieniaj. Sugerowałbym używanie tylko znaków, które da się wpisać z klawiatury. Owszem, spadnie bezpieczeństwo. Ale co by po nim było, gdyby nie dało się go w ogóle wprowadzić? (mam tu na myśli kodowanie znaków w plikach, itp.).

O blokowaniu funkcji już wspomniałem.

Co może zrobić webmaster?

Niektóre pliki nie powinny być w ogóle widoczne dla wścibskich użytkowników. Najprostszy sposób, to trzymanie ich poza katalogiem public_html. Wtedy tylko źle napisany skrypt bądź włamanie na serwer będzie stanowiło problem. ;)

Czasem nie mamy takiej możliwości, co się rzadko zdarza, aczkolwiek nie można tego aspektu pominąć. Najprostszym wyjściem jest ustawienie CHMOD-ów w taki sposób, aby odebrać dostęp wszystkim prócz właściciela pliku (700).

Powyższa metoda ma jednak wadę – nie będzie możliwości odczytania takich plików przez serwer czy PHP. Wtedy tworzymy plik .htaccess:

order deny, allow
deny from all

Można również zablokować jedynie możliwość wyświetlenia zawartości katalogów. Tworzymy .htaccess:

Options -Indexes

Umieszczamy go w folderze, w którym chcemy zablokować listingi. Uwaga: ustawienia działają rekurencyjnie – wszystkie podkatalogi będą miały takie same ustawienia jak nadrzędny, z plikiem .htaccess.

Użytkownik to cracker! Nieważne, kto nim jest! Tak jak w prowadzeniu samochodu, tu też ma zastosowanie zasada ograniczonego zaufania: sprawdzaj WSZYSTKO, ale to WSZYSTKO, co pochodzi od użytkownika – „gość” może podmienić praktycznie wszystko, co wysyła do serwera (zapytania, itp.). Uczulam tu na wszelkie parametry przekazywane przez pasek adresu i w formularzach. Zabezpieczaj również swoje skrypty przed kilku (co najmniej) krotnym wykonaniem; czasem jedno „odśwież” gościa za dużo i serwis stoi…

Dlaczego filtrowanie danych jest takie ważne? XSS, SQL-Injection – jak często o tym słyszymy… Przykład:

$q = mysql_query('SELECT * FROM users WHERE ID='.$_GET['ID']);

I co wtedy? No… niby nic. Tak? To popatrz: wchodzimy sobie na stronę, która zawiera taki ciąg w pasku adresu:

http://bleble.pl/profil?user=1;DROP TABLE users

I co? Ano zostanie zamienione w to:

$q = mysql_query('SELECT * FROM users WHERE ID=1; DROP TABLE users');

Skutki: jedna tabela w bazie mniej… Co wystarczy? Pilnowanie, co wpisujemy do treści zapytań. Dla MySQL powstała funkcja mysql_real_escape_string. Na tej stronie manuala PHP znajduje się również kilka życiowych przykładów wykorzystania.

Sposób wprowadzania danych również ma znaczenie. Często spotykanym sposobem zestawiania połączenia z bazą jest:

$baza_user = 'user';
$baza_haslo = 'abcdefh123';
mysql_connect('localhost', $baza_user, $baza_haslo);

OK, niby nie ma nic w tym złego, ale nie wywołuj wilka z lasu. Zobacz przypadek, jeśli dojdzie do małego sabotażu, w którym ktoś dopisze jakiegokolwiek dalszego pliku:

if($_GET['demaskuj']){
echo 'user bazy: '.$baza_user.' haslo: '.$baza_haslo;
}

Jak można by temu zaradzić? Przede wszystkim, nie dopuszczać. Oraz…

mysql_connect('localhost', 'user', 'abcdefh123');

Istnieją wtedy trzy, cztery możliwości przechwycenia

  1. Wprowadzenie do systemu serwera jakiegoś exploita umożliwiającego dowolne hasanie po pamięci.
  2. Ingerencja w sam kod parsera.
  3. Podejrzenie pliku odpowiedzialnego za zestawianie połączenia.
  4. (Nie)świadome wypisanie danych na stronie przez samego autora serwisu :D (paradoksalnie, całkiem prawdopodobna opcja).

Zmień folder, w którym przechowywane są pliki z danymi sesji. Domyślnie, jest to systemowy folder tymczasowy (/tmp, itp.). Użyj ini_set i zmień wartość session.save_path. Każdy użytkownik serwera ma dostęp do tego katalogu, co może spowodować opłakane skutki (chociażby pobranie listy plików – odkrycie identyfikatora sesji).

Parę razy przewinął się problem podejrzenia plików. Można zakodować pliki z kodem źródłowym. Służy do tego np. ionCube Encoder czy Zend Encoder (Zend Guard). Zamieniają one kod źródłowy na postać zupełnie nie czytelną nawet dla guru-programistów. Niestety, z tego co mi wiadomo, to prekompilację umożliwiają wyłącznie pakiety komercyjne. Byłoby miło, gdyby ktoś, kto zna jakiś darmowy odpowiednik, podał jego nazwę. :)

Oczywiście, podział, który zastosowałem miał na celu wyłącznie pogrupowanie danych podkategorii. Nie oznacza to, iż nie zachodzi korelacja pomiędzy zastosowaniami/problematyką.

8 komentarzy

dopisz swój :: trackback :: RSS z komentarzami

RSS z komentarzami :: trackback

Skomentuj

Możesz używać znaczników XHTML. Dozwolone są następujące tagi: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre lang="" line="" escaped="" cssfile="">

Wszystkie komentarze przechodzą przez moderację oraz filtry antyspamowe. Nie zostanie opublikowany komentarz, jeśli:

  • Jego treść obraża kogokolwiek.
  • W treści znajdują się wulgaryzmy i słownictwo ogólnie uznane za nieprzyzwoite.
  • Mam wątpliwości co do autora wpisu (Wszelkie anonimy są kasowane - niezależnie od zawartości - wpisz prawdziwy e-mail. Jeśli usunąłem, Twoim zdaniem, komentarz niesłusznie - daj znać). Zdarza się, iż sprawdzam kim jest komentujący.
  • Zawiera jakąkolwiek formę reklamy.

Warning: Undefined variable $user_ID in /usr/home/er1zpl/domains/eriz.pcinside.pl/public_html/weblog/wp-content/themes/inBlueDiary/comments.php on line 112

Szufladka