Jeden nagłówek HTTP. Pięć dni wdrożenia.
Jeśli to brzmi jak przesada — być może nie próbowałeś skonfigurować go porządnie, bez pozostawiania tylnych drzwi.
Co to jest i dlaczego ma znaczenie
Przeglądarka, gdy dostaje stronę, uruchamia wszystkie skrypty, które znajdzie — bez pytania. To normalne zachowanie, ale też otwarte zaproszenie dla atakujących: jeśli ktoś zdoła wstrzyknąć złośliwy skrypt do odpowiedzi serwera, przeglądarka go posłusznie uruchomi.
Content Security Policy (CSP) to instrukcja dla przeglądarki: „nie uruchamiaj niczego, czego nie zatwierdziłem”. Serwer mówi przeglądarce, jakie skrypty są zaufane — przeglądarka blokuje resztę.
Większość aplikacji albo nie ma tego zabezpieczenia, albo ma je z furtką: „uruchamiaj wszystkie skrypty wbudowane w stronę”. To jak zamek z kluczem zostawionym pod wycieraczką.
Skąd jednorazowe tokeny
Są dwa sposoby powiedzenia przeglądarce, które skrypty są zaufane:
- Podać kryptograficzny odcisk palca każdego skryptu — ale przy każdej zmianie skryptu odcisk się zmienia, co trzeba śledzić ręcznie.
- Używać jednorazowych tokenów — serwer generuje unikalny kod dla każdego żądania, skrypt z tym kodem jest zaufany, reszta nie.
Wybrałem tokeny, bo aplikacja generuje dynamiczny HTML po stronie serwera. Śledzenie odcisków palców każdego skryptu byłoby nie do utrzymania.
Pięć dni
Dzień 1: Generator unikalnych tokenów — jeden losowy kod na każde żądanie HTTP, dostępny w całym kontekście tego żądania.
Dzień 2: Integracja z szablonami HTML. Każdy skrypt wbudowany w stronę musi dostać token z kontekstu żądania. Szukanie wszystkich miejsc w kodzie, gdzie generowany jest dynamiczny HTML.
Dzień 3: Testy i pierwsze awarie. Kilka zewnętrznych bibliotek (kod klienta dostawców usług) nie obsługuje tokenów. Decyzja dla każdej: przenosisz na zewnętrzny plik albo dodajesz domenę do listy zaufanych.
Dzień 4–5: Tryb raportowania — zamiast blokować, zbieraj przez dwie doby informacje o tym, co byłoby zablokowane. Analiza i naprawa ostatnich niezgodności. Potem przełączenie na tryb blokujący.
Szczera uwaga o tym „pięć dni”: tak to rozpisałem sobie w głowie i w opisach zadań — pięć etapów. W git wylądowały jednego dnia, jednym wdrożeniem. Praca rozłożyła się na kilka posiedzeń, ale historia repo pokazuje jeden moment. To znów ta sama lekcja co przy zbyt dużych commitach: rytm w głowie i rytm w git to nie to samo.
Bezpieczeństwo bez użytkowników
Budując aplikację dla mieszkańców Starachowic — osób z prawdziwymi adresami i numerami telefonów — chcę wiedzieć, że kiedy przyjdą pierwsi użytkownicy, fundamenty są bezpieczne.
Łatwiej zbudować to zabezpieczenie od zera niż przebudować je na działającej produkcji. Październik 2025 był na to idealnym momentem: aplikacja skompilowana, testy przechodzą, użytkowników zero.
Dziś wiem, co ma prawo się uruchomić w przeglądarce. Każdy nieznany skrypt — zablokowany zanim zdąży zadziałać.
Content Security Policy — jak to działa (ogólnie)
Podejście to nonce-based Content Security Policy. Serwer przy każdym żądaniu generuje jednorazowy token (kryptograficznie bezpieczny generator losowości) i dokleja go tylko do zaufanych skryptów. Przeglądarka uruchamia skrypt wyłącznie z aktualnym tokenem — całą resztę blokuje.
Najważniejsza zasada: żadnych furtek. Tryby w stylu „uruchamiaj wszystkie skrypty wbudowane w stronę” są tym, co najczęściej zamienia CSP w fikcję — więc na produkcji ich po prostu nie ma.
Wdrożenie etapami było kluczem do tego, żeby niczego nie zepsuć: najpierw tryb tylko-raportujący (przeglądarka zgłasza, co zostałoby zablokowane, ale jeszcze przepuszcza), analiza zgłoszeń, a dopiero potem twarde blokowanie. Dzięki temu legalne skrypty zewnętrznych narzędzi wychodzą na jaw, zanim zablokujesz je użytkownikowi.