Beteszed az ffmpeg.wasm-et vagy egy sql.js-alapú in-browser adatbázist, és az első hívásnál elszáll: SharedArrayBuffer is not defined. Rákeresel, találsz egy Stack Overflow választ, bemásolod a két fejlécet, működik a demó. Másnap meg kiderül, hogy a Google-bejelentkezés popupja üresen marad, mert a window.opener hirtelen null. Ez a cikk arról szól, mi történik ilyenkor a motorháztető alatt, és miért nem véletlen baleset, hanem szándékos csereüzlet.

Miért kellett ide egyáltalán két fejléc
2018 januárjában, a Spectre nyilvánosságra hozatala után minden nagyobb böngészőgyártó egyszerre kapcsolta ki a SharedArrayBuffer-t. A probléma nem a szinkronizált memóriával volt, hanem azzal, hogy elég pontos időméréssel a JavaScript ki tudta silabizálni, mi van a processz memóriájában – a Spectre pont ezt a fajta oldalcsatornás olvasást használja ki. Egy megosztott, pontosan mérhető memóriaterület ehhez tökéletes eszköz.
A SharedArrayBuffer viszont nem opcionális parádéfunkció: WebAssembly-szálazáshoz, multi-threaded kodekekhez és hasonló, valódi teljesítményigényes feladatokhoz szinte nélkülözhetetlen. A böngészőgyártók ezért nem törölték, hanem feltételhez kötötték a visszahozatalát. A Chrome fejlesztői blogja szerint a Chrome 88 Androidon, a Chrome 92 pedig (2021 májusában) desktopon már csak akkor engedi újra, ha az oldal úgynevezett cross-origin isolated állapotban fut. Ezt az állapotot a COOP és a COEP fejléc együtt hozza létre.
Amit a cross-origin isolation kinyit
Ha self.crossOriginIsolated értéke true, három dolog válik elérhetővé, amit egyébként a böngésző zárva tart:
SharedArrayBufferlétrehozása és worker közötti megosztásaperformance.measureUserAgentSpecificMemory()– tényleges memóriahasználat lekérdezése- nagy felbontású, nem mesterségesen zajosított időzítők (pl. pontosabb
performance.now())
Ehhez az MDN dokumentációja szerint pontosan ez a fejlécpár kell:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Egy sima Node http-szerveren ez ennyi (kipróbálva, ténylegesen ezt a két fejlécet küldi vissza):
import { createServer } from 'node:http';
createServer((req, res) => {
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
res.end('<script>console.log(self.crossOriginIsolated)</script>');
}).listen(8931);
A COOP ára: elszakad a window.opener
A Cross-Origin-Opener-Policy négy értéket ismer. Az alapértelmezett unsafe-none semmit nem változtat. A same-origin viszont külön böngészési kontextus-csoportba teszi a dokumentumot minden olyan ablaktól, amivel nem azonos eredetű – ha ez a dokumentum nyitott egy cross-origin popupot (vagy őt nyitotta meg egy), a window.opener mindkét irányból null-ra vált. Pontosan ez töri el a klasszikus OAuth- és fizetési popup-mintát, ahol a bejelentkeztető ablak window.opener.postMessage(...)-al vagy közvetlen DOM-hozzáféréssel szól vissza a szülőnek.
Van egy harmadik érték, a same-origin-allow-popups, ami épp erre a helyzetre való: megtartja a popupokhoz fűződő kapcsolatot, miközben a többi cross-origin interakciót tiltja. A csapda az, hogy ez nem elég a cross-origin isolationhöz – a web.dev migrációs útmutatója is külön kiemeli, hogy a crossOriginIsolated ma kizárólag szigorú same-origin mellett lesz true. Vagyis nincs ingyenebéd: vagy megtartod a popup-alapú bejelentkeztetést, vagy megkapod a SharedArrayBuffer-t, a kettő együtt (egyelőre) nem megy natívan.
A COEP ára: minden beágyazott erőforrásnak be kell jelentkeznie
A Cross-Origin-Embedder-Policy require-corp értéke mellett minden no-cors módban betöltött cross-origin erőforrásnak (kép, iframe, script) vagy azonos eredetűnek kell lennie, vagy a Cross-Origin-Resource-Policy fejléccel kell explicit engedélyt adnia a beágyazásra. Egy térképwidget, egy fizetési iframe vagy egy régi CDN-es kép, ami erről nem tud, egyszerűen nem töltődik be – nincs hibaüzenet a UI-on, csak eltűnik a tartalom.
Erre való a harmadik COEP-érték, a credentialless: a cross-origin erőforrás CORP fejléc nélkül is betölthető, cserébe a kérés cookie-k nélkül megy ki, és a válasz sütijeit a böngésző eldobja. A MDN kompatibilitási adatai szerint ez Chrome 96 óta és Firefox 119 óta létezik, a Safari viszont még nem támogatja – tehát nem univerzális mentőöv, csak Chromium- és Firefox-alapú böngészőkben old meg gondot.
Hogyan vezesd be anélkül, hogy bármit eltörnél
A gyakorlatban nem kell az egész alkalmazást cross-origin isolate-elni. Ha csak egy konkrét oldal (egy videóvágó, egy in-browser adatbázis-eszköz) igényli a SharedArrayBuffer-t, érdemesebb azt külön útvonalra vagy aldomainre tenni, és csak arra a válaszra rátenni a két fejlécet – a bejelentkezés és a fizetési folyamat maradhat érintetlen a fő alkalmazásban.
Ha mégis az egész oldalt szeretnéd átállítani, a COEP-nek van megfigyelő üzemmódja: a Cross-Origin-Embedder-Policy-Report-Only fejléc ugyanazt a szabályt futtatja, de nem blokkol – csak riportot küld arról, mi törne el, ha élesítenéd. Ezzel előre feltérképezheted, mely beágyazott erőforrások hiányoznak a CORP fejlécből, mielőtt bárkit is elzárnál tőlük. Utána a legjobb ellenőrzés kliensoldalon egyetlen sor: if (self.crossOriginIsolated) { ... } – így a kódod ott is fut, ahol a fejlécek valamiért mégsem érnek célba (pl. egy proxy leveszi őket).
A tanulság egyszerű: a COOP és a COEP nem egy újabb biztonsági checkbox, amit bepipálsz és elfelejtesz. Konkrét, tervezhető funkcionális árat kérnek a SharedArrayBuffer-ért – és ha ezt előre tudod, jóval kevesebb idő megy el azon, hogy miért tűnt el hétfőn a bejelentkeztető ablak.