Bezpieczeństwo strony internetowej: 6 warstw ochrony (2026)

Advertisement
Czym właściwie jest bezpieczeństwo strony internetowej
Bezpieczeństwo strony internetowej to w praktyce zestaw zabezpieczeń, które chronią stronę lub aplikację webową przed przejęciem, podmianą treści, wykorzystaniem jej do atakowania własnych użytkowników albo cichym wyciekiem danych. Nie jest to jeden produkt ani jedno ustawienie. To cały stos decyzji, który zaczyna się u rejestratora domeny, a dalej obejmuje DNS, TLS, nagłówki HTTP wysyłane przez serwer, kod obsługujący logowanie i dane wejściowe, zewnętrzne pakiety, na których budujesz aplikację, sam serwer, a na końcu logi, które powiedzą Ci, że coś poszło nie tak.
Warto myśleć warstwami, bo tak właśnie myślą atakujący. Raport Verizon 2026 Data Breach Investigations Report wykazał, że 31% naruszeń zaczyna się dziś od wykorzystania podatności w oprogramowaniu — po raz pierwszy wyprzedziło to skradzione dane logowania jako najczęstszą drogę wejścia — oraz że 48% wszystkich naruszeń wiąże się z ransomware. Wystarczy jedna brakująca poprawka, jeden wyciekły klucz API albo panel administracyjny bez limitowania żądań. Żadne z zabezpieczeń opisanych w tym przewodniku nie jest egzotyczne; włamania zdarzają się zwykle na stronach, które dopracowywały jedną warstwę, a pominęły podstawy na innej.
Przewodnik jest uporządkowany od zewnątrz do środka. Przy każdej praktyce znajdziesz powód, dla którego jest ważna, dokładną konfigurację i sposób weryfikacji za pomocą polecenia lub darmowego narzędzia — bo najczęstszy problem, jaki widzimy przy sprawdzaniu nagłówków HTTP i sprawdzaniu certyfikatów SSL, to nie zła decyzja, lecz ustawienie, które ktoś uważał za włączone i nigdy tego nie potwierdził.
Stos bezpieczeństwa strony: sześć warstw do ochrony
Każdy atak na stronę internetową trafia w jedną z sześciu warstw. Poniższa tabela to mapa reszty przewodnika: co znajduje się na każdej warstwie, jak zwykle jest atakowane i które darmowe narzędzie potwierdzi, że Twoja ochrona faktycznie działa.
| Warstwa | Co robią tu atakujący | Kluczowe praktyki | Jak sprawdzić |
|---|---|---|---|
| 1. Domena i DNS | Przejmują domenę, zmieniają serwery nazw, przejmują porzucone subdomeny, uzyskują nieuprawnione certyfikaty | Registrar lock, MFA, DNSSEC, CAA, audyt nieaktualnych rekordów | Wyszukiwanie WHOIS, Wyszukiwanie DNS, Wyszukiwarka Subdomen |
| 2. Transport (TLS) | Wymuszają powrót do HTTP, usuwają szyfrowanie, wykorzystują stare szyfry i wygasłe certyfikaty | TLS 1.2+, HSTS z preload, automatyczne odnawianie | Sprawdzanie Certyfikatu SSL |
| 3. Nagłówki HTTP | Wstrzykują skrypty (XSS), osadzają stronę w ramce do clickjackingu, wykorzystują zgadywanie typów treści, wyciągają adresy z nagłówka Referer | CSP, frame-ancestors, nosniff, Referrer-Policy, Permissions-Policy | Sprawdzanie Nagłówków HTTP |
| 4. Aplikacja | Credential stuffing, injection, błędy kontroli dostępu, CSRF, niebezpieczne przesyłanie plików | Argon2id + MFA + limity żądań, zapytania parametryzowane, autoryzacja po stronie serwera, ciasteczka SameSite | Przegląd kodu, OWASP ZAP, Test Siły Hasła |
| 5. Zależności | Zatrute pakiety, znane CVE w bibliotekach, przejęte potoki CI | Lockfile, audyty, przypięte wersje, SBOM, tokeny z minimalnymi uprawnieniami | npm audit, Dependabot, Trivy |
| 6. Serwer i utrzymanie | Otwarte porty, domyślne hasła, brak poprawek, brak kopii zapasowych, brak logów | Firewall, SSH tylko z kluczami, regularne łatanie, kopie zapasowe 3-2-1, centralne gromadzenie logów | Sprawdzanie Portów, Sprawdzanie Czarnej Listy IP, Sprawdzanie Kondycji Domeny |
Advertisement
Warstwa 1: zabezpiecz domenę i DNS
Wszystko inne w tym przewodniku zakłada, że nadal kontrolujesz swoją domenę. Jeśli atakujący zaloguje się na Twoje konto u rejestratora albo zmieni serwery nazw, może skierować Twój ruch dokądkolwiek, uzyskać ważny certyfikat na Twoją domenę i czytać każdy e-mail wysłany do Ciebie — a kod Twojej aplikacji nawet się nie uruchomi. Dlatego bezpieczeństwo domeny i DNS to pierwsza z dobrych praktyk bezpieczeństwa stron i niemal w całości sprowadza się do konfiguracji.
Zacznij od wyszukiwania WHOIS dla własnej domeny i potwierdź trzy rzeczy: kody statusu zawierają clientTransferProhibited, data wygaśnięcia jest odległa o ponad rok, a rejestrator to firma, w której naprawdę masz konto. Zaskakująco często domena firmy wisi na koncie byłego podwykonawcy. Nasz przewodnik po WHOIS wyjaśnia każdy kod statusu, jaki zobaczysz.
Registrar lock, MFA i alerty o wygaśnięciu
Włącz registrar lock (nazywany też blokadą transferu), aby domeny nie dało się przenieść do innego rejestratora, dopóki jej wyraźnie nie odblokujesz, i włącz uwierzytelnianie wieloskładnikowe (MFA) na samym koncie u rejestratora. Konta u rejestratorów to ulubiony cel phishingu właśnie dlatego, że jedno logowanie kontroluje wszystko, co od niego zależy. Tam, gdzie rejestrator na to pozwala, używaj klucza sprzętowego lub aplikacji uwierzytelniającej zamiast SMS-ów.
Ustaw automatyczne odnawianie domeny z metodą płatności, która nie wygaśnie, a mimo to dodaj przypomnienie w kalendarzu 60 dni przed datą wygaśnięcia. Wygasłe domeny są przechwytywane przez usługi drop-catch w ciągu kilku godzin, a nowy właściciel przejmuje Twoją pocztę, Twój ruch i wszystkie logowania OAuth, które ufają Twojej domenie.
Registry lock (ręczna blokada na poziomie rejestru, zdejmowana poza zwykłym panelem) jest warta dodatkowej opłaty w przypadku domeny, od której zależy biznes; uniemożliwia zmianę serwerów nazw nawet z przejętego konta u rejestratora.
Rozdziel role. Osoba, która płaci za domenę, konto, na którym domena jest zarejestrowana, i dostawca DNS powinny być udokumentowane i nie mogą być powiązane ze skrzynką pocztową jednego pracownika.
Włącz ochronę prywatności WHOIS, aby Twoje dane kontaktowe nie stały się mapą dla phisherów, i podaj na koncie monitorowany adres funkcyjny, np.
domains@.
Podpisz strefę za pomocą DNSSEC
DNSSEC podpisuje strefę DNS, dzięki czemu resolver może wykryć sfałszowane odpowiedzi. Bez niego atakujący, który zatruje pamięć podręczną resolvera, może skierować Twoich odwiedzających na fałszywy serwer, a przeglądarka nadal będzie pokazywać nazwę Twojej domeny. Większość zarządzanych dostawców DNS (Cloudflare, Route 53, Google Cloud DNS i wielu rejestratorów) włącza go jednym przełącznikiem; jedynym ręcznym krokiem jest opublikowanie rekordu DS u rejestratora. Sprawdź go poleceniem dig albo testem DNSSEC w Sprawdzaniu Kondycji Domeny:
dig +dnssec +short yourdomain.com A
# A signed zone returns the record AND an RRSIG line:
# 203.0.113.10
# A 13 2 3600 20261015000000 20260924000000 34505 yourdomain.com. Kx3f...==
dig +short yourdomain.com DS
# A DS record at the parent zone proves the chain of trust is complete
# 34505 13 2 6A1B...C9Ogranicz wystawianie certyfikatów rekordem CAA
Rekord CAA (RFC 8659) informuje urzędy certyfikacji (CA), które z nich mogą wystawiać certyfikaty dla Twojej domeny. Każdy publiczny CA musi go sprawdzić przed wystawieniem certyfikatu, więc rekord CAA zamyka drogę atakującemu, który oszukał walidację domeny w innym CA. Trzy rekordy obejmują typowe przypadki: kto może wystawiać certyfikaty, kto może wystawiać certyfikaty wildcard i dokąd zgłaszać odrzucone żądania:
yourdomain.com. CAA 0 issue "letsencrypt.org"
yourdomain.com. CAA 0 issuewild ";"
yourdomain.com. CAA 0 iodef "mailto:security@yourdomain.com"Audyt nieaktualnych rekordów: przejęcie subdomeny
Do przejęcia subdomeny (subdomain takeover) dochodzi, gdy rekord DNS wciąż wskazuje usługę, z której już nie korzystasz: CNAME do usuniętej strony GitHub Pages, aplikacji Azure, bucketu S3 albo hosta dostawcy SaaS. Każdy może zarejestrować porzucony cel i serwować treści pod old-app.yourdomain.com w Twoim imieniu, razem z ważnym certyfikatem. To jedno z najczęstszych zgłoszeń w programach bug bounty, bo nikt nie pamięta, że taki rekord w ogóle istnieje.
Sprawdź swoją domenę w Wyszukiwarce Subdomen, która odczytuje logi Certificate Transparency, dzięki czemu zobaczysz każdą nazwę hosta, dla której kiedykolwiek wystawiono certyfikat. Następnie rozwiąż każdą z nich za pomocą wyszukiwania DNS i usuń każdy rekord, którego cel zwraca NXDOMAIN albo stronę dostawcy z komunikatem „no such app”. Powtarzaj to co kwartał, a usuwanie rekordu DNS uczyń stałym elementem wyłączania każdej usługi.
Warstwa 2: HTTPS i TLS skonfigurowane porządnie
HTTPS nie jest już dobrą praktyką, tylko absolutnym minimum — przeglądarki oznaczają zwykłe strony HTTP jako „Niezabezpieczona”. To, co wciąż odróżnia bezpieczną stronę od strony jedynie szyfrowanej, to akceptowane wersje TLS, to, czy do Twojej strony da się w ogóle dotrzeć przez HTTP, oraz to, czy odnawianie certyfikatów jest na tyle zautomatyzowane, by przetrwać skracanie ich ważności, które zaczęło się w marcu 2026 roku.
Advertisement
TLS 1.2 jako minimum, TLS 1.3 jako preferowany
TLS 1.0 i 1.1 zostały formalnie wycofane przez RFC 8996 w 2021 roku i żadna aktualna przeglądarka ich nie negocjuje. Wyłącz je na serwerze, preferuj TLS 1.3 (który eliminuje wszystkie znane słabe szyfry i kończy handshake w jednym przejściu tam i z powrotem) i zostaw TLS 1.2 wyłącznie z zestawami szyfrów AEAD, takimi jak ECDHE-ECDSA-AES128-GCM-SHA256 lub ECDHE-RSA-CHACHA20-POLY1305.
Podczas testu z zewnątrz liczą się dwie linie: wynegocjowany protokół oraz Verify return code: 0, co oznacza, że cały łańcuch certyfikatów przeszedł walidację. Brakujący certyfikat pośredni to najczęstsza przyczyna zgłoszeń typu „w Chrome działa, a w curl i na Androidzie nie”; nasz przewodnik po łańcuchu certyfikatów SSL pokazuje, jak to naprawić, a Sprawdzanie Certyfikatu SSL od razu oznacza niekompletny łańcuch. Oto test wykonany dla dnsrobot.net:
echo | openssl s_client -connect dnsrobot.net:443 -servername dnsrobot.net 2>/dev/null \
| grep -E 'Protocol|Cipher|Verify return'
# Protocol : TLSv1.3
# Cipher : TLS_AES_256_GCM_SHA384
# Verify return code: 0 (ok)
# Confirm the old versions are refused:
openssl s_client -connect dnsrobot.net:443 -tls1_1 2>&1 | grep -iE 'alert|no protocols'HSTS: przeglądarka już nigdy nie użyje HTTP
Samo przekierowanie z HTTP na HTTPS nie wystarczy. Pierwsze żądanie, jakie odwiedzający wysyła do http://yourdomain.com, wciąż wędruje otwartym tekstem, a atakujący w tej samej sieci może na nie odpowiedzieć, zanim zrobi to Twoje przekierowanie. HTTP Strict Transport Security (RFC 6797) rozwiązuje ten problem: gdy przeglądarka raz zobaczy ten nagłówek, każdy kolejny adres http:// Twojej domeny przepisuje na https://, zanim cokolwiek wyśle.
Ustaw max-age na co najmniej rok (31536000 sekund), dodaj includeSubDomains, gdy każda subdomena będzie obsługiwać HTTPS, a następnie zgłoś domenę na hstspreload.org, aby trafiła na listę wbudowaną na stałe w Chrome, Firefoksie, Safari i Edge. Preloading całkowicie zamyka lukę przy pierwszej wizycie. Sprawdź nagłówek w Sprawdzaniu Nagłówków HTTP, które ocenia HSTS w ramach oceny bezpieczeństwa w skali od A do F.
Strict-Transport-Security: max-age=31536000; includeSubDomains; preloadCertyfikaty 47-dniowe: zautomatyzuj odnawianie już teraz
Głosowanie SC-081 organizacji CA/Browser Forum etapami skraca maksymalny okres ważności każdego publicznego certyfikatu TLS. Pierwsze cięcie weszło w życie 15 marca 2026 roku, więc każdy certyfikat kupiony lub odnowiony dziś jest już ograniczony do 200 dni, a do 2029 roku certyfikat trzeba będzie wymieniać mniej więcej co sześć tygodni:
| Data wejścia w życie | Maksymalna ważność certyfikatu | Ponowne użycie walidacji domeny |
|---|---|---|
| Przed 15 marca 2026 | 398 dni | 398 dni |
| 15 marca 2026 | 200 dni | 200 dni |
| 15 marca 2027 | 100 dni | 100 dni |
| 15 marca 2029 | 47 dni | 10 dni |
Ręczne odnawianie nie wytrzyma tego harmonogramu. Dobra praktyka to w pełni zautomatyzowane wystawianie certyfikatów przez ACME: Let's Encrypt lub Google Trust Services za pomocą certbota, acme.sh, Caddy albo automatycznych certyfikatów wbudowanych w Cloudflare, Vercel, Netlify i większość paneli hostingowych. Przetestuj ścieżkę odnawiania, zanim będzie potrzebna (certbot renew --dry-run w przypadku certbota); szukaj przede wszystkim rekordu CAA lub reguły firewalla dodanych od ostatniego odnowienia, które teraz blokują walidację. Niezależnie od narzędzia monitoruj datę wygaśnięcia także z zewnątrz: Sprawdzanie Certyfikatu SSL pokazuje liczbę pozostałych dni, a wygasły certyfikat zamienia każdą wizytę w pełnoekranowe ostrzeżenie przeglądarki.
Warstwa 3: nagłówki bezpieczeństwa HTTP
Nagłówki bezpieczeństwa to instrukcje, które serwer przekazuje przeglądarce: co wolno, a czego nie wolno jej robić z Twoimi stronami — które skrypty mogą się uruchomić, czy stronę można osadzić w ramce, czy przeglądarka może zgadywać typy treści i co ma przekazywać innym witrynom o tym, skąd przyszedł odwiedzający. Nic nie kosztują, działają od razu na każdej podstronie i blokują całe klasy ataków. To jednocześnie warstwa, którą większość witryn konfiguruje tylko częściowo poprawnie, i dlatego Sprawdzanie Nagłówków HTTP wystawia jej ocenę. Oto, co wysyła dnsrobot.net (tylko nagłówki bezpieczeństwa):
curl -sI https://dnsrobot.net/ | grep -iE 'strict-transport|content-security|x-frame|x-content-type|referrer-policy|permissions-policy'
strict-transport-security: max-age=31536000; includeSubDomains; preload
content-security-policy: default-src 'self'; script-src 'self' ... https://*.googletagmanager.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'
x-frame-options: DENY
x-content-type-options: nosniff
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(self), microphone=(), geolocation=(), payment=(), usb=()Siedem nagłówków, które powinna wysyłać każda strona
To wartości, do których warto dążyć na typowej stronie. Pierwsze pięć wpływa na ocenę; ostatnie dwa to tanie dodatki, gdy pozostałe są już na miejscu.
| Nagłówek | Zalecana wartość | Co blokuje |
|---|---|---|
Strict-Transport-Security | max-age=31536000; includeSubDomains; preload | Downgrade protokołu, kradzież ciasteczek przez HTTP |
Content-Security-Policy | script-src oparty na nonce z 'strict-dynamic'; object-src 'none'; base-uri 'none'; frame-ancestors 'none' | Cross-site scripting (XSS), wstrzykiwanie danych, clickjacking |
X-Content-Type-Options | nosniff | MIME sniffing, który zamienia przesłany plik w wykonywalny skrypt |
Referrer-Policy | strict-origin-when-cross-origin | Wyciek pełnych adresów URL (tokenów, wyszukiwanych fraz) do stron trzecich |
Permissions-Policy | camera=(), microphone=(), geolocation=(), payment=() | Ciche korzystanie z potężnych API przeglądarki przez zewnętrzne skrypty |
X-Frame-Options | DENY (starszy zamiennik dla frame-ancestors) | Clickjacking w przeglądarkach bez CSP Level 2 |
Cross-Origin-Opener-Policy | same-origin | Ataki między oknami przez window.opener |
Content Security Policy bez psucia strony
Content Security Policy to najskuteczniejsza pojedyncza obrona przed XSS, bo blokuje wstrzyknięte skrypty nawet wtedy, gdy błąd umożliwiający wstrzyknięcie istnieje. Haczyk polega na tym, że restrykcyjna polityka psuje każdy skrypt inline i każdy zewnętrzny tag, o którym zapomniano. Nowoczesne podejście, zalecane przez Google i opisane w MDN, to polityka oparta na nonce: serwer generuje losowy nonce dla każdej odpowiedzi, dodaje go do każdego tagu script, który celowo renderuje, a przeglądarka odrzuca wszystko inne.
'strict-dynamic' sprawia, że taka polityka sprawdza się w praktyce: skrypt z nonce może ładować kolejne skrypty (analitykę, menedżery tagów, widżety) bez dodawania każdego z nich do listy dozwolonych, a tokeny https: i 'unsafe-inline' są ignorowane przez nowoczesne przeglądarki i służą jedynie jako fallback dla starych. Zacznij wdrożenie od Content-Security-Policy-Report-Only, obserwuj raporty o naruszeniach przez tydzień, a potem przełącz się na tryb egzekwowania. Frameworki takie jak Next.js, Rails i Django mają wbudowaną obsługę nonce; strony statyczne mogą zamiast tego użyć script-src opartego na hashach.
Content-Security-Policy:
script-src 'nonce-r4nd0m1z3dV4lu3' 'strict-dynamic' https: 'unsafe-inline';
object-src 'none';
base-uri 'none';
frame-ancestors 'none';
form-action 'self';
report-to csp-endpoint
<!-- Only scripts carrying the nonce run; strict-dynamic lets them load their own dependencies -->
<script nonce="r4nd0m1z3dV4lu3" src="/app.js"></script>Clickjacking: frame-ancestors i X-Frame-Options
Clickjacking polega na niewidocznym załadowaniu Twojej strony wewnątrz strony atakującego i nakłonieniu odwiedzającego do kliknięcia przycisku, którego nie widzi. frame-ancestors 'none' w CSP (lub 'self', jeśli osadzasz własne strony) zapobiega temu we wszystkich aktualnych przeglądarkach, a X-Frame-Options: DENY chroni starsze. Jeśli celowo udostępniasz widżet do osadzania, zwolnij z tej reguły tylko jego ścieżkę i tylko dla originów, które go potrzebują; nasz przewodnik po X-Frame-Options omawia dokładne reguły serwera i błędy „refused to connect”, na które trafisz podczas testów.
nosniff, Referrer-Policy, Permissions-Policy i COOP
X-Content-Type-Options: nosniff nie pozwala przeglądarce ignorować nagłówka Content-Type i zgadywać typu treści — a właśnie takie zgadywanie zamienia przesłany przez użytkownika „obrazek” zawierający HTML w wykonywalną stronę. Referrer-Policy: strict-origin-when-cross-origin (domyślna wartość w aktualnych przeglądarkach, ale ustaw ją jawnie) wysyła innym witrynom tylko Twój origin, więc tokeny resetu hasła i wyszukiwane frazy w adresach URL pozostają prywatne. Permissions-Policy wyłącza funkcje przeglądarki, z których nie korzystasz, więc przejęty zewnętrzny skrypt nie włączy kamery ani nie odczyta lokalizacji. Cross-Origin-Opener-Policy: same-origin zrywa powiązanie window.opener ze stronami, które otwierasz, co blokuje całą klasę ataków między oknami.
Liczą się też nagłówki, które należy usunąć: Server i X-Powered-By zdradzają skanerom podatności dokładne wersje oprogramowania, a X-XSS-Protection jest przestarzały i w starych przeglądarkach może powodować błędy, więc ustaw go na 0 albo całkowicie usuń. Wykrywanie CMS pokazuje, czego skaner dowiaduje się o Twoim stosie technologicznym z samych nagłówków.
Warstwa 4: uwierzytelnianie odporne na credential stuffing
Atakujący rzadko zgadują hasła po jednym. Odtwarzają miliardy par e-mail–hasło, które wyciekły z innych serwisów (credential stuffing), a resztę wyłudzają phishingiem. Dobre praktyki uwierzytelniania mają więc trzy części: przechowuj hasła tak, aby wyciek bazy danych nie oznaczał wycieku haseł, spraw, aby samo skradzione hasło było bezużyteczne, i spowolnij ataki brute force na tyle, żeby przestały mieć znaczenie. OWASP umieszcza Authentication Failures (błędy uwierzytelniania) na pozycji A07 w OWASP Top 10:2025.
Advertisement
Hashuj hasła za pomocą Argon2id lub bcrypt
Nigdy nie przechowuj hasła ani jego szybkiego hasha. MD5, SHA-1, a nawet SHA-256 zaprojektowano z myślą o szybkości, więc wyciekłą tabelę takich hashy można testować z prędkością miliardów prób na sekundę na jednym GPU. Używaj wolnej, pamięciochłonnej funkcji hashującej przeznaczonej do haseł z unikalną solą dla każdego hasła. OWASP Password Storage Cheat Sheet zaleca, w kolejności:
Argon2id z co najmniej 19 MiB pamięci, 2 iteracjami i stopniem równoległości 1 (lub 46 MiB przy 1 iteracji).
scrypt z N = 2^17, r = 8, p = 1, gdy Argon2 jest niedostępny.
bcrypt ze współczynnikiem pracy (work factor) 10 lub wyższym (pamiętaj o limicie 72 bajtów danych wejściowych; wstępne hashowanie dłuższych haseł wymaga ostrożności).
PBKDF2-HMAC-SHA256 z 600 000 iteracji tylko tam, gdzie wymaga tego zgodność z FIPS.
// Node.js with the argon2 package
import argon2 from "argon2";
const hash = await argon2.hash(password, {
type: argon2.argon2id,
memoryCost: 19456, // KiB = 19 MiB
timeCost: 2,
parallelism: 1,
});
// Later, on login:
const ok = await argon2.verify(hash, submittedPassword);Zasady dotyczące haseł, które naprawdę pomagają (NIST SP 800-63B)
Reguły złożoności, które większość stron wciąż wymusza (jedna wielka litera, jeden symbol, zmiana co 90 dni), prowadzą do przewidywalnych haseł w stylu Summer2026! i skłaniają ludzi do ich ponownego używania. Aktualne wytyczne NIST SP 800-63B zastępują je regułami, które mierzą to, co naprawdę ważne:
Minimum 8 znaków, gdy wymagane jest również MFA, i co najmniej 15 znaków dla hasła używanego samodzielnie; dopuszczaj co najmniej 64 znaki.
Żadnych reguł złożoności i żadnej wymuszonej okresowej zmiany; wymagaj zmiany hasła tylko wtedy, gdy istnieją dowody jego przejęcia.
Sprawdzaj każde nowe hasło z listą haseł, które wyciekły (API zakresów Have I Been Pwned pozwala to zrobić bez wysyłania hasła) i odrzucaj te, które już wyciekły.
Pozwalaj na wklejanie i menedżery haseł, akceptuj spacje i znaki Unicode oraz pokazuj wskaźnik siły hasła oparty na entropii, a nie na regułach.
Nasz Test Siły Hasła pokazuje, czym te miary różnią się od starych reguł: szacuje czas złamania na podstawie entropii i wykrywania wzorców oraz sprawdza hasło z danymi z wycieków, co daje znacznie bardziej przydatną informację zwrotną niż czerwony komunikat „wymagany symbol”.
MFA i passkeys
Uwierzytelnianie wieloskładnikowe sprawia, że skradzione hasło prowadzi donikąd. Oferuj je wszystkim i wymagaj go od ról administracyjnych, finansowych i wsparcia. Uszereguj opcje według odporności na phishing: passkeys i klucze bezpieczeństwa FIDO2 nie dają się wyłudzić phishingiem, bo poświadczenie jest powiązane z Twoim prawdziwym originem; aplikacje uwierzytelniające (TOTP) są dobre; kody SMS są lepsze niż nic, ale można je przechwycić przez SIM swapping. Passkeys są obsługiwane we wszystkich aktualnych przeglądarkach i systemach operacyjnych i całkowicie eliminują hasło, a wraz z nim credential stuffing jako kategorię ataku.
Chroń ścieżkę odzyskiwania konta równie starannie jak logowanie: kody odzyskiwania przechowywane jako hashe, resety przez e-mail z jednorazowymi tokenami, które wygasają w ciągu 15 minut, i żadnych pytań pomocniczych.
Limitowanie żądań, blokady i ochrona przed botami
Stosuj rate limiting na endpointach logowania, rejestracji, resetu hasła i MFA — dla każdego adresu IP i dla każdego konta — i po przekroczeniu limitu zwracaj 429 Too Many Requests z nagłówkiem Retry-After (nasz przewodnik po błędzie HTTP 429 opisuje, jak powinni reagować klienci). Połącz to z rosnącym opóźnieniem lub tymczasową blokadą po powtarzających się nieudanych próbach na jednym koncie oraz z CAPTCHA lub wyzwaniem proof-of-work na endpointach narażonych na rozproszone ataki. Zapisuj w logach każde nieudane logowanie wraz ze źródłowym adresem IP, aby wzorzec ataku credential stuffing był widoczny w ciągu minut, a nie miesięcy.
# nginx: 5 login attempts per minute per client IP
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
location = /api/login {
limit_req zone=login burst=4 nodelay;
limit_req_status 429;
proxy_pass http://app;
}Sesje, ciasteczka i CSRF
Po zalogowaniu ciasteczko sesji jest użytkownikiem, więc zasługuje na taką samą ochronę jak hasło. Większość pracy wykonują trzy atrybuty ciasteczka i jeden prefiks nazwy:
Secureoznacza, że ciasteczko nigdy nie jest wysyłane przez zwykłe HTTP, więc nie da się go podsłuchać w publicznej sieci Wi-Fi.HttpOnlysprawia, że ciasteczko jest niewidoczne dla JavaScriptu, więc błąd XSS nie pozwoli go odczytać.SameSite=Lax(lubStrictdla paneli administracyjnych) sprawia, że ciasteczko nie jest dołączane do międzywitrynowych żądań POST, co neutralizuje większość ataków CSRF.Prefiks
__Host-sprawia, że przeglądarka akceptuje ciasteczko tylko wtedy, gdy ma ono atrybutSecure,Path=/i nie ma atrybutuDomain, więc subdomena nie może go nadpisać.
Set-Cookie: __Host-session=9f1c7e2a...; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=28800CSRF: druga linia obrony za SameSite
Cross-site request forgery (CSRF) nakłania zalogowaną przeglądarkę do wysłania z innej strony żądania, które zmienia stan w Twoim serwisie. Ciasteczka SameSite powstrzymują typowy przypadek, ale na każdym żądaniu innym niż GET utrzymuj drugą linię obrony: token anty-CSRF przypisany do sesji w ukrytym polu lub własnym nagłówku albo sprawdzenie, czy nagłówek Origin (lub Sec-Fetch-Site) odpowiada Twojemu originowi. Większość frameworków (Django, Rails, Laravel, server actions w Next.js) robi to domyślnie; błąd polega na wyłączeniu tej ochrony dla endpointu API, a potem wywoływaniu go z przeglądarki.
Identyfikatory sesji, rotacja i JWT
Generuj identyfikatory sesji z co najmniej 128 bitami losowości, zmieniaj identyfikator przy logowaniu i przy każdej zmianie uprawnień, aby udaremnić session fixation, wygaszaj sesje po okresie bezczynności i daj użytkownikom przycisk „wyloguj ze wszystkich urządzeń”, który unieważnia je po stronie serwera. Tokeny JWT niech będą krótkotrwałe (minuty, z refresh tokenem, który można odwołać); nigdy nie przechowuj ich w localStorage, gdzie może je odczytać dowolny skrypt, a wyciek klucza podpisującego traktuj jak pełne naruszenie bezpieczeństwa, bo każdy token, który ten klucz kiedykolwiek podpisał, można teraz podrobić.
Injection i XSS: waliduj dane wejściowe, koduj dane wyjściowe
Injection (A05:2025, wstrzykiwanie kodu) i cross-site scripting to ten sam błąd w różnych miejscach: dane od użytkownika trafiają do interpretera (SQL, powłoki, zapytania LDAP, strony HTML) tak, jakby były kodem. Rozwiązanie też jest wszędzie takie samo: oddzielaj dane od kodu i nigdy nie buduj polecenia przez sklejanie stringów.
Advertisement
Zapytania parametryzowane zamiast sklejania stringów
W przypadku SQL używaj prepared statements lub ORM, który je generuje; baza danych traktuje wtedy dane wejściowe jako wartość, która nigdy nie zmieni struktury zapytania. Ta sama zasada dotyczy poleceń powłoki (przekazuj tablicę argumentów, nigdy string), filtrów NoSQL (odrzucaj w danych od użytkownika obiekty operatorów, takie jak {"$gt": ""}) i ścieżek plików (rozwiąż ścieżkę i upewnij się, że pozostaje w docelowym katalogu).
// Vulnerable: the input becomes part of the query text
const rows = await db.query(`SELECT * FROM users WHERE email = '${email}'`);
// Safe: the driver sends the value separately from the query
const rows = await db.query("SELECT * FROM users WHERE email = $1", [email]);
// Python / psycopg: same idea
cur.execute("SELECT * FROM users WHERE email = %s", (email,))Kodowanie danych wyjściowych powstrzymuje XSS
Do cross-site scriptingu dochodzi, gdy tekst kontrolowany przez użytkownika trafia na stronę bez zakodowania go odpowiednio do kontekstu, w którym się znajdzie. Nowoczesne silniki szablonów (React i JSX, Vue, Jinja2, Blade, ERB) domyślnie kodują dane; dziś XSS bierze się zwykle z furtek, które tę ochronę omijają: dangerouslySetInnerHTML, v-html, |safe, innerHTML oraz budowania adresów URL lub inline'owych handlerów zdarzeń z danych użytkownika. Koduj dane dokładnie pod dany kontekst, a rozbudowany HTML oczyszczaj specjalnie do tego stworzoną biblioteką, taką jak DOMPurify, a nie wyrażeniem regularnym.
| Gdzie trafiają dane | Jak kodować | Przykład |
|---|---|---|
| Treść HTML | Encje HTML | < zmienia się w <, a " w " |
| Atrybut HTML | Encje w atrybucie ujętym w cudzysłów | Zawsze ujmuj atrybut w cudzysłów; koduj " i ' |
| JavaScript | Nie wstrzykuj danych do treści skryptu; przekazuj je przez atrybuty data- lub blok JSON <script type="application/json"> | </script> wewnątrz stringa zamienia się w \u003c/script\u003e |
| Parametr URL | Kodowanie procentowe (percent-encoding) | encodeURIComponent(value) |
| Wartość CSS | Unikaj całkowicie; wybieraj nazwy klas z listy dozwolonych | Nigdy nie wstawiaj danych od użytkownika do style |
SSRF, przesyłanie plików i deserializacja
Server-side request forgery (SSRF): jeśli Twój serwer pobiera adres URL podany przez użytkownika (webhooki, proxy obrazów, podglądy linków), atakujący może skierować go na usługi wewnętrzne lub endpoint metadanych chmury 169.254.169.254 i odczytać poświadczenia. Najpierw rozwiąż nazwę hosta, odrzucaj zakresy prywatne i link-local, wyłącz przekierowania i umieść mechanizmy pobierające w segmencie sieci, z którego nie da się dotrzeć do niczego wewnętrznego.
Przesyłanie plików: sprawdzaj typ pliku na podstawie jego zawartości, a nie rozszerzenia czy nagłówka Content-Type wysłanego przez klienta; egzekwuj limit rozmiaru; przechowuj pliki poza katalogiem głównym serwera WWW lub w object storage pod losową nazwą, którą sam generujesz; a jeśli to możliwe, serwuj je z osobnego originu z nosniff i Content-Disposition: attachment. Nigdy nie pozwól, aby przesłany plik trafił tam, gdzie serwer WWW go wykona.
Deserializacja i mass assignment: nigdy nie deserializuj niezaufanych danych w formacie, który może tworzyć instancje dowolnych klas (natywna serializacja Javy, pickle w Pythonie, unserialize w PHP, YAML z niestandardowymi tagami); używaj JSON ze schematem. Wiąż treść żądania z jawną listą dozwolonych pól, aby użytkownik nie mógł wysłać POST-em "role": "admin" do modelu, który akurat ma taką kolumnę.
Broken Access Control: ryzyko numer jeden
Broken Access Control (błędy kontroli dostępu) zajmuje pierwsze miejsce w OWASP Top 10 od 2021 roku i utrzymuje je jako A01:2025. To nie pojedynczy błąd, lecz nawyk: przy każdym żądaniu sprawdza się, kim ktoś jest (uwierzytelnianie), ale zapomina się sprawdzić, do czego ma dostęp (autoryzacja). Klasyczna postać to insecure direct object reference (IDOR): GET /api/invoices/1042 działa dla właściciela faktury, ale też dla każdego, kto zmieni numer.
Domyślnie odmawiaj. Każda trasa wymaga jawnej reguły przyznającej dostęp; brak reguły oznacza 403, a nie 200.
Egzekwuj na serwerze, dla każdego obiektu. Ukrycie przycisku w interfejsie to nie kontrola dostępu. Każdy odczyt i zapis musi potwierdzać, że bieżący użytkownik jest właścicielem konkretnego rekordu lub ma do niego uprawnienia — również w endpointach zbiorczych, eksportach i zadaniach w tle.
Używaj nieprzewidywalnych identyfikatorów tam, gdzie to pomaga (UUID), ale nigdy na nich nie polegaj: ukrycie to nie autoryzacja.
Zablokuj CORS.
Access-Control-Allow-Origin: *z poświadczeniami albo odbijanie dowolnego nagłówkaOriginwysłanego w żądaniu oddaje Twoje API każdej stronie, którą odwiedza użytkownik.Wyłącz listowanie katalogów, blokuj
.git,.env, kopie zapasowe i pliki konfiguracyjne na poziomie serwera WWW, a panele administracyjne trzymaj poza publicznym internetem albo za MFA i listą dozwolonych adresów IP.Limituj i loguj błędy autoryzacji. Seria odpowiedzi 403 w jednej sesji oznacza trwający atak enumeracyjny.
// Express: ownership check on every object access
app.get("/api/invoices/:id", requireAuth, async (req, res) => {
const invoice = await Invoice.findById(req.params.id);
if (!invoice || invoice.ownerId !== req.user.id) {
return res.status(404).end(); // 404, not 403: don't confirm the record exists
}
res.json(invoice);
});Warstwa 5: zależności i łańcuch dostaw oprogramowania
Typowa aplikacja webowa to może 5% kodu napisanego przez Ciebie i 95% kodu pobranego z zewnątrz. Dlatego Software Supply Chain Failures (błędy łańcucha dostaw oprogramowania) debiutuje na pozycji A03 w OWASP Top 10 z 2025 roku i dlatego w DBIR 2026 wykorzystanie podatności wyprzedziło skradzione dane logowania. We wrześniu 2025 roku wystarczył jeden opiekun pakietów, który dał się złapać na phishing, by do chalk, debug i 16 innych pakietów npm — pobieranych łącznie ponad dwa miliardy razy w tygodniu — trafiło złośliwe oprogramowanie kradnące kryptowaluty; każdy projekt, który w tym czasie zainstalował nieprzypiętą wersję, automatycznie je pobrał.
Commituj lockfile (
package-lock.json,yarn.lock,pnpm-lock.yaml,poetry.lock,go.sum) i w CI instaluj zależności przeznpm ci, aby buildy były powtarzalne, a nowe wydanie upstream nie mogło wślizgnąć się bez przeglądu.Skanuj w sposób ciągły:
npm audit,pip-audit,bundler-audit, GitHub Dependabot lub Renovate do aktualizacji oraz skaner kontenerów, taki jak Trivy lub Grype, dla warstwy systemu operacyjnego.Opóźniaj aktualizacje niezwiązane z bezpieczeństwem o kilka dni (
minimumReleaseAgew Renovate), aby zatrute wydanie zwykle zostało wycofane, zanim je wdrożysz — ale poprawki bezpieczeństwa stosuj natychmiast.Przypinaj zewnętrzne akcje i skrypty w CI do SHA commita i używaj Subresource Integrity (
integrity="sha384-...") dla każdego skryptu ładowanego z publicznego CDN.Dawaj CI minimalne uprawnienia: tokeny tylko do odczytu tam, gdzie to możliwe, krótkotrwałe poświadczenia OIDC zamiast długowiecznych sekretów i prawa do publikacji ograniczone do zadania wydającego release.
Generuj SBOM (CycloneDX lub SPDX) podczas builda, aby po pojawieniu się kolejnego CVE klasy Log4Shell odpowiedzieć na pytanie „czy nas to dotyczy?” w kilka minut.
# Reproducible install + audit in CI
npm ci --ignore-scripts
npm audit --audit-level=high
# Pin a GitHub Action to a commit, not a floating tag
# uses: actions/checkout@v4 <- can change underneath you
# uses: actions/checkout@<full-commit-sha> # v4.2.2
# Subresource Integrity for a CDN script (hash from: openssl dgst -sha384 -binary lib.min.js | openssl base64 -A)
<script src="https://cdn.example.com/lib.min.js"
integrity="sha384-<base64-hash>"
crossorigin="anonymous"></script>Jeśli korzystasz z WordPressa lub innego CMS-a, te same zasady dotyczą wtyczek i motywów, od których zaczyna się większość włamań na CMS-y. Aktualizuj rdzeń i każdą wtyczkę, usuwaj te, których nie używasz, i sprawdź w Wykrywaniu CMS, czego skaner może się dowiedzieć o Twoich wersjach.
Warstwa 6: hardening serwera (porty, poprawki, sekrety, kopie zapasowe)
Warstwa aplikacji nie ma znaczenia, jeśli serwer pod nią przyjmuje logowanie hasłem przez SSH, udostępnia bazę danych na publicznym porcie albo nie był aktualizowany od chwili postawienia. Dobre praktyki serwerowe są nudne — i to właśnie tędy wchodzi ransomware.
Zamknij każdy port, którego nie potrzebujesz
Publiczny serwer WWW potrzebuje otwartych portów 80 i 443 oraz SSH dostępnego z Twojego zakresu adresów IP. Bazy danych (3306, 5432, 27017), Redis (6379), Elasticsearch (9200), panele administracyjne i endpointy metryk powinny nasłuchiwać tylko na localhost lub w sieci prywatnej. Po każdej zmianie sprawdzaj to z zewnątrz za pomocą Sprawdzania Portów, bo właśnie taki widok ma atakujący, a grupy zabezpieczeń w chmurze łatwo źle odczytać.
# Ubuntu: default-deny firewall, allow only web + SSH from your office range
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow from 203.0.113.0/24 to any port 22 proto tcp
sudo ufw enable
# SSH: keys only, no root password login
sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
sudo systemctl reload sshŁataj regularnie, trzymaj sekrety poza kodem
Włącz automatyczne aktualizacje bezpieczeństwa systemu operacyjnego, zapisz się na listy bezpieczeństwa swojego środowiska uruchomieniowego i frameworka, a krytyczne CVE w czymkolwiek dostępnym z internetu traktuj jako zadanie na ten sam dzień. Uruchamiaj usługi jako użytkownicy bez uprawnień, w kontenerach lub z sandboxingiem systemd, aby przejęty proces nie mógł odczytać całej maszyny.
Sekrety (hasła do baz danych, klucze API, klucze podpisujące) nigdy nie powinny trafiać do repozytorium, warstwy obrazu Dockera ani do JavaScriptu po stronie klienta. Wczytuj je ze zmiennych środowiskowych wstrzykiwanych podczas wdrożenia lub z menedżera sekretów, zmieniaj je, gdy odchodzi ktoś, kto miał do nich dostęp, i skanuj historię repozytorium narzędziem takim jak gitleaks — bo klucz raz zacommitowany jest skompromitowany na zawsze, nawet po usunięciu commita.
Kopie zapasowe odporne na ransomware i CDN przed serwerem
Stosuj zasadę 3-2-1: trzy kopie, na dwóch różnych nośnikach, jedna poza siedzibą, i zadbaj, aby co najmniej jedna kopia była niezmienna (immutable) lub offline, tak by ransomware, które dotrze do serwera, nie zaszyfrowało również kopii zapasowych. Szyfruj kopie, uwzględniaj w nich bazę danych i katalog z przesłanymi plikami, a przede wszystkim regularnie testuj odtwarzanie. Kopia zapasowa, z której nikt nigdy niczego nie odtworzył, to nadzieja, a nie plan. To czas przywrócenia decyduje, czy incydent będzie zwykłą przerwą w działaniu, czy końcem firmy.
Umieść CDN lub WAF (Cloudflare, Fastly, AWS CloudFront z WAF albo odpowiednik u Twojego hostingodawcy) przed serwerem źródłowym. Pochłania on wolumetryczne ataki DDoS, stosuje zarządzane reguły dla typowych payloadów injection i złych botów, ukrywa adres IP serwera źródłowego i daje Ci miejsce do egzekwowania limitów żądań oraz reguł geograficznych bez ingerencji w aplikację. Następnie skonfiguruj firewall serwera źródłowego tak, aby bezpośrednio do portu 443 docierały tylko zakresy IP CDN; w przeciwnym razie atakujący po prostu go obejdą. Sprawdzanie hostingu pokazuje, czy prawdziwy serwer źródłowy strony jest odsłonięty mimo CDN.
Uwierzytelnianie poczty: SPF, DKIM i DMARC
Bezpieczeństwo strony obejmuje też pocztę, która dostarcza resety haseł, faktury i odpowiedzi działu wsparcia. Bez SPF, DKIM i DMARC każdy może wysyłać wiadomości jako billing@yourdomain.com, a od lutego 2024 roku Google i Yahoo wymagają wszystkich trzech od nadawców masowych. Cały zestaw to trzy rekordy DNS TXT:
yourdomain.com. TXT "v=spf1 include:_spf.google.com -all"
google._domainkey.yourdomain.com. TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkq..."
_dmarc.yourdomain.com. TXT "v=DMARC1; p=reject; rua=mailto:dmarc@yourdomain.com; adkim=s; aspf=s"Zacznij DMARC od p=none, aby zbierać raporty, a następnie przejdź na p=quarantine i p=reject, gdy każdy uprawniony nadawca (platforma marketingowa, helpdesk, CRM) przejdzie weryfikację zgodności (alignment). Dodaj rekordy MTA-STS i TLS-RPT, aby wymusić szyfrowane dostarczanie poczty do Twoich serwerów, i opublikuj null MX (MX 0 .) w domenach, które nigdy nie wysyłają poczty, aby w ogóle nie dało się ich podszyć. Zweryfikuj każdy rekord w Sprawdzaniu Rekordu SPF, Sprawdzaniu DKIM i Sprawdzaniu DMARC, a jeśli spadnie dostarczalność, sprawdź swoje adresy IP nadawcze na czarnych listach w Sprawdzaniu Czarnej Listy IP.
Loguj, monitoruj i planuj na wypadek włamania
Dwie z dziesięciu kategorii OWASP 2025 dotyczą tego, co dzieje się po błędzie: Security Logging and Alerting Failures (A09, błędy logowania zdarzeń i alertowania) oraz nowa kategoria Mishandling of Exceptional Conditions (A10, niewłaściwa obsługa sytuacji wyjątkowych). Typowe włamanie wciąż pozostaje niewykryte miesiącami, bo dowody nigdy nie zostały zapisane — albo zostały, ale nikt do nich nie zajrzał.
Loguj zdarzenia bezpieczeństwa z kontekstem: każde udane i nieudane logowanie, zmiany MFA, resety haseł, zmiany uprawnień, odmowy dostępu, błędy walidacji danych wejściowych i działania administratorów — każde ze znacznikiem czasu, identyfikatorem użytkownika, źródłowym adresem IP i user agentem.
Nigdy nie zapisuj sekretów w logach: haseł, tokenów sesji, pełnych numerów kart, kluczy API. Maskuj je w warstwie zapisu logów, a nie w każdym miejscu wywołania.
Wysyłaj logi poza serwer do centralnego magazynu (CloudWatch, Loki, Elastic, SIEM) z retencją co najmniej 90 dni, aby atakujący z uprawnieniami roota nie mógł zatrzeć śladów.
Ustawiaj alerty na wzorce, a nie pojedyncze zdarzenia: 50 nieudanych logowań w ciągu minuty, skok liczby odpowiedzi 403 w jednej sesji, nowe konto administratora utworzone poza godzinami pracy, certyfikat wystawiony dla Twojej domeny, którego nikt u Ciebie nie zamawiał.
Odmawiaj w razie awarii (fail closed): gdy kontrola zależna (usługa uwierzytelniania, rate limiter, WAF) zwraca błąd, odrzuć żądanie. Kategoria A10 istnieje, bo tak wiele systemów przyznaje dostęp, gdy kontrola, która powinna była go zablokować, rzuca wyjątek.
Monitoruj z zewnątrz: dostępność, wygasanie certyfikatów, zmiany DNS, wpisy na czarnych listach i regresje nagłówków. Sprawdzanie Kondycji Domeny łączy testy DNS, SSL, uwierzytelniania poczty i nagłówków w jeden wynik, który możesz sprawdzać ponownie po każdym wdrożeniu.
Napisz plan reagowania na incydenty, zanim będzie potrzebny
Zdecyduj z wyprzedzeniem, kto ma dyżur, jak zmienić każde poświadczenie, jak przełączyć stronę w tryb tylko do odczytu, gdzie są czyste kopie zapasowe oraz który organ nadzorczy lub których klientów trzeba powiadomić i w ciągu ilu godzin (72 zgodnie z RODO). Raz w roku przeprowadź ćwiczenie typu tabletop. Zespoły z przećwiczonym planem wracają do działania w ciągu dni; zespoły bez planu improwizują tygodniami.
Testuj: skanery, testy penetracyjne i ciągła weryfikacja
Każde z powyższych zabezpieczeń da się zweryfikować, a weryfikację warto w miarę możliwości zautomatyzować, żeby z czasem nie zanikła. Rozsądna drabina — od darmowych i natychmiastowych testów po okresowe i płatne:
| Test | Narzędzie | Jak często |
|---|---|---|
| Konfiguracja TLS, łańcuch, wygaśnięcie | Sprawdzanie Certyfikatu SSL, SSL Labs, testssl.sh | Po każdej zmianie certyfikatu lub serwera; wygaśnięcie codziennie |
| Nagłówki bezpieczeństwa i CSP | Sprawdzanie Nagłówków HTTP, MDN HTTP Observatory | Po każdym wdrożeniu (dodaj do CI) |
| Otwarte porty | Sprawdzanie Portów, nmap | Po każdej zmianie firewalla lub konfiguracji chmury |
| DNS, DNSSEC, uwierzytelnianie poczty | Sprawdzanie Kondycji Domeny, Sprawdzanie DMARC | Co miesiąc i po każdej zmianie w DNS |
| Nieaktualne subdomeny | Wyszukiwarka Subdomen | Co kwartał i przy każdym wyłączaniu usługi |
| Zależności ze znanymi podatnościami | npm audit, Dependabot, Trivy | Przy każdym buildzie |
| Błędy na poziomie kodu (SAST) | Semgrep, CodeQL | Przy każdym pull requeście |
| Podatności działającej aplikacji (DAST) | OWASP ZAP, Nuclei | Co tydzień na środowisku stagingowym |
| Autoryzacja i logika biznesowa | Ręczny test penetracyjny lub program bug bounty | Raz w roku i po wdrożeniu ważnych nowych funkcji |
Skanery dobrze radzą sobie z injection, nagłówkami i przestarzałymi komponentami, a słabo z autoryzacją i logiką biznesową, więc zaplanuj budżet na co najmniej jeden test wykonywany przez człowieka rocznie dla wszystkiego, co obsługuje płatności lub dane osobowe. Naprawiaj według ekspozycji: wszystko, co nieuwierzytelniony użytkownik może wykorzystać z internetu, ma pierwszeństwo.
Lista kontrolna bezpieczeństwa strony internetowej
Skopiuj ją do README projektu lub systemu zgłoszeń. Każdy wiersz to jedna z opisanych wyżej praktyk, sformułowana tak, aby dało się ją oznaczyć jako wykonaną lub nie; ostatnia kolumna to dowód.
| Warstwa | Praktyka | Gotowe, gdy |
|---|---|---|
| Domena | Registrar lock i MFA na koncie u rejestratora; automatyczne odnawianie włączone | WHOIS pokazuje clientTransferProhibited; wygaśnięcie za ponad 12 miesięcy |
| DNS | Strefa podpisana DNSSEC; rekord CAA wymieniający tylko Twoje CA | dig +dnssec zwraca RRSIG; rekord DS obecny w strefie nadrzędnej |
| DNS | Brak wiszących rekordów CNAME i A | Każda nazwa hosta z Wyszukiwarki Subdomen wskazuje zasób pod Twoją kontrolą |
| TLS | Tylko TLS 1.2+, preferowany TLS 1.3, serwowany pełny łańcuch | Sprawdzanie Certyfikatu SSL: łańcuch kompletny, TLS 1.0/1.1 odrzucane |
| TLS | HSTS z max-age na rok, includeSubDomains, na liście preload | Domena widnieje na hstspreload.org |
| TLS | Odnawianie zautomatyzowane przez ACME; wygaśnięcie monitorowane | certbot renew --dry-run przechodzi; alert ustawiony na 14 dni przed wygaśnięciem |
| Nagłówki | CSP (oparta na nonce), frame-ancestors, nosniff, Referrer-Policy, Permissions-Policy | Ocena A w Sprawdzaniu Nagłówków HTTP |
| Uwierzytelnianie | Hashowanie Argon2id lub bcrypt; zasady haseł NIST; sprawdzanie z listą wycieków | Jeden hash trwa od 100 do 250 ms; brak reguł złożoności |
| Uwierzytelnianie | MFA dostępne dla wszystkich, wymagane dla administratorów; obsługa passkeys | Logowanie administratora niemożliwe bez drugiego składnika |
| Uwierzytelnianie | Limity żądań na endpointach logowania, resetu hasła i MFA dla każdego IP i każdego konta | Szósta próba w ciągu minuty zwraca 429 |
| Sesje | Ciasteczko __Host- z Secure, HttpOnly, SameSite; rotacja ID przy logowaniu | Atrybuty ciasteczka widoczne w DevTools; stara sesja nieważna po zalogowaniu |
| Dane wejściowe | Zapytania parametryzowane wszędzie; kodowanie wyjścia zależne od kontekstu; przesyłane pliki walidowane po zawartości | Przeszukanie kodu nie znajduje zapytań sklejanych ze stringów; payloady XSS wyświetlają się jako tekst |
| Kontrola dostępu | Routing z domyślną odmową; sprawdzanie własności każdego obiektu; restrykcyjny CORS | Odtworzenie żądań z identyfikatorami innego użytkownika zwraca 404 lub 403 |
| Zależności | Lockfile w repozytorium; npm ci; audyt w CI; akcje przypięte do SHA; SRI dla skryptów z CDN | Build kończy się błędem przy CVE o wysokiej ważności |
| Serwer | Publicznie tylko 80 i 443; SSH wyłącznie z kluczami; automatyczne aktualizacje bezpieczeństwa; sekrety w zmiennych środowiskowych lub menedżerze sekretów | Sprawdzanie Portów pokazuje, że 22, 3306 i 6379 są zamknięte od strony internetu |
| Kopie zapasowe | 3-2-1 z jedną kopią niezmienną; odtwarzanie przetestowane | Ostatni udany test odtwarzania nie starszy niż 90 dni |
| Poczta | SPF -all, DKIM, DMARC p=reject, MTA-STS | Sprawdzanie DMARC zaliczone; raporty zbiorcze napływają |
| Monitoring | Zdarzenia uwierzytelniania i autoryzacji logowane centralnie; alerty na wzorce; zewnętrzny monitoring dostępności, certyfikatów i DNS | Testowy alert wysłany i odebrany |
| Reagowanie | Spisany plan reagowania na incydenty; opublikowany security.txt; przeprowadzone ćwiczenie tabletop | Plan przejrzany w ciągu ostatnich 12 miesięcy |
Jak to się ma do OWASP Top 10:2025
OWASP Top 10 to najczęściej cytowana lista zagrożeń dla aplikacji webowych, a edycja z 2025 roku zmieniła kolejność i wprowadziła dwie nowe kategorie. Jeśli klient, audytor lub framework zgodności zapyta, jak się do niej odnosisz, oto zestawienie z praktykami z tego przewodnika:
| OWASP Top 10:2025 | Praktyki z tego przewodnika |
|---|---|
| A01 Broken Access Control | Domyślna odmowa, kontrola dla każdego obiektu, restrykcyjny CORS, zabezpieczone panele administracyjne |
| A02 Security Misconfiguration | Nagłówki bezpieczeństwa, HSTS, zamknięte porty, usunięte nagłówki z wersjami, wyłączone listowanie katalogów |
| A03 Software Supply Chain Failures (nowa) | Lockfile, audyty, przypięte akcje, SRI, SBOM, CI z minimalnymi uprawnieniami |
| A04 Cryptographic Failures | TLS 1.2+ i 1.3, HSTS, Argon2id, zarządzanie sekretami, szyfrowane kopie zapasowe |
| A05 Injection | Zapytania parametryzowane, kodowanie danych wyjściowych, CSP, walidacja przesyłanych plików |
| A06 Insecure Design | Model zagrożeń dla każdej warstwy, domyślne zachowanie fail closed, limitowanie żądań uwzględnione w projekcie |
| A07 Authentication Failures | Zasady haseł NIST, sprawdzanie wycieków, MFA i passkeys, rotacja sesji |
| A08 Software or Data Integrity Failures | SRI, podpisane i przypięte zależności, bezpieczna deserializacja |
| A09 Security Logging and Alerting Failures | Centralne logi, alerty na wzorce, monitoring zewnętrzny |
| A10 Mishandling of Exceptional Conditions (nowa) | Fail closed, jednolite odpowiedzi błędów, żadnych stack trace'ów dla użytkowników |
Sprawdź nagłówki bezpieczeństwa swojej strony w kilka sekund
Darmowe Sprawdzanie Nagłówków HTTP od DNS Robot pobiera dowolny adres URL i ocenia jego nagłówki bezpieczeństwa w skali od A do F: HSTS, Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, Referrer-Policy i Permissions-Policy — z dokładną znalezioną wartością i informacją, czego brakuje. Bez rejestracji.
Wypróbuj Sprawdzanie Nagłówków HTTPAdvertisement
Bezpieczeństwo strony internetowej — najczęstsze pytania
Wymuś HTTPS z nowoczesnym TLS i HSTS, wysyłaj restrykcyjne nagłówki bezpieczeństwa (zwłaszcza Content Security Policy), hashuj hasła algorytmem Argon2id i chroń logowanie za pomocą MFA oraz limitów żądań, używaj zapytań parametryzowanych i kodowania danych wyjściowych, egzekwuj kontrolę dostępu na serwerze dla każdego obiektu, aktualizuj i przypinaj zależności, zamknij nieużywane porty i centralnie zbieraj logi zdarzeń bezpieczeństwa. Zacznij od zabezpieczenia domeny i DNS, bo wszystko inne zależy od tego, czy nadal kontrolujesz swoją domenę.