Powrót do bloga

Security headers: HSTS, CSP, X-Frame-Options i inne

· 7 min czytania

Backend to forteca, ale druga połowa obrony jest w przeglądarce. HSTS, CSP i inne nagłówki blokują XSS, clickjacking i MITM - i są darmowe.

Security headers: HSTS, CSP, X-Frame-Options i inne

Dlaczego mają znaczenie

Aplikacja może być doskonale zabezpieczona po stronie backendu, a mimo to pozostawać podatna na ataki po stronie przeglądarki: cross-site scripting (XSS), clickjacking, protocol downgrade, MIME confusion. Security headers przesuwają obronę do samej przeglądarki.

Strict-Transport-Security (HSTS)

Wymusza HTTPS dla wszystkich przyszłych odwiedzin. Po pierwszej wizycie przez HTTPS przeglądarka zapamiętuje domenę i sama przepisuje każdy link http:// na https://, nawet jeśli użytkownik kliknie zły odnośnik.

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
  • max-age=63072000 = obowiązuje 2 lata
  • includeSubDomains = reguła obowiązuje też dla wszystkich subdomen; wymusza HTTPS wszędzie, więc jeśli masz subdomenę działającą tylko przez HTTP, np. staging.example.com, stanie się niedostępna w przeglądarce
  • preload = pozwala na włączenie do listy HSTS preload wbudowanej w Chrome / Firefox / Safari

Uwaga: Usunięcie HSTS z preload z listy trwa 6+ miesięcy. Nie włączaj preload, zanim nie zweryfikujesz, że HTTPS działa stabilnie również na wszystkich subdomenach.

Content-Security-Policy (CSP)

Najmocniejszy, ale zarazem najtrudniejszy do skonfigurowania nagłówek. Whitelistuje, skąd przeglądarka może ładować skrypty, style, obrazy i iframe'y. Bez CSP atakujący, który potrafi wstrzyknąć tag <script>, może uruchomić dowolny JS; z CSP tylko kod z zatwierdzonych źródeł.

Content-Security-Policy: default-src 'self'; script-src 'self' 'sha256-...'; img-src 'self' data: https://cdn.example.com; style-src 'self' 'unsafe-inline'; frame-ancestors 'none'

Częste dyrektywy:

  • default-src 'self' = domyślnie pozwól tylko na zasoby z mojej domeny
  • script-src = źródła JS (dla skryptów inline podaj hash lub nonce)
  • style-src = źródła CSS
  • img-src = obrazy
  • connect-src = AJAX, WebSocket, EventSource
  • frame-ancestors 'none' = nikt nie może osadzić strony w iframe (lepsze niż X-Frame-Options)
  • report-uri /csp-report = przeglądarka wysyła JSON przy każdej violation (ty łapiesz ją w backendzie i logujesz)
  • Uwaga: report-uri jest dziś deprecated - przeglądarki obsługujące report-to go ignorują. Nowoczesny sposób to dyrektywa report-to plus osobny nagłówek Reporting-Endpoints; report-uri zostaw tylko jako fallback dla Firefoksa, który nie obsługuje jeszcze report-to.

X-Frame-Options

Starsza alternatywa dla frame-ancestors. Chroni przed clickjackingiem, w którym atakujący osadza twoją stronę jako iframe i nakłada na nią niewidzialne przyciski.

X-Frame-Options: DENY

Wartości: DENY (nikt), SAMEORIGIN (tylko moja domena), ALLOW-FROM uri (deprecated).

X-Content-Type-Options

X-Content-Type-Options: nosniff

Wyłącza MIME sniffing, dzięki czemu przeglądarka respektuje Content-Type zwrócony przez serwer. Bez tego atakujący może wgrać plik z błędnym typem (np. obraz, który w rzeczywistości jest HTML ze skryptem) i przeglądarka może go uruchomić jako stronę WWW.

Referrer-Policy

Referrer-Policy: strict-origin-when-cross-origin

Kontroluje, ile informacji o poprzedniej stronie przeglądarka wysyła w nagłówku Referer przy kliknięciu. Domyślna wartość w nowoczesnych przeglądarkach to już strict-origin-when-cross-origin, ale jawne ustawienie nagłówka gwarantuje spójne zachowanie.

Permissions-Policy

Wyłącza API, których nie potrzebujesz, takie jak kamera, mikrofon, GPS i geolokalizacja. Atakujący wykorzystujący XSS nie może zażądać tych API.

Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()

Praktyczna konfiguracja w nginx

server {
  listen 443 ssl http2;
  server_name example.com;

  add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
  # preload celowo tu pominęliśmy - dodaj go dopiero gdy masz 100% pewność (zob. pułapka z preload powyżej)
  add_header X-Content-Type-Options "nosniff" always;
  add_header X-Frame-Options "DENY" always;
  add_header Referrer-Policy "strict-origin-when-cross-origin" always;
  add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

  # CSP jest rozległa - zdefiniuj ją według swojej aplikacji
  add_header Content-Security-Policy "default-src 'self'; script-src 'self'; ..." always;
}

Kluczowe jest always - bez niego nginx pomija nagłówki przy odpowiedziach błędu (4xx, 5xx).

Audyt aktualnego stanu

Najprościej za pomocą narzędzia online. Nasz header check pokaże ci, których nagłówków brakuje i które są niewłaściwe. Do dokładnego audytu securityheaders.com również daje ocenę A-F.

Wnioski

Security headers należą do najlepszych inwestycji w bezpieczeństwo pod względem stosunku wysiłku do zysku: typowo pół godziny konfiguracji w reverse proxy przeciwko całej kategorii ataków po stronie przeglądarki. Audyt raz na pół roku w ramach regularnej konserwacji to rozsądne minimum.

Bezpłatny audyt security headers

Bez rejestracji, wynik w trzy sekundy.

Przetestuj stronę →

Udostępnij: Skopiowano link

Wypróbuj ePulz.io za darmo - 7 dni bez karty kredytowej.

Utwórz konto