Krótko: Zapora dla serwera WWW dopuszcza ruch przychodzący tylko na TCP 80 i 443, przy HTTP/3 dodatkowo UDP 443, a SSH tylko ze znanych sieci. Wszystko inne jest odrzucane: bazy danych, pamięci podręczne, PHP-FPM i panele administracyjne nie należą do internetu. Proszę najpierw sprawdzić poleceniem ss -tulpn, co nasłuchuje, następnie zbudować zestaw reguł z ustawieniem domyślnym „odrzucaj” i przetestować wynik z zewnątrz. Ograniczenie połączeń wychodzących dodatkowo zmniejsza skutki włamania.
Zapomniane drzwi obok drzwi wejściowych
W PHP2Shell uwaga skupiła się na samym serwerze WWW, na portach 80 i 443. Muszą być otwarte, w przeciwnym razie nie ma strony. Przy inwentaryzacjach po takich lukach często zwraca jednak uwagę coś innego: obok drzwi wejściowych stoją otwarte kolejne drzwi. Baza danych nasłuchuje na wszystkich interfejsach sieciowych, pula PHP-FPM na porcie 9000, usługa pamięci podręcznej bez hasła, panel administracyjny z fazy konfiguracji.
Każde z tych drzwi to osobna powierzchnia ataku z własnymi lukami. Zapora, która dopuszcza tylko to, co niezbędne, zamyka je kilkoma regułami. Jest to jeden z najskuteczniejszych środków przy zabezpieczaniu własnego serwera, jak opisuje także strona Samodzielne zabezpieczanie serwerów root i vServer.
Co ma być otwarte, a co nie?
Zasadą jest lista dozwolonych: wszystko jest zamknięte, a osiągalne są tylko świadomie udostępnione usługi. Pomaga w tym poniższy przegląd.
| Usługa | Typowy port | Dostępna z zewnątrz? |
|---|---|---|
| HTTP i HTTPS | TCP 80, TCP 443 | tak |
| HTTP/3 (QUIC) | UDP 443 | tylko jeśli używane |
| SSH | TCP 22 | tylko ze znanych sieci lub przez VPN |
| Panel hostingu | zależnie od produktu | tylko ze znanych sieci |
| MySQL/MariaDB, PostgreSQL | TCP 3306, TCP 5432 | nie |
| Redis, Memcached | TCP 6379, TCP 11211 | nie |
| PHP-FPM | TCP 9000 lub gniazdo Unix | nie, lepiej tylko gniazdo |
| Serwer pocztowy | TCP 25, 465, 587, 993 | tylko jeśli serwer odbiera lub udostępnia pocztę |
Usługa, która nie musi być dostępna na zewnątrz, powinna dodatkowo nasłuchiwać tylko na 127.0.0.1 lub gnieździe Unix. Wtedy pozostaje nieosiągalna także w razie awarii zapory. Do dostępów administracyjnych nadają się zezwolenia dla adresów IP lub VPN; szczegóły w artykule Osłanianie paneli administracyjnych przez IP, VPN lub dodatkową ochronę.
Krok 1: ustalić, co nasłuchuje
Zanim napiszą Państwo reguły, proszę zyskać przegląd. Poniższe polecenia tylko odczytują:
# Wszystkie nasłuchujące porty TCP i UDP z procesem
sudo ss -tulpn
# Bieżący zestaw reguł nftables
sudo nft list ruleset
# Status ufw, jeśli używany
sudo ufw status verbose
W wyniku ss proszę zwrócić uwagę na adres przed portem. 0.0.0.0 lub [::] oznacza: usługa nasłuchuje na wszystkich interfejsach. 127.0.0.1 oznacza: tylko lokalnie. Dla każdej usługi proszę odnotować, czy musi być osiągalna z zewnątrz. Ta lista jest zarazem częścią Państwa inwentarza serwerów.
Zapora dla serwera WWW z nftables
nftables to aktualny filtr pakietów w Linuksie. Poniższy zestaw reguł wzoruje się na przykładzie z oficjalnej wiki nftables i dodaje zezwolenie na SSH tylko z sieci administracyjnej. Adresy są przykładami dokumentacyjnymi i trzeba je zastąpić.
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
set verwaltung_v4 {
type ipv4_addr
flags interval
elements = { 203.0.113.0/28 }
}
chain input {
type filter hook input priority 0; policy drop;
ct state vmap { established : accept, related : accept, invalid : drop }
iifname lo accept
# IPv6 potrzebuje wykrywania sąsiadów
icmpv6 type { nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept
icmp type echo-request limit rate 5/second accept
icmpv6 type echo-request limit rate 5/second accept
# Strona WWW
tcp dport { 80, 443 } accept
# tylko przy HTTP/3:
# udp dport 443 accept
# SSH tylko z sieci administracyjnej
ip saddr @verwaltung_v4 tcp dport 22 accept
}
chain forward {
type filter hook forward priority 0; policy drop;
}
}
Przed załadowaniem proszę sprawdzić zestaw reguł poleceniem nft -c -f /etc/nftables.conf. Pierwszy wiersz flush ruleset usuwa wszystkie istniejące reguły, także te, które wpisał Docker lub Fail2ban. Na serwerach z Dockerem lub innymi usługami ustawiającymi własne reguły należy więc dostosować zestaw reguł, zamiast go zbiorczo zastępować. Jak Fail2ban współpracuje z zaporą, opisuje artykuł Konfiguracja Fail2ban dla serwerów WWW.
Kto woli prościej, używa w Ubuntu ufw. Ważna jest kolejność: najpierw zezwolenie dla SSH, potem włączenie.
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow proto tcp from 203.0.113.0/28 to any port 22
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
Reguły zapory na zdalnych serwerach proszę zmieniać tylko przy zapewnieniu drugiej drogi dostępu, na przykład konsoli dostawcy. Błędna reguła SSH w przeciwnym razie zablokuje Państwa samych.
Połączenia wychodzące: niedoceniana połowa
Większość zapór zezwala na wszystko w kierunku wychodzącym. Dla serwera WWW to hojność. Po włamaniu atakujący używają połączeń wychodzących, aby doładowywać narzędzia, wyprowadzać dane lub odbierać polecenia. Procesy serwera WWW lub PHP, które nawiązują nieoczekiwane połączenia, są więc uznawane za sygnał ostrzegawczy.
Ograniczenie połączeń wychodzących jest bardziej pracochłonne, ponieważ każde uzasadnione połączenie musi być znane: źródła pakietów dla aktualizacji, operatorzy płatności, interfejsy, wysyłka poczty, serwery czasu. Pragmatyczną drogą jest najpierw rejestrowanie połączeń wychodzących, potem sporządzenie listy dozwolonych i dopiero na końcu blokowanie. Jak sprawdzić istniejące połączenia, opisuje artykuł Sprawdzanie połączeń wychodzących serwera; jakie inne ślady wskazują na włamanie, opisuje artykuł Czy już zostałem przejęty?.
Na koniec proszę przetestować z zewnątrz, z innego komputera i wyłącznie wobec własnych systemów, jakie porty są faktycznie osiągalne. Dopiero ten test pokazuje, czy Docker, zapora dostawcy i reguły serwera razem dają pożądany obraz.
Wniosek: kilka reguł, duży efekt
Zapora serwera WWW nie musi być skomplikowana. Porty WWW otwarte, administracja tylko ze znanych sieci, wszystko inne odrzucane: dzięki tym regułom podstawowym bazy danych, pamięci podręczne i zapomniane usługi znikają z internetu. To jeden ze środków o najlepszym stosunku nakładu do pożytku.
Przeciw lukom w serwerze WWW lub w samym PHP zapora nie pomaga, bo właśnie te porty muszą pozostać otwarte. Jej siła polega na utrzymywaniu małej pozostałej powierzchni ataku i, dzięki ograniczeniu połączeń wychodzących, powstrzymywaniu skutków włamania.
Decydująca jest pielęgnacja. Każda nowa usługa, każdy kontener i każda zmiana u dostawcy może przesunąć obraz. Dlatego kontrolę z zewnątrz proszę powtarzać po każdej większej zmianie i w stałych odstępach, na przykład raz na kwartał.
Jak konkretnie pomaga PHP SOS?
- Inwentaryzacja: rejestrujemy wszystkie nasłuchujące usługi i sprawdzamy z zewnątrz, co jest faktycznie osiągalne.
- Budowa zestawu reguł: tworzymy listę dozwolonych dla połączeń przychodzących i, gdzie to sensowne, wychodzących, uzgodnioną z Dockerem, Fail2ban i zaporą dostawcy.
- Bezpieczne przestawienie: zmiany wprowadzamy z drugą drogą dostępu i planem awaryjnym.
- Dokumentowanie: porty, zezwolenia i powody utrwalamy w ramach utwardzania serwerów.
Częste pytania
Jakich portów potrzebuje serwer WWW?
Dla odwiedzających zazwyczaj tylko TCP 80 i 443, przy HTTP/3 dodatkowo UDP 443. SSH i panele administracyjne powinny być dostępne tylko ze znanych sieci lub przez VPN. Porty baz danych, pamięci podręcznej i PHP-FPM nie należą do internetu.
Czy zapora chroni przed PHP2Shell?
Nie. PHP2Shell jest wykorzystywane przez serwer WWW, czyli przez dokładnie te porty, które muszą być otwarte. Zapora zmniejsza jednak pozostałą powierzchnię ataku, a ograniczenie połączeń wychodzących może powstrzymać skutki włamania.
Dlaczego ufw pokazuje porty jako zamknięte, mimo że są osiągalne?
Znanym przypadkiem są kontenery Docker: Docker wpisuje opublikowane porty bezpośrednio do reguł filtra pakietów, z pominięciem ufw. Dlatego otwarte porty należy zawsze dodatkowo sprawdzać z zewnątrz.
Czy zapora mojego dostawcy hostingu wystarczy?
Zapora dostawcy umieszczona przed serwerem to dobra druga warstwa. Nie zastępuje jednak zapory na serwerze, ponieważ nie działa przy błędzie w konfiguracji dostawcy ani przy połączeniach wewnętrznych. Najlepiej używać obu z tymi samymi regułami podstawowymi.