Jak nie dać się zhakować: obrona aplikacji Laravel i serwera w praktyce

18 min. czytania

Port, o którym nikt nie pamiętał

Większość udanych ataków na małe i średnie firmy nie wymaga geniusza. Wystarczy port otwarty na świat, którego nikt nie powinien był otworzyć, i bot, który skanuje cały internet w kilka godzin.

Niedawno widziałem taki scenariusz z bliska. Nie było zero-daya ani wyrafinowanego phishingu, tylko usługa dostępna z zewnątrz, choć miała słuchać wyłącznie wewnątrz sieci. Po fakcie wszyscy wiedzieli, jak temu zapobiec.

Na typowym serwerze z Laravelem najczęściej dzieje się to w banalny sposób: przez linijkę ports: "6379:6379" w docker-compose.yml. Docker sam dopisuje reguły iptables, więc port jest publiczny, choć ufw status twierdzi, że ruch przychodzący jest blokowany. Wrócę do tego niżej, bo to jedyny punkt z tej listy, w który ludzie nie wierzą, dopóki nie przeskanują własnego serwera.

Dla kogo: dla developerów i małych zespołów, które same utrzymują aplikacje (najczęściej Laravel na VPS-ie lub w chmurze) i nie mają osobnego działu bezpieczeństwa.

Co dostaniesz:

  • 12 błędów, od których zaczyna się większość włamań w małych zespołach
  • warstwę sieci i serwera w szczegółach, razem z pułapką Dockera
  • warstwy aplikacji i danych w formie krótkiej checklisty z linkami do pogłębień
  • sposób na wykrycie ataku, zanim zrobi to klient, w tym 10-linijkowy codzienny diff portów
  • plan na pierwsze 72 godziny incydentu, razem z obowiązkami z RODO i NIS2/KSC
  • checklistę do wydruku (także jako PDF do pobrania)

Cztery zasady

Każdy punkt dalej w artykule wynika z jednej z tych czterech.

  1. Minimalna powierzchnia ataku. Czego nie wystawiasz, tego nikt nie zaatakuje. Każdy otwarty port, publiczny endpoint i zainstalowany pakiet to potencjalne wejście.
  2. Obrona warstwowa (defense in depth). Zakładasz, że każda pojedyncza warstwa zawiedzie. Firewall nie zastąpi autoryzacji w aplikacji, a autoryzacja nie zastąpi szyfrowania backupów.
  3. Najmniejsze uprawnienia. Użytkownik bazy dla aplikacji nie potrzebuje DROP. Klucz API do wysyłki maili nie potrzebuje dostępu do całego konta w chmurze. Proces PHP nie musi działać jako root.
  4. Załóż, że ktoś już jest w środku (assume breach). Zapytaj, jak szybko się zorientujesz i jak mało ugrają. Dlatego logi, alerty i przetestowane backupy są równie ważne jak firewall.
text
internet
   │
   ▼
[ 1. sieć i serwer ]  firewall, zamknięte porty, SSH, aktualizacje   ┐
   │                                                                 │
   ▼                                                                 │  wykrywanie:
[ 2. aplikacja ]      autoryzacja, walidacja, rate limiting          │  logi, alerty,
   │                                                                 │  codzienne skany
   ▼                                                                 │
[ 3. dane ]           sekrety, szyfrowanie, backupy 3-2-1            ┘

Atakujący musi przejść przez każdą warstwę po kolei. Wykrywanie obserwuje wszystkie trzy.

Top 12 błędów, przez które firmy są atakowane

Większość incydentów w małych firmach zaczyna się od jednego z tych punktów. Pierwsze sześć to infrastruktura, kolejne sześć to aplikacja i procesy.

#BłądDlaczego to groźneNaprawa
1Baza, Redis, Elasticsearch lub panel admina dostępne z internetuboty skanują cały IPv4 w godziny; Redis bez hasła to często zdalne wykonanie kodusłuchanie na 127.0.0.1 lub w sieci prywatnej, firewall „domyślnie zamknięte”
2ports: "6379:6379" w Docker ComposeDocker sam dopisuje reguły iptables i omija UFW; port jest publiczny, choć firewall „go blokuje”127.0.0.1:6379:6379 albo brak ports i wewnętrzna sieć Dockera
3SSH z logowaniem hasłem, root może się logowaćbrute force działa całą dobę, słabe hasło to kwestia czasutylko klucze, PermitRootLogin no, fail2ban lub CrowdSec, najlepiej SSH tylko przez VPN
4Brak aktualizacji systemu i obrazówznane podatności mają gotowe exploity w ciągu dni od publikacjiunattended-upgrades, regularny rebuild obrazów, alerty o nowych wersjach
5Document root ustawiony na katalog projektu zamiast public//.env i /.git/ do pobrania przez przeglądarkę razem z hasłami i kodemroot serwera WWW zawsze na public/, jawna blokada plików zaczynających się od kropki
6APP_DEBUG=true na produkcjistack trace z konfiguracją, ścieżkami i fragmentami kodu; przy facade/ignition w wersji przed 2.5.2 (CVE-2021-3129) strona debug prowadziła nawet do zdalnego wykonania koduAPP_ENV=production, APP_DEBUG=false, sprawdzone w pipeline przed deployem
7Sekrety w repozytorium (.env, klucze API, APP_KEY)wyciek repo to wyciek wszystkiego; wyciek APP_KEY pozwala fałszować zaszyfrowane cookies.env w .gitignore, skaner sekretów w CI (np. gitleaks), sekrety w menedżerze sekretów
8Brak autoryzacji na poziomie obiektu (IDOR)/invoices/124 zamiast /invoices/123 i widzisz cudze danePolicies dla każdego modelu, Gate::authorize() (albo $this->authorize() z traitem AuthorizesRequests), scoped route bindings
9Mass assignmentużytkownik dopisuje is_admin=1 do formularza i dostaje uprawnienia$fillable, zapis tylko z $request->validated()
10Brak rate limitingu na logowaniu i APIbrute force haseł, credential stuffing, enumeracja kontthrottle na trasach logowania, resetu hasła i API
11Backupy na tym samym serwerze i nigdy nietestowaneransomware szyfruje dane i backupy jednocześnie; albo okazuje się, że kopia jest pustazasada 3-2-1, kopia niezmienialna lub offline, test odtworzenia co kwartał
12Brak logów i alertówatak trwa tygodniami, a dowiadujesz się od klienta albo z wyciekucentralne logi, alerty na anomalie, regularny skan z zewnątrz

Jeśli masz czas tylko na jedną rzecz po przeczytaniu tego artykułu: przeskanuj swoje serwery z zewnątrz (następna sekcja) i porównaj wynik z tym, co Twoim zdaniem powinno być otwarte.

Warstwa 1: sieć i serwer

Cel tej warstwy jest prosty: z internetu widać tylko 80 i 443. Wszystko inne jest zamknięte albo dostępne wyłącznie przez VPN.

Sprawdź, co naprawdę jest otwarte

Zacznij od skanu z zewnątrz, z maszyny spoza Twojej sieci. Tak widzi Cię atakujący.

bash
# pełny skan TCP wszystkich portów
nmap -Pn -p- --min-rate 1000 TWOJ_ADRES_IP

# wersje usług na znalezionych portach
nmap -Pn -sV -p 22,80,443,3306,5432,6379,9200 TWOJ_ADRES_IP

Sprawdź też swoje adresy IP w Shodanie lub Censysie. Jeśli jest tam Twój Redis albo Elasticsearch, boty znalazły go dawno temu.

Na samym serwerze wypisz wszystko, co nie jest przypięte do loopbacka, żeby zobaczyć gniazda na 0.0.0.0, [::] i adresach publicznych:

bash
ss -tulpn | grep -vE '127\.0\.0\.[0-9]+|\[::1\]'

Firewall: domyślnie zamknięte

bash
ufw default deny incoming
ufw default allow outgoing
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow from ADRES_VPN to any port 22 proto tcp
ufw enable

Uwaga: jeśli robisz to przez SSH, a VPN jeszcze nie działa, ufw enable odetnie Cię od serwera. Najpierw postaw VPN albo tymczasowo wpuść port 22 ze swojego obecnego IP i usuń tę regułę, gdy potwierdzisz, że SSH przez VPN działa. Trzymaj otwartą konsolę webową dostawcy jako drogę awaryjną.

W chmurze ustaw to samo w security groups lub w firewallu dostawcy. To druga, niezależna warstwa: błąd w jednej nie otwiera serwera.

Pułapka Dockera

Wpis ports: "5432:5432" w docker-compose.yml publikuje port na wszystkich interfejsach. Docker wstawia własne reguły iptables przed tymi, którymi zarządza UFW, przez co UFW ich nie blokuje, nawet jeśli ufw status mówi coś innego. Tu prawdę powie tylko skan z zewnątrz z poprzedniej sekcji.

yaml
services:
  app:
    image: your-app
    networks: [frontend, backend]
  db:
    image: postgres:16
    # źle: port publiczny mimo firewalla
    # ports: ["5432:5432"]
    # dobrze: bez ports, dostęp tylko z sieci backend
    networks: [backend]
  redis:
    image: redis:7
    # tak samo: bez ports, dostęp tylko z sieci backend
    networks: [backend]

networks:
  frontend:
  backend:
    internal: true

internal: true odcina też sieć backend od wychodzącego internetu, a tego właśnie chcesz dla bazy i cache'a. Oznacza to też, że publikowane porty na tej sieci nie działają. Jeśli potrzebujesz dostępu do bazy z hosta (tunel SSH), podłącz ją dodatkowo do sieci bez internal i opublikuj jako 127.0.0.1:5432:5432, nigdy 5432:5432. Jeśli kontener naprawdę musi być publiczny, filtruj ruch w łańcuchu DOCKER-USER, a nie w UFW.

SSH, aktualizacje i reszta konfiguracji bazowej

Tylko klucze (PasswordAuthentication no, PermitRootLogin no, lista AllowUsers), fail2ban lub CrowdSec i najlepiej SSH dostępne wyłącznie przez VPN (WireGuard, Tailscale) albo bastion. Zmiana portu SSH na niestandardowy zmniejsza szum w logach, ale nie jest zabezpieczeniem. Włącz unattended-upgrades dla poprawek bezpieczeństwa i przebudowuj obrazy Dockera według harmonogramu, a nie tylko przy zmianie kodu: FROM php:8.3-fpm sprzed roku to rok niezałatanych bibliotek.

Pełna konfiguracja serwera, z użytkownikami, systemd, journald i odtwarzaniem, jest w artykule Bazowa konfiguracja serwera Linux dla DevOps.

Usługi, które nigdy nie powinny być publiczne

MySQL i PostgreSQL, Redis, Memcached, Elasticsearch i OpenSearch, RabbitMQ, API Dockera (2375/2376), panele administracyjne (phpMyAdmin, Horizon, Telescope, Grafana, Portainer), dashboardy kolejek i monitoringu. Jeśli musisz mieć do nich dostęp z zewnątrz: VPN albo tunel SSH, plus osobne logowanie.

Warstwa 2: aplikacja Laravel

Laravel ma dobre ustawienia domyślne: CSRF, escapowanie w Blade, zapytania z bindowaniem parametrów. Większość podatności w aplikacjach Laravel powstaje wtedy, gdy ktoś te mechanizmy obchodzi albo zapomina o autoryzacji. Pogłębienie z kodem do każdego punktu znajdziesz w artykule Zabezpiecz aplikację Laravel: ryzyka OWASP i praktyczna obrona. Tu wersja skrócona.

Najczęściej spotykana luka to brak autoryzacji na poziomie obiektu. Uwierzytelnienie mówi, kim jesteś; autoryzacja mówi, do czego masz dostęp.

php
// źle: każdy zalogowany zobaczy każdą fakturę
public function show(Invoice $invoice)
{
    return view('invoices.show', compact('invoice'));
}

// dobrze: Policy sprawdza właściciela
public function show(Invoice $invoice)
{
    Gate::authorize('view', $invoice);

    return view('invoices.show', compact('invoice'));
}

Zasada: każdy endpoint, który przyjmuje identyfikator, ma test sprawdzający, że inny użytkownik dostaje 403. Dla zasobów zagnieżdżonych używaj scopeBindings(), żeby /teams/1/projects/5 nie zwracało projektu z innego zespołu.

Reszta w formie checklisty:

  • Konfiguracja produkcyjna: APP_ENV=production, APP_DEBUG=false, SESSION_SECURE_COOKIE=true i krok w CI, który zatrzymuje deploy, gdy debug jest włączony. Telescope, Horizon i Debugbar za bramką (gate) albo wyłączone poza środowiskiem lokalnym.
  • Mass assignment: zapis tylko z $request->validated(), nigdy z $request->all(); zdefiniowane $fillable; w developmencie włączone Model::preventSilentlyDiscardingAttributes().
  • SQL injection: DB::raw(), whereRaw() i orderByRaw() zawsze z bindowaniem; kolumny sortowania z requestu sprawdzane z białą listą.
  • XSS: {!! !!} w Blade i dangerouslySetInnerHTML w React tylko dla HTML-a oczyszczonego po stronie serwera.
  • Rate limiting: throttle na logowaniu, resecie hasła, rejestracji i weryfikacji 2FA; limit na użytkownika lub token w każdym API.
  • Uploady: walidacja typu i rozmiaru, zapis na dysk prywatny, pobieranie przez kontroler z autoryzacją, a serwer WWW nigdy nie wykonuje PHP z katalogów uploadów.
  • Nagłówki: HSTS, CSP wdrażane najpierw w trybie report-only, X-Content-Type-Options: nosniff, frame-ancestors 'none'.
  • Zależności: composer audit i npm audit --omit=dev w każdym pull requeście, do tego Dependabot lub Renovate. Większość dziurawych aplikacji nie ma błędu w swoim kodzie, tylko w paczce sprzed dwóch lat.

Warstwa 3: sekrety, dane i backupy

Ta warstwa decyduje o tym, jak duża będzie szkoda, gdy dwie poprzednie zawiodą.

Sekrety

  • .env nigdy w repozytorium. W CI dodaj skaner sekretów (gitleaks, trufflehog), który blokuje commit z kluczem.
  • Osobne klucze dla każdego środowiska. Klucz z produkcji nie trafia na laptopa developera.
  • Klucze API z minimalnym zakresem: token do S3 tylko do jednego bucketa, token do bramki płatności bez uprawnień administracyjnych.
  • Lista wszystkich sekretów i sposób ich rotacji spisane zawczasu. W trakcie incydentu nie ma czasu na szukanie, gdzie jest klucz do Mailguna.
  • APP_KEY to klucz do szyfrowania cookies i danych, więc jego wyciek jest poważny. Rotacja unieważnia sesje i dane zaszyfrowane starym kluczem, dlatego Laravel pozwala podać poprzednie klucze w APP_PREVIOUS_KEYS na czas migracji.

Dane

  • Szyfrowanie w transmisji wszędzie: HTTPS, połączenie z bazą po TLS, jeśli baza stoi na innej maszynie.
  • Pola szczególnie wrażliwe (PESEL, dane medyczne, tokeny) szyfrowane w bazie, np. castem encrypted w Eloquent.
  • Najmniej danych, ile się da. Czego nie przechowujesz, tego nie wyciekniesz.
  • Konto bazy dla aplikacji bez uprawnień do zmiany schematu na produkcji. Migracje z osobnego konta w pipeline.

Backupy: zasada 3-2-1 plus test

  • 3 kopie danych, na 2 różnych nośnikach lub u dwóch dostawców, 1 poza główną infrastrukturą.
  • Przynajmniej jedna kopia niezmienialna (Object Lock w S3, backup offline). Ransomware szyfruje wszystko, do czego ma dostęp, także backupy podmontowane do serwera.
  • Kopia szyfrowana, a klucz do niej przechowywany gdzie indziej niż kopia.
  • Test odtworzenia co kwartał. Backup, którego nigdy nie przywróciłeś, to hipoteza. Zmierz, ile trwa pełne odtworzenie: to Twój realny czas przestoju.

Jak wykryć atak, zanim zrobi to klient

Nie wykryjesz tego, czego nie logujesz, i nie zareagujesz na log, którego nikt nie czyta. Potrzebujesz trzech rzeczy: właściwych zdarzeń w logach, jednego miejsca, w którym się zbierają, i alertów, które budzą człowieka.

Co logować

ŹródłoZdarzeniaPrzykładowy sygnał ataku
Aplikacjanieudane i udane logowania, zmiany haseł i uprawnień, 403 przy dostępie do zasobów, eksporty danych50 nieudanych logowań na jedno konto w 5 minut; seria 403 na kolejnych ID
Serwer WWWstatusy 4xx/5xx, ścieżki, User-Agentżądania do /.env, /.git/config, /wp-login.php, /vendor/phpunit
Systemlogowania SSH, sudo, nowi użytkownicy, zmiany w cronie i systemdlogowanie SSH z nowego kraju o 3:00 w nocy
Baza danychnowe połączenia, zapytania wolne i masoweSELECT * na tabeli użytkowników spoza aplikacji
Siećotwarte porty, nietypowy ruch wychodzącynowy port w skanie; serwer wysyła gigabajty na nieznany adres

W Laravelu zdarzenia logowania dostajesz za darmo: nasłuchuj Illuminate\Auth\Events\Failed, Login i Lockout i zapisuj je do osobnego kanału logów z adresem IP i identyfikatorem użytkownika. Nigdy nie loguj haseł, tokenów ani pełnych numerów dokumentów.

Jedno miejsce i alerty

  • Logi z wszystkich serwerów w jednym systemie (Grafana Loki, Graylog, ELK lub usługa chmurowa). Atakujący z dostępem do serwera zacznie od czyszczenia lokalnych logów.
  • Alerty na kilka konkretnych reguł zamiast na wszystko. Zbyt wiele alertów kończy się tym, że nikt ich nie czyta.
  • Na start: skok nieudanych logowań, żądania do wrażliwych ścieżek, logowanie SSH spoza VPN, nowy otwarty port, skok ruchu wychodzącego.

Wskaźniki, że ktoś już jest w środku

  • nowe konta systemowe lub nowe klucze w ~/.ssh/authorized_keys
  • nieznane wpisy w crontab -l i nowe usługi w systemd
  • pliki PHP w katalogach uploadów albo w storage/ i public/ (web shelle)
  • procesy z dużym zużyciem CPU o dziwnych nazwach (koparki kryptowalut to częsty pierwszy ładunek)
  • zmienione pliki aplikacji, które nie przeszły przez deploy

Codzienny diff portów

To alert, który wyłapie port otwarty przez pomyłkę, i mieści się w dziesięciu linijkach. Uruchamiaj go z maszyny spoza sieci serwera (mały VPS, runner CI odpalany z harmonogramu), nigdy z samego serwera.

bash
#!/usr/bin/env bash
# port-diff.sh HOST: alert, gdy zmieni się zestaw otwartych portów TCP na HOST
set -euo pipefail
host="$1"
baseline="$HOME/port-diff/$host.baseline"
current="$(nmap -Pn -p- --min-rate 1000 -oG - "$host" | grep -oE '[0-9]+/open' | sort -u)"

if [[ "$current" != "$(cat "$baseline")" ]]; then
    printf 'Open ports on %s changed:\n%s\n' "$host" "$current" \
        | mail -s "port-diff: $host" [email protected]
fi

Wzorzec utwórz raz, gdy już zamkniesz wszystko, co miało być zamknięte: mkdir -p ~/port-diff && nmap -Pn -p- -oG - HOST | grep -oE '[0-9]+/open' | sort -u > ~/port-diff/HOST.baseline. Potem codzienny wpis w cronie, a mail zamień na to, co faktycznie dociera do zespołu (webhook Slacka, ticket).

Dwie kolejne automatyzacje warte czasu na konfigurację:

  • Kontrola integralności plików, np. Wazuh lub AIDE, z alertem przy zmianie plików aplikacji poza deployem.
  • Monitoring dostępności (Uptime Kuma, Better Stack), bo atak często widać najpierw jako spowolnienie.

Gdy do ataku już doszło: plan na pierwsze 72 godziny

W trakcie incydentu nikt nie myśli jasno. Dlatego plan spisuje się wcześniej, a w kryzysie tylko się go wykonuje.

  1. Nie panikuj i nie kasuj. Zanim cokolwiek naprawisz, zrób snapshot dysku i zachowaj logi. Reinstalacja serwera „na szybko” niszczy dowody i nie mówi, jak atakujący wszedł.
  2. Odizoluj. Odetnij zaatakowaną maszynę od sieci (security group, firewall), ale nie wyłączaj jej, jeśli to możliwe: pamięć RAM też jest dowodem.
  3. Zamknij wejście. Znajdź i zamknij drogę wejścia: port, podatność, wykradzione hasło. Bez tego atakujący wróci po odtworzeniu systemu.
  4. Wymień wszystkie sekrety. Hasła do baz, klucze API, APP_KEY, tokeny w CI, klucze SSH. Zakładaj, że wszystko, co było na serwerze, wyciekło.
  5. Szukaj furtek. Atakujący zostawia sobie dostęp: konta, klucze SSH, wpisy w cronie, usługi systemd, web shelle. Najpewniej jest postawić serwer od zera z czystego obrazu i przywrócić dane z kopii sprzed ataku.
  6. Oceń, co wyciekło. Które dane, czyje, z jakiego okresu. Od tego zależą obowiązki prawne poniżej.
  7. Zgłoś, jeśli musisz. Patrz tabela niżej. Terminy liczą się od momentu, w którym się dowiedziałeś, a nie od zakończenia analizy.
  8. Poinformuj. Klientów i użytkowników, jeśli dotyczy ich ryzyko. Krótko, konkretnie, z informacją, co mają zrobić (np. zmienić hasło).
  9. Napisz post-mortem bez szukania winnych. Oś czasu, przyczyna źródłowa, co zadziałało, co nie, lista działań z właścicielami i terminami.

Obowiązki prawne

RegulacjaKiedy dotyczyKomu i kiedy zgłosić
RODOwyciek lub utrata danych osobowych z ryzykiem dla osóbPrezesowi UODO w ciągu 72 h; osobom, których dotyczy, gdy ryzyko jest wysokie
NIS2 / ustawa o KSCfirma jest podmiotem kluczowym lub ważnym (m.in. ochrona zdrowia, energetyka, transport, infrastruktura cyfrowa), a incydent jest poważnyCSIRT: wczesne ostrzeżenie w 24 h, zgłoszenie w 72 h, raport końcowy w ciągu miesiąca

Te obowiązki są niezależne: przy wycieku danych osobowych w firmie objętej KSC zgłaszasz incydent i do CSIRT, i do UODO. Nowelizacja ustawy o KSC wdrażająca NIS2 obowiązuje w Polsce od 3 kwietnia 2026 r., a objęte nią podmioty mają czas do 3 kwietnia 2027 r. na dostosowanie systemów i procedur. Czy i jako jaki podmiot firma jej podlega, ocenia prawnik lub zespół compliance.

Cheat sheet: checklista do wydruku

Przejdź ją raz na kwartał dla każdego serwera i każdej aplikacji. Ta sama lista jest do pobrania jako jednostronicowy PDF pod tytułem artykułu.

Serwer i sieć

  • Skan nmap -p- z zewnątrz pokazuje tylko 80 i 443
  • Firewall domyślnie blokuje ruch przychodzący, także w panelu chmury
  • Żaden ports: w Docker Compose nie publikuje bazy, Redisa ani paneli na 0.0.0.0
  • SSH tylko z kluczy, root wyłączony, dostęp przez VPN lub bastion
  • Włączone automatyczne poprawki bezpieczeństwa, obrazy przebudowane w ostatnim miesiącu
  • Panele administracyjne (phpMyAdmin, Horizon, Telescope, Grafana) niedostępne publicznie

Aplikacja

  • APP_ENV=production, APP_DEBUG=false
  • Document root wskazuje public/; /.env i /.git/ zwracają 404
  • Każdy endpoint z identyfikatorem ma Policy i test na 403 dla obcego użytkownika
  • Zapis modeli tylko z validated(), modele mają $fillable
  • Brak whereRaw/DB::raw z danymi z requestu bez bindowania
  • Rate limiting na logowaniu, resecie hasła, rejestracji i API
  • Uploady walidowane, na dysku prywatnym, bez wykonywania PHP
  • Nagłówki HSTS, CSP, nosniff, frame-ancestors ustawione
  • Cookies sesji z Secure, HttpOnly, SameSite

CI/CD i zależności

  • composer audit i npm audit w każdym pull requeście
  • Dependabot lub Renovate włączony
  • Skaner sekretów blokuje commity z kluczami
  • Pipeline blokuje deploy z APP_DEBUG=true

Dane i backupy

  • Kopie 3-2-1, w tym jedna niezmienialna lub offline
  • Ostatni test pełnego odtworzenia: nie starszy niż kwartał
  • Wrażliwe pola szyfrowane w bazie
  • Lista sekretów i procedura ich rotacji spisana

Wykrywanie i reakcja

  • Logi z aplikacji, serwera WWW i systemu w jednym miejscu
  • Alerty: nieudane logowania, wrażliwe ścieżki, SSH spoza VPN, nowy port
  • Codzienny automatyczny skan portów z zewnątrz
  • Plan reakcji na incydent spisany, z kontaktami i terminami zgłoszeń (UODO 72 h, CSIRT 24 h)
  • Wiadomo, kto w zespole decyduje w trakcie incydentu

Zapobieganie zamiast bohaterstwa

Nie musisz wdrażać wszystkiego naraz. Kolejność, która daje najwięcej najmniejszym kosztem:

  1. Skan z zewnątrz i zamknięcie wszystkiego poza 80 i 443.
  2. SSH tylko z kluczy i przez VPN.
  3. APP_DEBUG=false, poprawny document root, composer audit w CI.
  4. Policies i testy autoryzacji na każdym endpoincie z identyfikatorem.
  5. Backup 3-2-1 z testem odtworzenia.
  6. Centralne logi, codzienny diff portów i cztery kolejne podstawowe alerty.

A u Ciebie? Który z tych 12 błędów widziałeś na produkcji? Napisz w komentarzu. Mnie najbardziej zaskakuje, jak często wraca punkt z Dockerem.

Jeśli wolisz, żeby ktoś przeszedł tę checklistę na Twoich serwerach i w Twoim kodzie, albo jesteś w trakcie incydentu i potrzebujesz drugiej pary rąk, to praca, którą realizuję dla firm i software house'ów. Napisz do mnie.

Źródła i dalsza lektura

Powiązane artykuły

Wsparcie istniejącego systemu

Potrzebujesz pomocy z działającą aplikacją?

Pomagam firmom rozwijać działające systemy, porządkować wdrożenia i dodawać nowe funkcje bez dokładania chaosu do projektu.

Komentarze (0)
Zaloguj się, aby dodać komentarz

Musisz być zalogowany, aby dodać komentarz.

Zaloguj się

Potrzebujesz kogoś, kto weźmie odpowiedzialność za kolejny krok?

Porozmawiajmy o Twoim projekcie i określmy zakres, który ma sens dla Twoich celów.