Apache – podstawowy poziom bezpieczeństwa – podsumowanie
Napisał: Patryk Krawaczyński
02/03/2014 w Bezpieczeństwo Brak komentarzy. (artykuł nr 437, ilość słów: 2004)
G
dybym miał na dzisiejszy dzień zapewnić podstawowy poziom bezpieczeństwa swojego serwera WWW – po standardowej instalacji z paczki zacząłbym od kilku rzeczy:
Pierwszą z nich na pewno jest zmiana ustawień nagłówka HTTP Server, aby nikt nie przeprowadzał sondy odnośnie wersji mojego serwera i załadowanych modułów za pomocą techniki banner grabbing np. przy pomocy prostego pluginu do Firefoksa jakim jest Firebug. Poruszając się dalej w strefie wycieku informacji – kolejnym krokiem była by eliminacja ETagów, ponieważ i tak przy większej liczbie serwerów nie spełniają one swojej roli.
Skoro mowa o spełnianiu roli to trudno nie zgodzić się z faktem, że metoda TRACE jest czymś, co obniża poziom bezpieczeństwa naszego serwera i zalecane jest jej wyłączenie. Przejdźmy teraz trochę do obszaru z zakresu wydajności. Podobnie jak w przypadku bezpieczeństwa systemów operacyjnych – nigdy nie należy instalować dodatkowego oprogramowania, którego nigdy się nie będzie używać, a które w swojej standardowej konfiguracji może powodować różnego rodzaju luki. Analogicznie możemy odnieść się do serwera Apache – czy te wszystkie moduły typu mod_negotiation, czy mod_autoindex są nam naprawdę potrzebne? Przecież nasza strona nie posiada innej wersji językowej niż polska i nigdy nie będziemy listować zawartości katalogów bez plików index.html. Dlatego podstawowym krokiem przed wystawieniem naszego serwera na okrutny świat Internetu jest refaktoryzacja ładowanych modułów. Nie tylko wpływają one na jego wydajność, ale także rozszerzają rodzaje i podatności na różne ataki. Tym bardziej, że w większości przypadków są pozostawiane bez użytku w standardowej, dostarczonej przez pakiet konfiguracji.
Kolejnym etapem jest zaplanowanie struktury naszych hostów wirtualnych i ich obsługi. W dzisiejszych czasach rzadko zdarza się, że serwer WWW nazywa się identycznie (posiada taką samą nazwę DNS), jak domena strony którą obsługuje – co stanowi dobrą praktykę. W ten sposób możemy zapewnić separację wielu adresów IP / interfejsów, czy żądań HTTP zwracanych i interpretowanych w nagłówku HOST – tym samym rozróżniając, które żądania są naprawdę przeznaczone do konkretnych domen i aplikacji, a które przez przypadek próbują uzyskać dostęp do zastrzeżonych zasobów. W serwerze Apache pierwszy zdefiniowany host wirtualny dla danego adresu IP (i portu) – obsługuje wszystkie żądania jakie kierowane są do serwera pod warunkiem, że nie znalazły one odwzorowania w definicjach innych vhostów. Dlatego pierwszym wirtualnym hostem powinien być wirtualny host typu catch all. Przykład: serwer gundam.x0x.pl (12.34.56.78) ma hostować stronę o adresie: x0x.pl.
Zanim zdefiniujemy nasz pierwszy vhost musimy zadbać o odpowiednie prawa do wybranych katalogów / podkatalogów, ponieważ każdy z nich mimo, że jest odwzorowany w systemie (z własnymi właścicielami i prawami dostępu) – w serwerze Apache może zostać osobno skonfigurowany do przestrzegania odpowiednich uprawnień i obsługi różnych funkcjonalności. Dlatego dla głównego i najwyższej postanowionego w hierarchii katalogu “/” powinniśmy przyjąć najbardziej restrykcyjne ustawienia. Jest to podobna polityka jak dla firewalli – blokujemy wszystko z założenia, aby później robić dziury zezwoleniami:
<Directory /> Options None AllowOverride None Order deny,allow Deny from all </Directory>
Wracając do hostów wirtualnych. Naszym pierwszym hostem będzie zarówno strona statyczna dla serwera gundam.x0x.pl, jak i “śmietnik” dla wszystkich innych żądań, które nie odwołują się do serwisu x0x.pl:
NameVirtualHost 12.34.56.78:80 <VirtualHost 12.34.56.78:80> ServerAdmin administrator@x0x.pl ServerName gundam.x0x.pl ServerAlias www.gundam.x0x.pl UseCanonicalName On DocumentRoot /var/www/html <Directory /var/www/html> Order allow,deny Allow from all <Limit GET HEAD> Order allow,deny Allow from all </Limit> <LimitExcept GET HEAD> Order deny,allow Deny from all </LimitExcept> </Directory> ErrorLog /var/log/httpd/error_log LogLevel warn CustomLog /var/log/httpd/access_log combined </VirtualHost>
Dlaczego “śmietnik”? Ponieważ od teraz wszystkie żądania, które w nagłówku “Host:
” będą miały ustawione nieprawidłowe lub spreparowane nazwy domen, a będą kierowane na nasz serwer (12.34.56.78) trafią do tego wirtualnego hosta. Co to oznacza w praktyce? Że wszystkie boty, skanery i inne plugastwa ustawione na automatyczne i bezmyślne przeszukiwanie podatności serwerów WWW będą odbijały się od np. pliku index.html
umieszczonym w katalogu /var/www/html
, a nasze logi zapełnią się wpisami typu:
... "GET /w00tw00t.at.ISC.SANS.DFind:) HTTP/1.1" 404 198 "-" "-" ... "GET /cgi-bin/php5 HTTP/1.1" 404 184 "-" "-" ... "GET /phpmyadmin/config/config.inc.php?p=phpinfo(); HTTP/1.1" 404 195 "-" "-"
W definicji w/w vhosta nie musimy ponownie definiować wpisów typu:
Options None AllowOverride None
Ponieważ nie odejmujemy, ani dodajemy żadnych uprawnień i funkcji, a zgodnie z regułą łączenia definicji uprawnień w sekcjach dziedziczymy te już wcześniej zdefiniowane dla katalogu “/”. Ze względu na fakt, że będziemy wyświetlać prostą stronę HTML ograniczamy metody HTTP do dwóch podstawowych i zupełnie wystarczających GET oraz HEAD za pomocą dyrektywy Limit. Możemy przejść do kolejnego stadium, jakim jest definicja hosta wirtualnego dla aplikacji obsługiwanej przez adres x0x.pl:
<VirtualHost 12.34.56.78:80> ServerAdmin administrator@x0x.pl ServerName x0x.pl ServerAlias www.x0x.pl UseCanonicalName On DocumentRoot /data/x0x/www <Directory /data/x0x/www> Options SymLinksIfOwnerMatch Order allow,deny Allow from all RewriteEngine On RewriteBase / RewriteCond %{HTTP_HOST} ^www\.x0x\.pl$ [NC] RewriteRule ^(.*)$ http://x0x.pl/$1 [R=301,L] <Limit GET POST HEAD> Order allow,deny Allow from all </Limit> <LimitExcept GET POST HEAD> Order deny,allow Deny from all </LimitExcept> <FilesMatch "configuration.php"> Order allow,deny Deny from all </FilesMatch> </Directory> ErrorLog /var/log/httpd/x0x_error_log LogLevel warn CustomLog /var/log/httpd/x0x_access_log combined </VirtualHost>
W naszej aplikacji x0x umożliwiamy podążanie za linkami symbolicznymi w systemie plików, ale tylko jeśli właściciel linku symbolicznego i jego celu to ten sam użytkownik (SymLinksIfOwnerMatch
). Skoro jesteśmy administratorami naszego serwera WWW nie ma sensu definicja umożliwiająca korzystanie z plików .htaccess
(AllowOverride All
), ponieważ wszystkie interesujące nas reguły przepisujące możemy zawrzeć w definicji samego hosta. Jeśli zmuszeni jesteśmy jednak do udostępnienia plików typu .htaccess
znacznie lepiej jest przyznawać dostęp do konkretnych funkcji niż zezwolić na wszystko w standardzie:
AllowOverride None AllowOverrideList RewriteEngine RewriteOptions RewriteBase RewriteCond RewriteRule
Poza tym nieprawidłowe wykorzystanie plików .htaccess
może również wpłynąć negatywnie na wydajność serwera. Jak widzimy lista metod HTTP została rozszerzona o metodę POST umożliwiającą przesyłanie danych w formularzach i edytorach. Dodatkowo Apache będzie starał się chronić wrażliwy plik configuration.php zawierający dane autoryzujące dedykowanego użytkownika aplikacji do bazy danych. Jeśli mamy problem z określeniem, który wirtualny host jest pierwszy dla danego adresu IP i pełni rolę catch-all powinniśmy wykorzystać polecenie: httpd
lub apache2 -S
.
Serwer HTTPd powinien zawsze być uruchomiony z prawami zwykłego użytkownika (bez możliwości korzystania z powłoki systemowej!) w celu zapewnienia separacji uprawnień (w razie luki w zabezpieczeniach) dla innych daemonów uruchomionych na systemie. Zazwyczaj serwery Apache instalowane są za pomocą pakietów, które tworzą i uruchamiają go z prawami takich użytkowników – dla wielu dystrybucji jest to “apache” lub “www-data”:
[root@gundam conf]# ps -ef | grep http | grep -v grep root 15570 1 0 Feb24 ? 00:00:07 /usr/sbin/httpd apache 15606 15570 0 Feb24 ? 00:02:53 /usr/sbin/httpd apache 15650 15570 0 Feb24 ? 00:02:45 /usr/sbin/httpd apache 15674 15570 0 Feb24 ? 00:04:10 /usr/sbin/httpd apache 15675 15570 0 Feb24 ? 00:02:45 /usr/sbin/httpd apache 15676 15570 0 Feb24 ? 00:03:11 /usr/sbin/httpd
Powyżej widzimy procesy serwera Apache odpalonego w trybie prefork. Jeden proces niestety nadal jest uruchomiony z prawami root. Taka kolej rzeczy jest spowodowana koniecznością nasłuchu serwera na uprzywilejowanym porcie 80. Jedną z metod jest wykorzystanie POSIX capabilities, drugą natomiast ustawienie nasłuchu serwera np. na porcie 8080 i wykorzystanie loadbalancera lub serwera proxy do przekazywania połączeń z portu 80 na 8080. Możemy to także wykorzystać za pomocą iptables:
iptables -t nat -A PREROUTING -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 8080 iptables -t nat -A OUTPUT -p tcp -m tcp -d $IP --dport 80 -j REDIRECT --to-ports 8080
Gdzie $IP
jest adresem IP, na którym nasłuchuje serwer. Niezależnie jakie rozwiązanie wybierzemy powinniśmy również zabezpieczyć pliki związane z serwerem httpd (tutaj przykład na podstawie dystrybucji CentOS):
chmod 750 /etc/httpd chmod 750 /etc/httpd/conf /etc/httpd/conf.d /var/log/httpd chmod 750 /usr/sbin/httpd chmod 640 /etc/httpd/conf/*.conf /etc/httpd/conf.d/*.conf
Właścicielem tych plików powinien być użytkownik z prawami, którego zdecydowaliśmy się uruchamiać serwer httpd, a grupą ten sam użytkownik lub uniwersalnie: root. Na koniec warto przypomnieć, że jeśli nasza aplikacja jest pobierana za pomocą dowolnego repozytorium kodu należy pamiętać o zabezpieczeniu odpowiednich katalogów. Możemy również rozważyć restrykcyjne potraktowanie połączeń sieciowych generowanych przez nią samą. Oczywiście zupełną podstawą jest systematyczna aktualizacja oprogramowania.
Więcej informacji: Security Tips