De HTTP-reactieheader Connection-Allowlist beperkt een document of worker tot communicatie met alleen een expliciete lijst van netwerkeindpunten. In tegenstelling tot Content Security Policy (CSP), waarvoor afzonderlijke richtlijnen per bron (connect-src, img-src, script-src, font-src, form-action) moeten worden onderhouden en die geen speculatieve of niet-Fetch-netwerkkanalen zoals <link rel="dns-prefetch">, <link rel="preconnect"> of WebRTC beheert, dwingt Connection-Allowlist één toelatingslijst op netwerklaag af voor alle uitgaande verbindingen die door de context worden gestart (fetch(), XMLHttpRequest, subbronnen, navigator.sendBeacon(), <a ping>, navigatie op het hoogste niveau en in frames, brontips, speculatieregels, WebSocket, WebTransport en WebRTC).
Hoe wordt dit geïmplementeerd
- Serialiseer de toelatingslijst als een RFC 9651 Inner List
(...): Maak de indelingConnection-Allowlist(ofConnection-Allowlist-Report-Only) als één lijst tussen haakjes met door spaties gescheiden items. Neem de niet-geciteerde tokenresponse-originop om de eigen oorsprong van het document of de worker toe te staan, gevolgd door tekenreeksen tussen dubbele aanhalingstekensURLPatternvoor elk legitiem extern eindpunt waarmee de context verbinding maakt of waarnaar de context navigeert (API's, CDN's voor afbeeldingen/lettertypen en uitgaande<a href>,<form action>, OAuth of omleidingstargets voor betalingen). Als je patronen dynamisch maakt op basis van configuratie- ofURL-objecten, normaliseer jews:/wss:naarhttp:/https:en verwijder je de afsluitende slashes in URL's die alleen de oorsprong bevatten (omdatnew URL('https://api.example.com').href/toevoegt, waardoorURLPattern-overeenkomsten worden beperkt tot alleen het hoofdpad/). - Middleware bereik op
Sec-Fetch-Desten dek werkers: Browsers respecterenConnection-Allowlistalleen voor document-/navigatiereacties en reacties van werkerscripts (Sec-Fetch-Dest-waardendocument,iframe,worker,sharedworkerenserviceworker). Als je headers varieert opSec-Fetch-Dest, neem jeVary: Sec-Fetch-Destop zodat gedeelde caches geen reacties zonder headers leveren. Lever een overeenkomende toelatingslijst aan voor Dedicated en Shared Worker-scripts, zodat gehackte paginascripts geen gegevens kunnen exfiltreren viaworker.postMessage(). Omdat een servicemedewerker eigen netwerkfetches uitvoert bij ontbrekende cache-items en navigatie-preloads, stel je deConnection-Allowlistin als een superset van de toelatingslijsten van alle documenten die de servicemedewerker beheert. - Rapportage van schendingen en gefaseerde of dubbele implementatie instellen: Definieer een
Reporting-Endpoints-koptekst en voeg een niet-geciteerde; report-to=<endpoint-name>-parameter toe na de afsluitende). Je kuntConnection-Allowlist-Report-Onlyalleen sturen tijdens de eerste implementatie of beideConnection-Allowlist(een basislijst met toegestane entiteiten afdwingen) enConnection-Allowlist-Report-Only(een strengere kandidatenlijst met toegestane entiteiten controleren) sturen in dezelfde reactie. Schendingsrapporten worden geleverd met"type": "connection-allowlist"en hoofdtekst{ url, connection, allowlist, disposition }(waarbijconnectionde geblokkeerde bestemmings-URL of"webrtc"is endisposition"enforce"of"report"is). - Houd rekening met standaard omleidingen en WebRTC-blokkering en verwerk clientfouten: Standaard blokkeert
Connection-Allowlistalle3xxHTTP-omleidingen (redirects=block, zelfs bij omleiding tussen URL's op de toelatingslijst of URL's met dezelfde oorsprong) en al hetRTCPeerConnection-verkeer (webrtc=block). Voeg; redirects=allowalleen toe als vertrouwde eindpunten omleidingen aan de serverzijde vereisen (het eerste verzoek moet nog steeds overeenkomen met de toelatingslijst) of; webrtc=allowals WebRTC is vereist. Verpak op de client uitgaandefetch()-aanroepen intry...catchen verwerkWebSocketerror-gebeurtenissen, zodat verzoeken die worden afgewezen metTypeError(net::ERR_NETWORK_ACCESS_REVOKED) een verwerkte foutstatus retourneren en de UI-status updaten in plaats van onverwerkte afwijzingen te veroorzaken.
Voorbeeldcode
Vary: Sec-Fetch-Dest
Reporting-Endpoints: conn-reports="https://app.example.com/api/security-reports"
Connection-Allowlist: (response-origin "https://api.example.com" "https://realtime.example.com" "https://*.cdn.example.com/*"); report-to=conn-reports
Connection-Allowlist-Report-Only: (response-origin "https://api.example.com/v2/*"); report-to=conn-reports
const ALLOWLIST_DESTS = new Set(['document', 'iframe', 'worker', 'sharedworker', 'serviceworker']);
// Normalize a config URL or URLPattern string for Connection-Allowlist:
// - Rewrites ws:// and wss:// to http:// and https:// (WebSocket matching rule).
// - Strips a bare trailing slash on origin-only URLs (e.g. from new URL(...).href)
// so all subpaths on the origin are permitted instead of only "/".
export function normalizeAllowlistPattern(rawPattern) {
const trimmed = String(rawPattern).trim().replace(/^ws(s?):\/\//i, 'http$1://');
if (!/^https?:\/\//i.test(trimmed)) {
throw new Error(`Pattern must include an http:// or https:// scheme: ${rawPattern}`);
}
if (/[\s,]/.test(trimmed)) {
throw new Error(`Pattern must be a single URLPattern without spaces or commas: ${rawPattern}`);
}
if (/[()]/.test(trimmed)) {
throw new Error(`Custom regex groups are rejected by Connection-Allowlist: ${rawPattern}`);
}
return trimmed.replace(/^(https?:\/\/[^/?#]+)\/$/i, '$1');
}
// Build an RFC 9651 Inner List value for Connection-Allowlist or Connection-Allowlist-Report-Only.
export function buildConnectionAllowlistHeader({
allowSelf = true,
patterns = [],
reportTo,
redirects = 'block',
webrtc = 'block',
} = {}) {
const items = allowSelf ? ['response-origin'] : [];
for (const pattern of patterns) {
const raw = String(pattern).trim();
if (raw === 'response-origin') {
if (!items.includes('response-origin')) items.push('response-origin');
continue;
}
const normalized = normalizeAllowlistPattern(raw);
items.push(`"${normalized.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`);
}
let header = `(${items.join(' ')})`;
if (redirects === 'allow') header += '; redirects=allow';
if (webrtc === 'allow') header += '; webrtc=allow';
if (reportTo) {
const endpointToken = String(reportTo).trim().replace(/^"+|"+$/g, '');
header += `; report-to=${endpointToken}`;
}
return header;
}
// Server or middleware helper applying Connection-Allowlist and Reporting-Endpoints
// on document and worker responses.
export function setConnectionAllowlistHeaders(res, options = {}) {
const {
secFetchDest = 'document',
patterns = ['https://api.example.com', 'https://realtime.example.com', 'https://*.cdn.example.com/*'],
reportOnlyPatterns,
reportEndpointName = 'conn-reports',
reportEndpointUrl = 'https://app.example.com/api/security-reports',
redirects = 'block',
webrtc = 'block',
reportOnly = false,
} = options;
const setHeader = (name, value) =>
typeof res.setHeader === 'function' ? res.setHeader(name, value) : res.headers.set(name, value);
if (typeof res.appendHeader === 'function') {
res.appendHeader('Vary', 'Sec-Fetch-Dest');
} else if (typeof res.headers?.append === 'function') {
res.headers.append('Vary', 'Sec-Fetch-Dest');
} else {
setHeader('Vary', 'Sec-Fetch-Dest');
}
if (secFetchDest && !ALLOWLIST_DESTS.has(String(secFetchDest).toLowerCase())) return;
setHeader('Reporting-Endpoints', `${reportEndpointName}="${reportEndpointUrl}"`);
setHeader(
reportOnly ? 'Connection-Allowlist-Report-Only' : 'Connection-Allowlist',
buildConnectionAllowlistHeader({ allowSelf: true, patterns, reportTo: reportEndpointName, redirects, webrtc }),
);
if (!reportOnly && Array.isArray(reportOnlyPatterns)) {
setHeader(
'Connection-Allowlist-Report-Only',
buildConnectionAllowlistHeader({
allowSelf: true,
patterns: reportOnlyPatterns,
reportTo: reportEndpointName,
redirects,
webrtc,
}),
);
}
}
// Client-side helper that catches blocked outbound requests gracefully without unhandled rejections.
export async function fetchWithPolicyHandling(url, { statusElement, ...fetchOptions } = {}) {
try {
const response = await fetch(url, fetchOptions);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const body = await response.text();
if (statusElement) {
statusElement.textContent = `Allowed: Loaded ${url} (${body.length} bytes).`;
}
return { ok: true, body, error: null };
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
if (statusElement) {
statusElement.textContent = `Blocked: Request to ${url} was rejected by the outbound connection policy (${message}).`;
}
return { ok: false, body: null, error };
}
}
Praktische tips
- WEL: Maak de indeling van
Connection-Allowlistals één RFC 9651 Inner List(...)van door spaties gescheiden items. Als een niet-legeConnection-Allowlist-header de parsering van het gestructureerde veld op het hoogste niveau niet doorstaat (bijvoorbeeld door(...)weg te laten of enkele aanhalingstekens'...'te gebruiken), wordt de browser gesloten met een lege toelatingslijst en worden alle uitgaande netwerkverbindingen geblokkeerd. GebruikConnection-Allowlist: ()als je opzettelijk alle netwerktoegang wilt intrekken na het laden (lokale schema'sdata:,blob:,about:enfilesystem:blijven toegestaan). - WEL: Laat de afsluitende slash weg bij URL-patronen voor de hele oorsprong (
"https://api.example.com") of gebruik een expliciete wildcard ("https://api.example.com/*") zodat subpaden zoals/v1/userszijn toegestaan. Verwijder afsluitende slashes die zijn toegevoegd doornew URL(origin).hrefvoordat patronen worden geserialiseerd. - Zet WEL
WebSocket-eindpunten (ws://enwss://) op de toelatingslijst met de patronen"http://..."en"https://..."inConnection-Allowlist, omdat de browser WebSocket-schema's normaliseert naar HTTP/HTTPS voordat deze worden vergeleken. - Voeg verwachte externe navigatiedoelen (
<a href>,<form action>, OAuth-/SSO-providers, betaalpagina's) toe aan de toelatingslijst van een beperkt document en onthoud dat de standaardredirects=blockzelfs3xx-omleidingen van dezelfde oorsprong blokkeert, tenzij; redirects=allowis opgegeven. - Zet
response-originof parameterwaarden voor interne lijsten (; report-to=conn-reports,; redirects=allow,; webrtc=allow) NIET tussen aanhalingstekens en scheid items in(...)niet met komma's. - Gebruik GEEN aangepaste groepen met reguliere expressies, zoals
(\d+), of relatieve paden, zoals"/api/*", in URL-patronen. De parser van de netwerkservice van de browser weigert regex-groepen en -patronen zonder schema en hostnaam. - Vertrouw nog NIET op het experimentele kenmerk
<iframe connectionAllowlist="...">of de ingesloten handhavingsheaderAllow-Connection-Allowlist-Fromin de productie, omdat ingesloten handhaving niet standaard aanstaat in verzendbrowsers.
Reservestrategie
Omdat Connection-Allowlist wordt afgedwongen op de netwerklaag van de browser via een HTTP-reactieheader, kan deze niet worden gepolyfilled in JavaScript aan de clientzijde. Als je Baseline-doel browsers bevat die Connection-Allowlist nog niet ondersteunen, koppel je Connection-Allowlist aan een Content-Security-Policy-header (CSP) (en een <meta http-equiv="Content-Security-Policy">-element voor ophalen op documentniveau en beperkingen voor subbronnen als HTTP-reactieheaders niet kunnen worden geconfigureerd):
- Additieve diepgaande verdediging: Ondersteunende browsers dwingen beide beleidsregels tegelijkertijd af.
Connection-Allowlistblokkeert niet-CSP-exfiltratiekanalen (dns-prefetch,preconnect, WebRTC en omleidingen), terwijl CSPfetch(), subbronnen en<form action>-navigaties beperkt in browsers zonderConnection-Allowlist. - Dek
form-actionenworker-srcexpliciet af: In CSP valtdefault-srcniet terug voor<form action>-inzendingen. Neemform-actionaltijd expliciet op. Neemworker-src 'self'op (met uitzondering vanblob:), zodat beperkte pagina's geenblob:SharedWorkers kunnen genereren om beperkingen op documentniveau te omzeilen. - Toegestane herkomsten toewijzen aan CSP-instructies per bron: In tegenstelling tot
Connection-Allowlist(die één eindpuntlijst gebruikt en geen inline opmaak beheert), beperktdefault-src 'self'in CSP elk brontype (connect-src,img-src,style-src,font-src,script-src) en blokkeert inline<style>- en<script>-blokken, tenzij deze zijn geautoriseerd via hashes of nonces. - Neem
wss://op in CSPconnect-src: HoewelConnection-Allowlistwss://-verbindingen vergelijkt met"https://..."-patronen, moet CSPconnect-srcin omgevingen met meerdere browsers explicietwss://-oorsprongen vermelden als WebSockets worden gebruikt.
Content-Security-Policy: default-src 'self'; connect-src 'self' https://api.example.com https://realtime.example.com wss://realtime.example.com; img-src 'self' https://*.cdn.example.com data: blob:; form-action 'self'; worker-src 'self'; object-src 'none'; base-uri 'self'; report-to conn-reports
<!-- Document-level fallback when HTTP response headers cannot be set on static hosting -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; connect-src 'self' https://api.example.com https://realtime.example.com wss://realtime.example.com; img-src 'self' https://*.cdn.example.com data: blob:; form-action 'self'; worker-src 'self'; object-src 'none'; base-uri 'self'"
/>