Content Security Policy dla chatbotów na stronie: Bezpieczne zezwalanie na widget, API, obrazy i streaming
Praktyczna polityka CSP dla chatbota na stronie pozwala tylko na rzeczywiście wymagane skrypty, połączenia API, strumienie i obrazy – bez zbędnych symboli wieloznacznych (wildcards).

Chatbot na stronie internetowej rzadko składa się w przeglądarce tylko z jednego pliku JavaScript. Loader otwiera widget, API przyjmuje wiadomości, odpowiedzi wracają w formie strumienia, a zdjęcia profilowe lub media mogą znajdować się na osobnej domenie. Content Security Policy (CSP) uwidacznia te ścieżki i je ogranicza: przeglądarka ładuje lub łączy się tylko z tym, na co strona jednoznacznie zezwala.
Jest to ważna, druga warstwa ochrony przed Cross-Site Scripting (XSS) i nieoczekiwanymi treściami stron trzecich. CSP nie naprawi jednak ani niebezpiecznego API, ani braku uwierzytelniania, niedostatecznej walidacji danych wejściowych czy podatności na prompt injection. Zmniejsza ona możliwości wstrzykniętego kodu i ogranicza promień rażenia błędu. Kluczowe jest zatem posiadanie jak najmniejszej, przetestowanej polityki zamiast długiej listy ogólnie zatwierdzonych domen.
Dlaczego widgety chatbotów potrzebują szczególnych reguł CSP
W przypadku klasycznej strony z treścią często wystarczają zasoby z własnego źródła (origin). Chatbot natomiast po załadowaniu kontynuuje komunikację. connect-src kontroluje między innymi fetch(), XMLHttpRequest, EventSource, WebSocket oraz sendBeacon(). To właśnie tędy przepływają wiadomości, odpowiedzi strumieniowe, zdarzenia zwrotne i ewentualnie telemetria. Jeśli brakuje właściwego źródła, widget wprawdzie się pojawi, ale nie będzie mógł odpowiedzieć.
Inne elementy podlegają własnym dyrektywom. script-src decyduje o loaderze widgetu, img-src o awatarach i obrazach w odpowiedziach, style-src o arkuszach stylów, a font-src o zewnętrznych czcionkach. Widget oparty na ramce iframe wymaga dodatkowo frame-src. default-src służy jako rozwiązanie awaryjne dla wielu jawnie niewymienionych typów zasobów, ale nie zastępuje świadomego audytu.
Najważniejsza praca przygotowawcza nie odbywa się więc w generatorze CSP, lecz w przeglądarce: otwórz reprezentatywną stronę, rozpocznij rozmowę, pozwól na wygenerowanie długiej odpowiedzi strumieniowej, otwórz źródła, wyślij opinię i przetestuj przypadki błędów oraz przekazania rozmowy do człowieka (handoff). W panelu sieciowym zobaczysz faktycznie wywoływane źródła (origins). Udokumentuj dla każdego hosta cel, typ zasobu oraz osobę odpowiedzialną.
Osobne zezwalanie dla czterech kluczowych ścieżek danych
1. Skrypt widgetu i inicjalizacja
Pobieraj loader w miarę możliwości ze stabilnego, wersjonowanego adresu. Zezwolenie takie jak script-src https: byłoby zbyt szerokie, ponieważ dopuszczałoby skrypty z dowolnej domeny HTTPS. Zamiast tego zezwól na dokładne źródło CDN lub dostarczaj loader samodzielnie. Jeśli integracja wymaga kodu inline, użyj wartości nonce generowanej nowo dla każdej odpowiedzi HTTP lub odpowiedniego hasza. 'unsafe-inline' nie powinno stać się szybkim, stałym rozwiązaniem.
Wartość nonce powinna być dołączana tylko do skryptów, które szablon po stronie serwera generuje samodzielnie. Oprogramowanie pośredniczące (middleware), które ślepo dodaje ten sam nonce do każdego istniejącego znacznika script, dałoby zaufanie również wstrzykniętym znacznikom. W przypadku statycznego, wersjonowanego skryptu zewnętrznego pomocny może być mechanizm Subresource Integrity; jednak przy często zmieniających się plikach hasz musi być kontrolowanie aktualizowany.
2. API, Server-Sent Events i WebSocket
Zwykłe żądania POST oraz odpowiedź strumieniowana przez fetch() wymagają źródła HTTPS API w connect-src. Podlega pod to również Server-Sent Events poprzez EventSource. Dla WebSocketa wpisz jawnie konkretne źródło wss://. MDN zwraca uwagę, że 'self' nie obejmuje automatycznie schematów WebSocket we wszystkich przeglądarkach. Dedykowana dyrektywa o nazwie stream-src nie istnieje.
CSP i CORS rozwiązują różne zadania. CSP określa, dokąd strona w ogóle może się łączyć; CORS określa po stronie serwera, które źródła mogą odczytać odpowiedź w przeglądarce. Zezwolenie w CSP nie naprawi więc ani błędu CORS, ani wygasłego tokenu dostępu. Proxy typu Same-Origin może uprościć politykę, ale nadal musi prawidłowo obsługiwać uwierzytelnianie, limity żądań (rate limits), przekroczenia czasu oraz przekazywanie błędów.
3. Obrazy, awatary i wygenerowane media
W img-src zezwalaj tylko na własne źródło oraz faktycznie używane źródło mediów. data: jest potrzebne tylko wtedy, gdy widget używa małych osadzonych obrazów; blob: tylko wtedy, gdy przeglądarka rzeczywiście generuje obrazy jako URL typu Blob. Każde dodatkowe źródło zwiększa powierzchnię ataku. Jeśli obraz jest najpierw ładowany przez fetch(), a następnie konwertowany na Blob URL, może to dotyczyć zarówno connect-src, jak i img-src.
Przetestuj nie tylko standardowy awatar. Sprawdź miniatury podglądu, zrzuty ekranu źródeł, załączniki plików, tryb ciemny i wyświetlanie błędów dla niedostępnych mediów. Parametry URL mogą przenosić poufne informacje do raportów CSP; punkty końcowe raportowania powinny zatem przetwarzać raporty z poszanowaniem prywatności danych i nie przechowywać ich w nieskończoność.
4. iframe, style, czcionki i opcjonalne workery
Widget osadzony bezpośrednio w drzewie DOM zazwyczaj nie potrzebuje obcej ramki. Wtedy frame-src 'none' może pozostać bez zmian. Jeśli jednak czat działa wewnątrz iframe, zezwól wyłącznie na jego dokładne źródło. Od tego należy odróżnić frame-ancestors: ta dyrektywa określa na serwowanym zasobie, które strony mogą go osadzać. Dostawca widgetu musi ją zatem odpowiednio ustawić na swojej odpowiedzi iframe.
Ta sama zasada dotyczy stylów i czcionek. Zezwalaj na konkretnych hostów i unikaj 'unsafe-inline', na ile pozwala na to integracja. Workery lub funkcje audio dodaje się tylko wtedy, gdy produkt faktycznie z nich korzysta. Zapobiegawcze zezwalanie na blob:, całe domeny z symbolami wieloznacznymi lub dowolne źródła mediów utrudnia późniejsze audyty.
Realistyczny przykład CSP dla widgetu chatbota
Poniższe domeny są celowo zarezerwowanymi domenami przykładowymi. Zastąp je źródłami z własnej analizy sieciowej. Przykład zakłada zewnętrzny loader, HTTPS API, osobny WebSocket do strumieniowania i hosta mediów. Nie używa ogólnych symboli wieloznacznych:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.chat.example 'nonce-{RANDOM}';
connect-src 'self' https://api.chat.example wss://stream.chat.example;
img-src 'self' data: https://media.chat.example;
style-src 'self' 'nonce-{RANDOM}';
font-src 'self';
frame-src 'none';
worker-src 'self';
object-src 'none';
base-uri 'self';
frame-ancestors 'self';
form-action 'self';
upgrade-insecure-requests;{RANDOM} oznacza silną wartość, generowaną na nowo dla każdej odpowiedzi, która jest identyczna w nagłówku oraz w dozwolonych elementach script lub style. Jeśli Twój widget używa iframe, zastąp frame-src 'none' dokładnym źródłem widgetu. Jeśli używa wyłącznie strumieniowania HTTPS przez fetch() lub EventSource, źródło WebSocket można usunąć. Usuń każde źródło, które nie jest potrzebne po pełnym teście funkcjonalnym.
Polityka ta jest praktycznym punktem wyjścia, a nie uniwersalnym szablonem. Nowoczesne, ścisłe CSP może jeszcze silniej kontrolować skrypty poprzez wartości nonce lub hasze i 'strict-dynamic'. To, czy jest to możliwe bez problemów z kompatybilnością, zależy od tego, jak loader generuje kolejne skrypty. Wyjaśnij ten proces z dostawcą i przetestuj przeglądarki, tryb zgody (Consent Mode) oraz warianty wdrożenia.
Od Report-Only do wymuszonej polityki
Nie uruchamiaj nowej polityki w trybie produkcyjnym bez sprawdzenia. Mechanizm W3C Content-Security-Policy-Report-Only zgłasza naruszenia bez blokowania zasobów. W ten sposób wykryjesz zapomniane hosty obrazów, odmienne źródło strumieniowania lub kod inline, zanim wpłynie to na użytkowników. OWASP zaleca nagłówek HTTP jako preferowany sposób dostarczania; w przeciwieństwie do elementu meta obsługuje on również pełny zakres funkcji.
- Stwórz przewodnik/inwentarz: Przetestuj uruchomienie widgetu, pierwszą wiadomość, długą odpowiedź strumieniową, źródła, obrazy, opinie, przekazanie konsultantowi i zmiany zgód na wielu typach stron.
- Wdróż Report-Only: Zacznij od planowanej, wąskiej polityki i zbieraj naruszenia w określonym przedziale czasu. Odfiltruj rozszerzenia przeglądarki i inne powtarzalne zakłócenia.
- Uzasadnij każdego hosta: Rozszerzaj politykę tylko wtedy, gdy konkretna funkcja produktu wymaga danego źródła. Unikaj symboli wieloznacznych w odpowiedzi na pojedyncze zgłoszenia.
- Testuj automatycznie: Uzupełnij testy end-to-end, które wysyłają wiadomość, czekają na strumieniowanie i ładują obraz. Jednocześnie sprawdzaj konsolę przeglądarki pod kątem naruszeń CSP.
- Wymuszaj i obserwuj: Włącz nagłówek
Content-Security-Policy, zachowaj równolegle jeszcze surowszy wariant Report-Only i porównuj współczynniki błędów.
Krokowe wdrożenie dobrze pasuje do chatbota w trybie Shadow Mode. W przypadku wskaźników specyficznych dla strumieniowania pomocny będzie artykuł o budżetach opóźnień i timeoutach. Naruszenia CSP powinny być przy tym traktowane jako osobny sygnał: przekroczenie czasu i zablokowane połączenie wymagają różnej analizy przyczyn.
Typowe błędne konfiguracje
- Zbyt szerokie listy źródeł:
*,https:lub duże domeny z symbolami wieloznacznymi sprawiają, że polityka jest wygodna, ale słaba i trudna do weryfikacji. - Testowanie tylko widocznego startu: Widget się otwiera, ale strumieniowanie, opinie, obrazy lub przekazanie rozmowy zawodzą dopiero później.
'unsafe-inline'pozostaje na stałe: Tymczasowa pomoc dla kompatybilności nie zostaje zastąpiona przez wartości nonce, hasze ani zewnętrzny kod.- Mylenie CSP z kontrolą dostępu: Polityka nie zastępuje uprawnień po stronie serwera, weryfikacji sesji ani ochrony przed nadużyciami wywołań narzędzi.
- Raporty zawierają zbyt wiele danych: Pełne adresy URL, parametry zapytań lub kontekst użytkownika trafiają niepotrzebnie na długo do systemu monitorowania.
- Środowiska stagingowe i produkcyjne się rozjeżdżają: Różne hosty CDN, API lub WebSocket stają się widoczne dopiero po wdrożeniu produkcyjnym.
Nawet wąskie script-src nie sprawia, że dozwolony dostawca zewnętrzny staje się automatycznie bezpieczny: jego JavaScript działa z takimi możliwościami, jakie daje mu Twoja strona. Z tego powodu sprawdzaj zmiany dostawców, nowe subdomeny i aktualizacje loaderów tak samo, jak inne zależności związane z bezpieczeństwem. Artykuł o ochronie przed prompt injection uzupełnia tę granicę przeglądarki o reguły dla RAG, narzędzi i danych.
Lista kontrolna przed uruchomieniem (Go-live)
- Czy wszystkie wymagane źródła (origins) z rzeczywistych sesji przeglądarki są udokumentowane i merytorycznie uzasadnione?
- Czy
script-srczezwala tylko na loader i kontrolowane skrypty, bez ogólnego'unsafe-inline'? - Czy
connect-srczawiera dokładne źródła HTTPS, EventSource i ewentualnie WSS? - Czy źródła obrazów, stylów, czcionek, ramek i workerów są rozdzielone i zdefiniowane tak wąsko, jak to możliwe?
- Czy wartości nonce są generowane na nowo dla każdej odpowiedzi i przypisywane tylko do zaufanych elementów?
- Czy przetestowano zmiany zgód, długie strumienie, obrazy, błędy, przekazanie rozmowy oraz urządzenia stacjonarne i mobilne?
- Czy polityka była najpierw obserwowana w trybie Report-Only, a następnie wymuszona jako nagłówek?
- Czy raporty CSP są przetwarzane bez zbędnych danych osobowych lub poufnych danych w adresach URL?
- Czy po aktualizacji widgetu lub infrastruktury przeprowadzany jest automatyczny test regresyjny?
Podsumowanie
Dobre CSP dla chatbota na stronie internetowej to nie zbiór wyjątków, lecz techniczna mapa dozwolonych ścieżek w przeglądarce. Oddziel loader, API, strumieniowanie, obrazy i zasoby iframe, zezwalaj na dokładne źródła (origins) i wdrażaj politykę najpierw w trybie Report-Only. Dzięki temu widget pozostaje funkcjonalny, podczas gdy nieoczekiwane skrypty i połączenia mają znacznie mniejsze pole do działania.
Źródła
Zamień odwiedziny w lepsze rozmowy
Uruchom chatbota AI użytecznego od pierwszego dnia
Trenuj ChatReact na podstawie swojej strony, dokumentów i zatwierdzonych faktów, aby odwiedzający otrzymywali szybsze odpowiedzi, a Twój zespół mniej powtarzalnych zgłoszeń.
Powiązane artykuły
Czytaj dalej
Jak dodać chatbota AI do strony internetowej, nie szkodząc UX ani SEO
Plan wdrożenia dodania chatbota do strony przy zachowaniu ścieżki użytkownika, szybkości ładowania i struktury treści.

Optymalizacja czasu odpowiedzi chatbota AI: budżet opóźnień, streaming i timeouty
Szybkie odpowiedzi chatbota zależą od całego łańcucha technicznego. Dowiedz się, jak planować budżety opóźnień, streaming, timeouty, ponowne próby i bezpieczne ścieżki awaryjne.

Testowanie chatbotów AI w trybie Shadow Mode: Bezpiecznie od prototypu do wdrożenia na stronie
Dzięki trybowi Shadow Mode, jasnym bramkom jakościowym i stopniowemu wdrażaniu zespoły internetowe bezpiecznie testują chatboty AI przed ich oficjalnym uruchomieniem.