A CSP-nk · soronként · miért van benne minden direktíva
A magyar piacon látott CSP-k vagy túl lazák, hogy számítsanak, vagy olyan szigorúak, hogy az oldal eltörik. Itt az, amit mi szállítunk · annotálva.
A magyar piacon látott CSP-k vagy túl lazák, hogy számítsanak, vagy olyan szigorúak, hogy az oldal eltörik. Itt az, amit mi szállítunk · annotálva.
A magyar piacon auditált produkciós CSP-k két táborba esnek. Az 'unsafe-inline mindenhol' fajta, ami csak elnémítja a böngésző-figyelmeztetést és kb. semmit sem zár el. És a 'kimásoltam egy hardening-guide-ból' fajta, ami olyan szigorú, hogy az első analitika-tag-en eltörik az oldal. Egyik sem segít.
Itt a CSP, amit szállítunk. Nem a leg-szigorúbb létező. A legszigorúbb olyan, ami mellett egy valódi termék · analitikával, fizetéssel, beágyazott videóval, CMS-szel · még működik. Minden direktíva mellett megjegyzés: mit blokkol, miért hagytuk benne, mi lenne a rossz default.
Content-Security-Policy:
default-src 'none';
script-src 'self' 'nonce-{NONCE}' 'strict-dynamic' https:;
style-src 'self' 'nonce-{NONCE}';
img-src 'self' data: https://images.example.com https://www.gstatic.com;
font-src 'self' data: https://fonts.gstatic.com;
connect-src 'self' https://api.example.com https://*.sentry.io https://www.google-analytics.com;
frame-src 'self' https://js.stripe.com https://www.youtube-nocookie.com;
frame-ancestors 'none';
form-action 'self';
base-uri 'self';
object-src 'none';
media-src 'self' https://videos.example.com;
worker-src 'self' blob:;
manifest-src 'self';
upgrade-insecure-requests;
report-to csp-endpointNullán indulunk. Amit nem engedélyeztünk explicit, az tilos. A másik default · `default-src 'self'` · szigorúbbnak hangzik, mint amilyen, mert csendben enged fetch-et, képet, fontot, frame-et és worker-t a saját origin-ról anélkül, hogy minden felületre rákényszerítene gondolkodni. A `'none'` arra kényszerít, hogy minden direktíva tudatos döntés legyen.
Három dolog ezen a soron: `'self'`, kérésenkénti `'nonce-{NONCE}'`, és `'strict-dynamic'`. A nonce-ot szerver-oldalon generáljuk, és minden inline `<script>`-re ráütjük; a `strict-dynamic` továbbviszi azokra, amiket dinamikusan injektálnak. A végén az `https:` fallback olyan böngészőknek, amelyek a `strict-dynamic`-et figyelmen kívül hagyják (régi Safari főleg) · ahol számít, ott a `strict-dynamic` felülírja.
Miért nem host-allowlist? Mert a `script-src`-re vonatkozó allowlistek évek óta törve vannak · kutatók kimutatták, hogy JSONP-végpontok és AngularJS-host-bypass-ok a legtöbb allowlistet hatástalanná teszik. A nonce + `strict-dynamic` túléli ezt.
Ha `'unsafe-inline'` vagy `'unsafe-eval'` van a `script-src`-ben, nincs CSP-d. Csak egy matrica, amin az áll, hogy CSP. Javítsd meg a mögötte lévő inline scripteket, és vedd ki a direktívát.
Ugyanaz a forma, mint a `script-src`-é: saját origin plusz kérésenkénti nonce. Inline kritikus CSS-t (LCP-nyereség) tudunk küldeni anélkül, hogy a policy-t feladnánk. Tailwind, CSS-in-JS, és a Next.js streaming style-chunkja is ráüti a nonce-ot, ha jól van bekötve.
A képek gyakori csempész-felület · pixel-tracker, beacon-callback, GET-en kiszivárgó adat. Engedjük az origint, az image CDN-t, és egy harmadik felet, amiben bízunk (itt a Google statikus eszközei, amit egy sign-in widget kér). A `data:` benne van, mert emoji és kis inline ikon ezt használja; ha nem kell, vedd ki.
Saját-hosted font teljesítményben és privacy-ben nyer. Az egy Google fonts engedmény, amikor a designer nemet mond. Ha minden fontot saját-hostolsz, ez a sor `font-src 'self' data:`-re zsugorodik.
Ezt használja a `fetch`, az `XMLHttpRequest`, az `EventSource`, a WebSocket és a beacon. Hagyd ki, és `fetch`-szel ingyen kiszivárog az adat. Listázunk: API-origin, hibajelentő (Sentry), analitika. Bármi más 'miért hív ez az oldal arra?' incidens.
A `frame-src` az, kit engedünk az oldalainkba: Stripe checkout iframe, beágyazott YouTube cookie nélkül. A `frame-ancestors 'none'` az, ki tehet minket frame-be · senki. Ez kiváltja az `X-Frame-Options: DENY`-t, és blokkolja a clickjacking-et. A két direktíva hasonlóan hangzik és ellentétes dolgokat csinál.
A `form-action 'self'` a 'injektált form más helyre POST-ol' kategóriát zárja el. A `base-uri 'self'` megakadályozza, hogy egy bejutó `<base href>` átírja az összes relatív URL-t. Mindkettő olcsó, mindkettő valódi hibaosztályt zár.
Az `object-src 'none'` kiöli az `<object>`-et és az `<embed>`-et · a Flash halott, de a felület még él a böngészőkben, és ismert XSS-emelő. A `media-src` a `<video>` és `<audio>` · a videós CDN-t engedjük explicit.
Service worker, web worker, shared worker · mind a `worker-src`-ben. `'self'` plusz `blob:`, mert pár lib `Blob`-URL-ből indít workert. A `manifest-src 'self'` a PWA-manifestet fedi.
Csendben átírja a maradék `http://` kérést `https://`-re. Olcsó biztosíték, ha valamelyik third-party SDK `http://` URL-t szállít a payloadban. Ha HSTS-ed is van (legyen), ez a kettő párba megy.
Sértéseket a `Report-To`-ra (és `Reporting-Endpoints`-ra az újabb headerre) küldünk. Enélkül nem tudod, mit blokkol a CSP-d; csak akkor, amikor egy felhasználó beír support-ra, hogy elromlott valami. Tedd a riportokat egy DB-be, dedupolj direktíva + blokkolt URI alapján, és új jelekre riassz.
Reporting-Endpoints: csp-endpoint="https://api.example.com/csp/report"
Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"https://api.example.com/csp/report"}]}Ha a csapat tiltakozik a bevezetési költség miatt, futtasd két hétig report-only-ban, és mutasd a logot. Az első analitika-tag exfil-kísérlete vagy egy `data:` URL, ami JS-t próbál tölteni · meglesz a meggyőzés.
A CSP nem jelölőnégyzet. Szerződés arról, milyen kód fut az origin-eden. A fenti verzió nem a leg-paranoiásabb · az, ami mellett egy valós termék fenntartható, hamis pozitívok nélkül. Ami túléli: `default-src 'none'`, nonce + `strict-dynamic` script-re és style-ra, explicit allowlist a hálózati felületekre, és valódi reporting endpoint mögötte. Minden más ennek a négy lépésnek a variációja.

Alapító, DField Solutions
Pénzügyi cégeknél és kreátor-eszközöknél is építettem már olyan rendszereket, amik nap mint nap élesben futnak. Budapesttől San Franciscóig · startupoknak és nagyobb vállalatoknak egyaránt.
Beszéljünk a projektedről. 30 perc, nincs kötelezettség.