Tillbaka till bloggen
Implementering20 augusti 20268 min läsningUppdaterad 30 augusti 2026

Content Security Policy för webbplats-chatbottar: Säkra widget, API, bilder och strömning

En praktisk CSP för webbplats-chatbottar tillåter endast de skript, API-anslutningar, strömmar och bilder som faktiskt behövs – utan onödiga jokerspår.

En vuxen eventtekniker kontrollerar en säkrad anslutningsmodul för en chatbot-widget på en somrig utomhusscen.

En webbplats-chatbot består i webbläsaren sällan av bara en enda JavaScript-fil. En laddare öppnar widgeten, ett API tar emot meddelanden, svar returneras som en ström och profilbilder eller medier kan ligga på en annan domän. En Content Security Policy (CSP) gör dessa vägar synliga och begränsar dem: webbläsaren laddar eller ansluter endast till det som webbplatsen uttryckligen tillåter.

Detta är ett viktigt andra skyddslager mot skriptinline-attacker (Cross-Site Scripting) och oväntat innehåll från tredje part. En CSP reparerar dock varken ett osäkert API, saknad autentisering, bristfällig indata-validering eller prompt injection. Den minskar möjligheterna för injicerad kod och begränsar räckvidden för ett fel. Det avgörande är därför en så liten och testad policy som möjligt istället för en lång lista med schablonmässigt godkända domäner.

Varför chatbot-widgetar behöver särskilda CSP-regler

För en klassisk innehållssida räcker det ofta med resurser från det egna ursprunget. En chatbot fortsätter däremot att kommunicera efter att den har laddats. connect-src styr bland annat fetch(), XMLHttpRequest, EventSource, WebSocket och sendBeacon(). Det är exakt här som meddelanden, strömmande svar, feedback-händelser och eventuell telemetri körs. Om rätt ursprung saknas visas widgeten, men den kan inte svara.

Andra komponenter faller under egna direktiv. script-src avgör över widget-laddaren, img-src över avatarer och svarsbilder, style-src över stilmallar och font-src över externa typsnitt. En iframe-baserad widget kräver dessutom frame-src. default-src fungerar som reserv för många resurstyper som inte uttryckligen nämns, men ersätter inte en medveten kartläggning.

Det viktigaste förarbetet sker därför inte i en CSP-generator, utan i webbläsaren: Öppna en representativ sida, starta en konversation, låt ett långt svar strömmas, öppna källor, skicka feedback och testa fel- samt handoff-fall. I nätverkspanelen ser du de ursprung (origins) som faktiskt anropas. Dokumentera syfte, resurstyp och en ansvarig person för varje värd.

Godkänn de fyra relevanta datavägarna separat

1. Widget-skript och initialisering

Hämta laddaren från en stabil, versionshanterad adress om möjligt. Att tillåta script-src https: är för brett, eftersom det skulle göra skript från vilken HTTPS-domän som helst tillåtna. Tillåt istället det exakta CDN-ursprunget eller leverera laddaren själv. Om integrationen kräver inline-kod, använd en nonce som genereras på nytt per HTTP-svar eller en passande hash. 'unsafe-inline' bör inte bli en snabb permanent lösning.

En nonce hör bara hemma på skript som mallen på serversidan själv skapar. En middleware som blint lägger till samma nonce på alla befintliga script-taggar skulle även ge förtroende åt injicerade taggar. För ett statiskt, versionshanterat tredjepartsskript kan Subresource Integrity ge ytterligare hjälp; vid filer som ändras ofta måste dock hashen uppdateras kontrollerat.

2. API, Server-Sent Events och WebSocket

Vanliga POST-förfrågningar och svar som strömmas via fetch() kräver HTTPS API-ursprunget i connect-src. Server-Sent Events via EventSource ingår också där. För en WebSocket anger du det konkreta wss://-ursprunget uttryckligen. MDN påpekar att 'self' inte automatiskt omfattar WebSocket-scheman i alla webbläsare. Det finns inget eget direktiv som heter stream-src.

CSP och CORS löser olika uppgifter. CSP bestämmer vart sidan överhuvudtaget får ansluta; CORS bestämmer på serversidan vilka ursprung som får läsa ett svar i webbläsaren. Ett CSP-godkännande åtgärdar därför varken ett CORS-fel eller en utgången åtkomsttoken. En Same-Origin-proxy kan förenkla policyn, men måste fortfarande hantera autentisering, hastighetsbegränsningar (rate limits), timeouter och fel vidarebefordran korrekt.

3. Bilder, avatarer och genererade medier

Tillåt endast det egna ursprunget och det medieursprung som faktiskt används i img-src. data: behövs endast om widgeten använder små inbäddade bilder; blob: behövs endast om webbläsaren faktiskt skapar bilder som en Blob-URL. Varje extra källa förstorar angreppsytan. Om en bild först hämtas via fetch() och därefter omvandlas till en Blob-URL kan både connect-src och img-src påverkas.

Testa inte bara standardavataren. Kontrollera förhandsgranskningsbilder, skärmdumpar av källor, filbilagor, mörkt läge och felvisning för otillgängliga medier. URL-parametrar kan föra över känslig information till CSP-rapporter; rapporteringsslutpunkter bör därför behandla rapporter datasnålt och inte spara dem obegränsat.

4. iframe, styles, fonts och valfria workers

En widget som är inbäddad direkt i DOM behöver oftast ingen extern frame. Då kan frame-src 'none' ligga kvar. Om chatten däremot körs i en iframe tillåter du enbart dess exakta ursprung. Detta ska skiljas från frame-ancestors: det direktivet anger på den levererade resursen vilka sidor som får bädda in den. Widget-leverantören måste därför ställa in det på ett passande sätt i sitt iframe-svar.

Samma princip gäller för styles och fonts. Tillåt konkreta värdar och undvik 'unsafe-inline' i den mån integrationen tillåter det. Workers eller ljudfunktioner läggs bara till om produkten faktiskt använder dem. Att tillåta blob:, hela jokerspår-domäner eller valfria mediekällor för säkerhets skull försvårar framtida granskningar.

Ett realistiskt CSP-exempel för en chatbot-widget

Följande domäner är medvetet reserverade exempeldomäner. Ersätt dem med ursprungen från din egen nätverksanalys. Exemplet antar en extern laddare, ett HTTPS-API, en separat WebSocket för strömning och en medievärd. Det använder inga generella jokerspår:

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} står för ett starkt värde som skapas på nytt per svar, och som är identiskt i headern och på de tillåtna script- respektive style-elementen. Om din widget använder en iframe ersätter du frame-src 'none' med det exakta widget-ursprunget. Om den enbart använder HTTPS-strömning via fetch() eller EventSource utgår WebSocket-ursprunget. Ta bort varje källa som inte behövs efter ett fullständigt funktionstest.

Policyn är en praktisk utgångspunkt, inte en universell mall. En modern strikt CSP kan styra skript ännu hårdare via nonces eller hashar och 'strict-dynamic'. Om det är möjligt utan kompatibilitetsproblem beror på hur laddaren skapar ytterligare skript. Red ut detta flöde med leverantören och testa webbläsare, consent-läge och distributionsvarianter.

Från Report-Only till tvingande policy

Aktivera inte en ny policy utan att ha testat den. W3C-mekanismen Content-Security-Policy-Report-Only rapporterar överträdelser utan att blockera resurser. På så sätt upptäcker du glömda bildvärdar, ett avvikande strömningsursprung eller inline-kod innan användarna påverkas. OWASP rekommenderar HTTP-headern som föredragen leveransväg; till skillnad från meta-elementet stöder den även hela funktionsomfånget.

  1. Skapa inventering: Testa widget-start, första meddelandet, långt strömmande svar, källor, bilder, feedback, handoff och ändring av samtycke (consent) på flera sidtyper.
  2. Rulla ut Report-Only: Börja med den planerade snäva policyn och samla in överträdelser under en begrensad tidsperiod. Filtrera bort webbläsartillägg och andra störsignaler som inte går att reproducera.
  3. Motivera varje värd: Utöka policyn endast om en konkret produktfunktion kräver ursprunget. Undvik jokerspår som reaktion på enstaka rapporter.
  4. Testa automatiserat: Komplettera med end-to-end-tester som skickar ett meddelande, väntar på strömning och laddar en bild. Kontrollera samtidigt webbläsarkonsolen för CSP-överträdelser.
  5. Tvinga och övervaka: Aktivera headern Content-Security-Policy, håll parallellt koll på en ännu strängare Report-Only-variant och jämför felnivåerna.

En stegvis utrullning passar bra ihop med en chatbot i Shadow Mode. För strömningsspecifika mätvärden hjälper artikeln om latensbudgetar och timeouter. CSP-överträdelser bör då gälla som en egen signal: en timeout och en blockerad anslutning kräver olika orsaksanalyser.

Typiska felkonfigurationer

  • För breda källistor: *, https: eller stora jokerspår-domäner gör policyn bekväm, men svag och svår att granska.
  • Bara den synliga starten testas: Widgeten öppnas, men strömning, feedback, bilder eller handoff misslyckas först senare.
  • 'unsafe-inline' blir kvar permanent: Ett kortsiktigt kompatibilitetsstöd ersätts inte av nonces, hashar eller extern kod.
  • CSP förväxlas med åtkomstkontroll: Policyn ersätter inga rättigheter på serversidan, ingen sessionskontroll och inget skydd mot missbruk av verktygsanrop.
  • Rapporter innehåller för mycket data: Fullständiga URL:er, query-parametrar eller användarkontext hamnar i övervakningen i onödan länge.
  • Staging och produktion glider isär: Olika CDN-, API- eller WebSocket-värdar blir synliga först efter go-live.

Även en snäv script-src gör inte automatiskt en tillåten tredje part säker: dess JavaScript körs med de möjligheter som din sida ger den. Kontrollera därför leverantörsbyten, nya underdomäner och laddaruppdateringar på samma sätt som andra säkerhetsrelevanta beroenden. Artikeln om skydd mot prompt injection kompletterar denna webbläsargräns med regler för RAG, verktyg och data.

Checklista före go-live

  • Är alla nödvändiga ursprung dokumenterade och sakligt motiverade utifrån verkliga webbläsarsessioner?
  • Tillåter script-src endast laddaren och kontrollerade skript, utan generellt 'unsafe-inline'?
  • Innehåller connect-src de exakta ursprungen för HTTPS, EventSource och eventuell WSS?
  • Är bild-, style-, font-, frame- och worker-källor separerade och definierade så snävt som möjligt?
  • Genereras nonces på nytt per svar och sätts de endast på betrodda element?
  • Har ändringar i samtycke, långa strömmar, bilder, fel, handoff samt stationära och mobila enheter testats?
  • Har policyn först observerats i Report-Only och därefter tvingats igenom som header?
  • Behandlas CSP-rapporter utan onödiga personuppgifter eller känsliga URL-data?
  • Finns det ett automatiserat regressionstest efter widget- eller infrastrukturuppdateringar?

Slutsats

En bra CSP för webbplats-chatbottar är inte en samling undantag, utan en teknisk karta över tillåtna vägar i webbläsaren. Separera laddare, API, strömning, bilder och iframe-resurser, tillåt exakta ursprung och inför policyn först i Report-Only-läge. På så sätt fortsätter widgeten att fungera, samtidigt som oväntade skript och anslutningar får betydligt mindre handlingsutrymme.

Källor

Förvandla webbplatsbesök till bättre konversationer

Lansera en AI-chatbot som är användbar från dag ett

Träna ChatReact med din webbplats, dokument och godkända fakta så att besökare får snabbare svar och ditt team får färre repetitiva förfrågningar.

Relaterade artiklar

Fortsätt läsa