Utwardzanie środowiska Apache + PHP + MySQL (na CentOS 6).

Zadanie

Podniesienie bezpieczeństwa serwera Apache HTTP oraz podstawowych dodatkowych komponentów używanych do serwowania dynamicznych stron internetowych.

Założenia

Serwer Apache oraz PHP i MySQL zainstalowane ze sztandarowego repozytorium na dystrybucji CentOS 6. W chwili pisania tej notatki, w CentOS 6 dostarczane były następujące wersje wspomnianych komponentów:

Komponent Wersja
Apache HTTP: 2.2.15
PHP: 5.3.3
MySQL: 5.1.73

Do instalacji niektórych, dodatowych pakietów podnoszących bezpieczeństowo użyjemy repozytorium EPEL (https://fedoraproject.org/wiki/EPEL). Opis konfiguracji tego repozytorium znajduje się poniżej.

Procedura

1. Dodanie do systemu repozytorium EPEL.

Źródło: https://fedoraproject.org/wiki/EPEL

2. Wyłączenie listowania katalogów w Apache HTTP.

Np. w pliku /etc/httpd/conf/httpd.conf w kontekście dyrektywy Directory, stawiamy znak “-” przed opcją Indexes:

Źródło: http://httpd.apache.org/docs/current/mod/core.html#options

3. Wyłączenie metody TRACE w serwerze Apache.

W pliku /etc/httpd/conf/httpd.conf wyłączamy metodę TRACE przez ustawienie dyrektywy TraceEnable na Off:

Źródło:  http://httpd.apache.org/docs/current/mod/core.html#traceenable

 4. Ograniczenie ilości ujawnianej informacji o środowisku serwera.

W pliku /etc/httpd/conf/httpd.conf ustawiamy dyrektywę ServerTokens na wartość Prod, oraz dyrektywę ServerSignature na Off:

Źródło: http://httpd.apache.org/docs/current/mod/core.html#servertokens, http://httpd.apache.org/docs/current/mod/core.html#serversignature

5.  Ograniczenie skuteczności ataków typu SYN flood.

Do ograniczenia skutków ataku SYN flood w systemie Linux można użyć mechanizmu SYN Cookies. Domyślnie, w dystrybucji CentOS 6, powinien on być włączony. Jest to mechanizm zaimplementowany na poziomie jądra, dlatego jego uaktywnienia dokonuje się  z poziomu pliku /etc/sysctl.conf.

Po ewentualnych zmianach w powyższym pliku, w celu ich aktywowania, należy wydać polecenie sysctl -p

Źródło: http://wiki.centos.org/HowTos/OS_Protection#head-058054661891c5655b9f9c9dbb42183d976df01b, http://pl.wikipedia.org/wiki/SYN_cookies

6. Blokada możliwości wołania “wrażliwych” funkcji PHP.

W pliku /etc/php.ini, przez parametr disable_functions, należy wyłączyć możliwość uruchamiania potencjalnie niebezpiecznych funkcji. W każdym przypadku lista zablokowanych funkcji może być inna. Przykładowy zbiór zablokowanych funkcji może wyglądać tak:

Źródło: www.php.net/ini.core

 7. Wyłączenie w PHP możliwości otwierania i załączania plików przy pomocy URL-i.

Źródło: http://php.net/manual/en/filesystem.configuration.php

8. Komunikaty z opisem błędów PHP nie mogą być wyświetlane w przeglądarce.

Komunikaty o błędach mogą ujawniać wrażliwe informacje, dlatego komunikaty tego typu zamiast być wypisywane w przeglądarce, powinny trafiać do logu. W pliku /etc/php.ini ustawiamy parametry display_errors i log_errors.

Źródło: http://php.net/manual/en/errorfunc.configuration.php, http://php.net/manual/en/ref.errorfunc.php

 9. Wyłączenie możliwości ładowania rozszerzeń PHP na żądanie.

W pliku /etc/php.ini ustawiamy parametr enable_dl na Off.

Źródło: http://php.net/manual/en/info.configuration.php, http://php.net/manual/en/function.dl.php

10. Ograniczenie dostępu z poziomu PHP do określonej gałęzi systemu plików.

W pliku /etc/php.ini, poprzez opcję open_basedir, ograniczamy dostęp do wszystkich operacji na plikach do wskazanej lokalizacji i pod-lokalizacji.

UWAGA: Takie ustawienie parametru open_basedir wymaga dostosowania parametru upload_tmp_dir, który określa katalog tymczasowy do którego trafiają pliki wgrywane na serwer. Trzeba zadbać o to żeby wartość tego parametru wskazywała na ścieżkę “zawartą” w gałęzi wskazanej przez open_basedir. Dla powyższego przykładu można założyć, że katalog tymczasowy dla wgrywanych plików zostanie umiejscowiony w ścieżce /var/www/html/tmp. Utworzenie i odpowiednie skonfigurowanie takiego katalogu może wyglądać tak:

A następnie w pliku /etc/php.ini należy odpowiednio ustawić parametr upload_tmp_dir:

 11. Ustawienie maksymalnego rozmiaru żądania HTTP dla serwera Apache.

W pliku /etc/httpd/conf/httpd.conf ustawiamy dyrektywę LimitRequestBody. Należy pamiętać, że maksymalny rozmiar żądania przekłada się również na maksymalny rozmiar plików jakie będzie można wgrać na serwer przy pomocy protokołu HTTP.

12. Ustawienie limitu na maksymalny rozmiar wgrywanego pliku (dotyczy php).

Maksymalny rozmiar pliku jaki można wgrać na serwer Apache przez PHP ustawia się w pliku /etc/php.ini, przez parametr upload_max_filesize.  Przykładowe ustawienie limitu na 1MB wygląda tak:

13. Ustawienie limitu na maksymalną ilość pamięci przeznaczoną do obsługi pojedynczego żądania (dotyczy php).

Do ustawienia limitu na maksymalny rozmiar pamięci przydzielony do obsługi pojedynczego żądania służy parametr memory_limit z pliku /etc/php.ini. W prezentowanym przykładzie, z komentarza widniejącego nad samym parametrem, widać że domyślna wartość limitu wynosiła aż 128MB. Nowo ustawiona wartość wynosi 16MB.

14. Instalacja i konfiguracja ModSecurity

ModSecurity to moduł serwera Apache podnoszący jego bezpieczeństwo głównie przez wprowadzenie mechanizmu detekcji i blokowania znanych zagrożeń. Przy pomocy bazy wyrażeń regularnych, w każdym żądaniu i odpowiedzi HTTP szukane są wzorce uważane za niebezpieczne. Dopasowanie do wzorca najczęściej kończy się wysłaniem do klienta odmowy dostępu. Zakładając, że zgodnie z opisem z pierwszego punktu, w systemie zostało zainstalowane repozytorium EPEL, instalacja modułu mod_security i bazy wzorców definiujących znane zagrożenia sprowadza się do prostych poleceń:

Zainstalowany moduł i reguły z wzorcami zagrożeń, zaczną działać po restarcie serwera Apache.

Reguły z wzorcami zagrożeń, zainstalowane z pakietu mod_security_crs, są niezwykle restrykcyjne. Zbiór reguł jest bardzo duży, i obejmuje wiele wzorców jedynie potencjalnie niebezpiecznych. W praktyce oznacza to, że  prawie na pewno, bezpośrednio po uruchomieniu modułu ModSecurity, witryna którą właśnie zabezpieczamy, zacznie być blokowana przez “przesadnie podejrzliwe” reguły z wzorcami zagrożeń. Dlatego, konieczne będzie znalezienie i wyłączenie reguł blokujących naszą stronę.

Za każdym razem, gdy ModSecurity zablokuje dostęp do witryny, to zostawia on stosowną informację w pliku logu. Domyślnie jest to plik /var/log/httpd/modsec_audit.log. Wpis ten zawiera dokładną informację identyfikującą regułę, która spowodowała zablokowanie żądania (zapisane zostają m.in. ścieżka i numer linii w pliku w którym jest zdefiniowana reguła oraz identyfikator reguły i krótki komentarz charakteryzujący dopasowaną regułę).  Informacje te, w szczególności identyfikator reguły, pozwalają na deklaratywne usunięcie reguł blokujących naszą witrynę. Do deklaratywnego usunięcia reguły służy dyrektywa SecRuleRemoveById. Argumentem tej dyrektywy jest identyfikator usuwanej reguły. Dyrektywy tej można użyć np. w pliku /etc/httpd/conf.d/mod_security.conf, na końcu bloku zamkniętego wewnątrz dyrektywy <IfModule mod_security2.c>. W takim przypadku, dyrektywa jest wyłączana globalnie  dla całego serwera Apache.  Umieszczając dyrektywę SecRuleRemoveById wewnątrz bloku określonego dyrektywami Location lub LocationMatch, można wyłączać poszczególne reguły w kontekście poszczególnych lokalizacji (przykład: http://www.binarymoon.co.uk/2011/05/wordpress-mod-security-2/). Należy pamiętać, że po wprowadzeniu zmian w plikach konfiguracyjnych, trzeba przeładować serwer Apache.

Domyślnie, odmowa dostępu następuje po dopasowaniu pierwszego pasującego wzorca. W praktyce, na etapie dostrajania konfiguracji do działania z naszą witryną, jest to dość niewygodne bo oznacza, że każdą blokującą regułę trzeba odblokowywać osobno.  Dlatego, w celu przyśpieszenia całej procedury, w pliku /etc/httpd/conf.d/mod_security.conf należy tymczasowo zmienić wartość dyrektywy SecRuleEngine z On na DetectionOnly.

Od tej chwili, ModSecurity nie blokuje dostępu do witryny, ale zapisuje w logu informację o wszystkich dopasowanych regułach, które normalnie spowodowały by blokadę dostępu.

Przykładowy fragment logu /var/log/httpd/modsec_audit.log z informacją o dopasowaniu do wzorca wygląda tak:

Z powyższego fragmentu logu można wyczytać, że dopasowana reguła znajduje się w pliku /etc/httpd/modsecurity.d/activated_rules /modsecurity_crs_21_protocol_anomalies.conf w linii 47 i jej identyfikator to 9600015.

Znając identyfikator reguły można ją wyłączyć przy pomocy dyrektywy SecRuleRemoveById umieszczonej np. w pliku /etc/httpd/conf.d/mod_security.conf na końcu bloku określonego przez  <IfModule mod_security2.c>. Przykładowe wyłączenie reguły odpowiadającej powyższemu fragmentowi z pliku logu wygląda tak:

Po wprowadzeniu takiego wpisu w pliku konfiguracyjnym i po przeładowaniu Apache, reguła o identyfikatorze 9600015 jest wyłączona.

Po wyłączeniu wszystkich konfliktowych reguł, należy ponownie uaktywnić blokowanie witryny w przypadku dopasowania wzorca zagrożenia (w pliku /etc/httpd/conf.d/mod_security.conf ustawiamy parametr SecRuleEngine na On).

ModSecurity, poza zbiorem wzorców znanych zagrożeń, pozwala na ustawienie paru bardziej ogólnych parametrów (np. maksymalny rozmiar żądania czy odpowiedzi). Te dodatkowe parametry ustawia się w pliku /etc/httpd/conf.d/mod_security.conf, a ich nazwy w większości przypadków wydają się być na tyle  “samo-opisujące”, że dość łatwo można się domyśleć ich przeznaczenia.

Źródło: https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual

15. Instalacja i konfiguracja mod_evasive

Moduł mod_evasive służy do ograniczania skutków ataków typu DOS i DDOS. Dla dystrybucji CentOS 6 jest on dostępny poprzez, skonfigurowane powyżej, repozytorium EPEL. Instalacja sprowadza się do wydania komendy:

Parametry konfiguracyjne dla tego modułu znajdują się w pliku /etc/httpd/conf.d/mod_evasive.conf, i są opatrzone dość dokładnymi opisami (w formie komentarzy). Domyślne wartości parametrów wydają być rozsądne i w większości przypadków można je pozostawić bez modyfikacji. Moduł zostanie załadowany po przeładowaniu Apache.

16. Ograniczenie interfejsów na których nasłuchuje MySQL

Jeśli pracujemy w środowisku, gdzie wiadomo, że serwer Apache i serwer MySQL zawsze będą uruchamiane na tym samym węźle, to interfejsy, na których MySQL nasłuchuje na połączenia, można ograniczyć do interfejsu lokalnego. W tym celu w pliku /etc/my.cfg w sekcji [mysqld] dodajemy parametr bind-address:

Nowe ustawienia zostaną uwzględnione po restarcie serwera MySQL:

W celu weryfikacji, czy serwer MySQL rzeczywiście nasłuchuje tylko na lokalnym interfejsie należy wydać polecenie:

Polecenie to wyświetla zestawienie wszystkich portów TCP, na których dany serwer nasłuchuje na połączenia. Ważne żeby port 3306, na którym domyślnie nasłuchuje serwer MySQL, był przypisany do interfejsu 127.0.0.1 (i żadnego innego, z wyjątkiem ::1, w przypadku korzystania z IPv6). Przykładowe zestawienie, z poprawnie nasłuchującym portem 3306 wygląda tak (patrz na kolumnę Local Address w szóstej linii):

17. Konfiguracja firewall

Lokalny firewall (iptables) powinien być skonfigurowany wg. zasady “blokujemy wszystko” poza portami potrzebnymi do działania serwera WWW. Plik /etc/sysconfig/iptables, z przykładową konfiguracją iptables dla serwera WWW, z obsługą jedynie http (tj. bez https) i ewentualnie z dostępem dla administratora po ssh, wygląda tak:

Po ewentualnych zmianach w pliku konfigurującym iptables, należy przeładować usługę iptables i zapewnić że będzie ona stratowana razem z systemem.

 

18. Instalacja fail2ban

Jeśli do naszego serwera chcemy mieć dostęp administracyjny po protokole ssh, to dobrze jest zabezpieczyć się przed atakami DOS i atakami słownikowymi lub typu “brute force”, przez instalację pakietu fail2ban.

W skrócie, jest to program analizujący na bieżąco logi systemowe, i wykrywający próby nieautoryzowanego dostępu. Jeśli liczba nieudanych prób, spod tego samego adresu IP, przekroczy jakąś założoną wartość, to przy pomocy iptables, na określony czas, blokowany jest dostęp do danej usługi z danego adresu IP.

Pakiet jest dostępny w repozytorium EPEL. Instalacja sprowadza się do wydania komendy:

Przykładowy plik z główną konfiguracją znajduje się pod ścieżką /etc/fail2ban/jail.conf. W celu wprowadzenia zmian w konfiguracji należy ten plik przekopiować do /etc/fail2ban/jail.local i ewentualne zmiany wprowadzać w tym drugim pliku.

Cały mechanizm jest bardzo elastyczny i pozwala na kontrolę dostępu do wielu usług. Domyślnie, monitoring większości z nich jest wyłączony. Nas interesuje usługa ssh. Monitoring tej usługi jest akurat aktywny bezpośrednio po zainstalowaniu pakietu. Można to zweryfikować szukając w pliku jail.local sekcji  [ssh-iptables] i mieszczonego w niej parametru enabled = true.

Najważniejsze parametry z pliku jail.conf, które być może chcieli byśmy dostosować do własnych potrzeb to:

bantime – liczba sekund na jaką następuje ewentualne zablokowanie danego adresu IP,

findtime – wyrażony w sekundach, czas określający maksymalny “wiek” wpisów z logów systemowych branych pod uwagę przy analizie “incydentów”,

maxentry – określa liczbę incydentów, które muszą zajść w czasie określonym przez findtime, zanim usługa zostanie zablokowana dla danego adresu IP, na czas określony przez bantime.

Proszę zwrócić uwagę, że powyższe parametry mogą wytępować w dwóch miejscach, w sekcji [default] oraz w sekcjach odpowiadających poszczególnym usługom. Dla przykładu, parametr maxentry powtarza się w sekcji [ssh-iptables], przez co nadpisuje wartość domyślną zdefiniowaną w sekcji [default].

Poniżej znajduje się fragment pliku konfiguracyjnego odpowiedzialny za monitoring usługi ssh:

Widać tutaj deklarację dwóch akcji wykonywanych w różnych momentach cyklu pracy fail2ban. Akcja o nazwie iptables, wraz z parametrami, odpowiedzialna jest za zablokowanie usługi SSH na poziomie lokalnego firewalla, natomiast akcja sendmail-whois, odpowiedzialna jest za wysyłanie powiadomień o zdarzeniach takich jak zablokowanie jakiegoś adresu IP czy uruchomienie i zatrzymanie mechanizmu fail2ban w kontekście monitoringu danej usługi. W parametrach, przekazywanych do deklaracji akcji sendmail-whois, widać parametr o nazwie dest. Jest to adres na jaki będą wysyłane powiadomienia. UWAGA: Domyślnie wartość tego parametru była ustawiona na root, ale powyżej widać, że została zmieniona na root@localhost.localdomain. W moim przypadku taka zmiana była konieczna, bo domyślnie powiadomienia były wysyłane do użytkownika root na węźle określonym przez wartość zwracaną przez funkcję systemową hostname(). Stanowiło to problem, ponieważ wcześniej, agent pocztowy został tak skonfigurowany, żeby nie odbierał połączeń na interfejsach innych niż lokalny interfejs 127.0.0.1. Tak więc, próby wysyłania poczty przez interfejs skojarzony z nazwą hosta zwracaną przez funkcję hostanme() nie udadzą się bo agent pocztowy ich nie obsłuży. Obejściem tego problemu było precyzyjne zdefiniowanie wartości parametru dest na root@localhost.localdomain. Możliwe jest też zupełne zrezygnowanie z powiadomień. W tym celu należy zupełnie usunąć lub zakomentować deklarację akcji sendmail-whois.

Należy też się upewnić, że w systemie została uruchomiona usługa fail2ban, i że będzie ona uruchamiana razem z systemem:

19. Instalacja logwatch

Logwatch to narzędzie uruchamiane raz dziennie z cron-a, i przygotowujące raport z analizy logów systemowych. Raport jest następnie wysyłany na zadane konto email.

W celu instalacji logwatch wydajemy komendę:

W pliku /usr/share/logwatch/default.conf/logwatch.conf ustawiamy adres na który wysyłane będą raporty:

Uwaga: Wartość parametru MailTo została zmieniona z root na root@localhost.localdomain” z powodów analogicznych do opisanych przy zmianie adresu do powiadomień w mechanizmie fail2ban.

Upewniamy się, że usługa crond jest aktywna i że będzie uruchamiana razem z systemem.

Od tej chwili, logwatch powinien być automatycznie uruchamiany raz dziennie.

Krótekie, zwięzłe wprowadzenie do logwatch można znaleźć pod adresem http://osworld.pl/logwatch-potezny-wszechstronny-i-niedoceniany-analizator-logow/

 20. Restart serwerów, iptables, itd.

Na koniec, żeby nowe ustawienia zostały uwzględnione należy przeładować serwer httpd i ewentualnie, jeśli tego wcześniej nie zrobiliśmy serwis iptables i serwer myslq:

Podsumowanie

Przedstawione powyżej kroki na pewno nie wyczerpują szerokiej gamy możliwości podniesienia bezpieczeństwa serwera WWW. W sieci można znaleźć wiele poradników na ten temat. Niektóre poradniki mogą zawierać dużo szersze procedury, a niektóre odrobinę prostsze. Procedura przedstawiona w tym miejscu jest procedurą którą uważam za minimum na serwerach które sam wystawiam do sieci. Po osiągnięciu takiego minimalnego poziomu bezpieczeństwa, w zależności od konkretnej sytuacji i wymagań, wprowadzam dodatkowe zabezpieczenia.

Jeśli instalujemy serwer, gdzie wymagania odnośnie bezpieczeństwa są bardzo wysokie to dobrym pomysłem jest skorzystanie z narzędzia które automatycznie wykonuje audyt serwera i przedstawia raport z sugestiami odnośnie wykrytych nieprawidłowości czy też podatności. Przykład takiego darmowego programu to np. Lynis. Można go pobrać ze strony http://cisofy.com/lynis.

Bezpieczne strony powinny też wykorzystywać protokół https, którego konfiguracja nie została przedstawiona w tym wpisie. Często spotyka się też rozwiązania gdzie z uwagi na koszty związane obsługą szyfrowania, protokół https jest wymuszony jedynie przy dostępie do najbardziej wrażliwych obszarów danego portalu. Gdy już się zdecydujemy na wdrożenie protokołu https, to warto zweryfikować poprawność jego konfiguracji i ewentualną podatność na znane błędy przy pomocy strony https://www.ssllabs.com/ssltest/. Na stronie tej podajemy adres naszego serwera, po czym wykonywany jest audyt siły i jakości konfiguracji protokołu https. Na końcu otrzymujemy raport z ewentualnymi sugestiami jak podnieść poziom bezpieczeństwa.

Napisano w Administracja, Apache, Bazy danych, PHP, www