Der HTTP-Antwortheader Connection-Allowlist beschränkt die Kommunikation eines Dokuments oder Workers auf eine explizite Liste von Netzwerkendpunkten. Im Gegensatz zur Content Security Policy (CSP), bei der separate ressourcenspezifische Anweisungen (connect-src, img-src, script-src, font-src, form-action) verwaltet werden müssen und die keine spekulativen oder nicht auf Fetch basierenden Netzwerkkanäle wie <link rel="dns-prefetch">, <link rel="preconnect"> oder WebRTC regelt, erzwingt Connection-Allowlist eine einzige Zulassungsliste auf Netzwerkebene für alle ausgehenden Verbindungen, die vom Kontext initiiert werden (fetch(), XMLHttpRequest, Unterressourcen, navigator.sendBeacon(), <a ping>, Navigationen auf oberster Ebene und Frame-Navigationen, Ressourcenhinweise, Spekulationsregeln, WebSocket, WebTransport und WebRTC).
Implementierung
- Zulassungsliste als RFC 9651-Inner List
(...)serialisieren: Formatieren SieConnection-Allowlist(oderConnection-Allowlist-Report-Only) als einzelne Liste mit durch Leerzeichen getrennten Elementen in Klammern. Fügen Sie das Tokenresponse-originohne Anführungszeichen ein, um den Ursprung des Dokuments oder Workers zuzulassen, gefolgt von Strings in doppelten Anführungszeichen (URLPattern) für jeden legitimen externen Endpunkt, zu dem der Kontext eine Verbindung herstellt oder zu dem er weiterleitet (APIs, Bild-/Schriftart-CDNs und ausgehende<a href>-,<form action>-, OAuth- oder Zahlungsweiterleitungsziele). Wenn Sie Muster dynamisch aus Konfigurations- oderURL-Objekten erstellen, normalisieren Siews:/wss:zuhttp:/https:und entfernen Sie nachgestellte Schrägstriche bei URLs, die nur den Ursprung enthalten (danew URL('https://api.example.com').href/anhängt, wodurch derURLPattern-Abgleich nur auf den Stammpfad/beschränkt wird). - Middleware nach
Sec-Fetch-Desteingrenzen und Worker abdecken: Browser berücksichtigenConnection-Allowlistnur bei Dokument-/Navigationsantworten und Worker-Skriptantworten (Sec-Fetch-Dest-Wertedocument,iframe,worker,sharedworkerundserviceworker). Wenn Sie Header nachSec-Fetch-Destvariieren, fügen SieVary: Sec-Fetch-Desthinzu, damit freigegebene Caches keine Antworten ohne Header bereitstellen. Stellen Sie eine passende Zulassungsliste für dedizierte und freigegebene Worker-Skripts bereit, damit kompromittierte Seitenskripts keine Daten überworker.postMessage()exfiltrieren können. Da ein Service Worker bei Cache-Fehlern und Navigations-Preloads eigene Netzwerkabrufe durchführt, sollten Sie seineConnection-Allowlistals Superset der Zulassungslisten aller von ihm gesteuerten Dokumente konfigurieren. - Verstoßberichte und gestaffelten oder dualen Roll-out konfigurieren: Definieren Sie einen
Reporting-Endpoints-Header und hängen Sie nach dem schließenden)einen; report-to=<endpoint-name>-Parameter ohne Anführungszeichen an. Sie können während des ersten Rollouts nurConnection-Allowlist-Report-Onlysenden oder beideConnection-Allowlist(Erzwingen einer Baseline-Zulassungsliste) undConnection-Allowlist-Report-Only(Prüfen einer strengeren Kandidaten-Zulassungsliste) in derselben Antwort senden. Verstoßberichte werden mit"type": "connection-allowlist"und Textkörper{ url, connection, allowlist, disposition }gesendet (wobeiconnectiondie blockierte Ziel-URL oder"webrtc"ist unddispositionentweder"enforce"oder"report"ist). - Standardweiterleitung und WebRTC-Blockierung berücksichtigen und Clientfehler beheben: Standardmäßig blockiert
Connection-Allowlistalle3xx-HTTP-Weiterleitungen (redirects=block, auch bei Weiterleitungen zwischen URLs auf der Zulassungsliste oder URLs mit demselben Ursprung) und den gesamtenRTCPeerConnection-Traffic (webrtc=block). Hängen Sie; redirects=allownur an, wenn vertrauenswürdige Endpunkte serverseitige Weiterleitungen erfordern (die ursprüngliche Anfrage muss weiterhin mit der Zulassungsliste übereinstimmen), oder; webrtc=allow, wenn WebRTC erforderlich ist. Verpacken Sie auf dem Client ausgehendefetch()-Aufrufe intry...catchund verarbeiten SieWebSocket-error-Ereignisse, damit Anfragen, die mitTypeError(net::ERR_NETWORK_ACCESS_REVOKED) abgelehnt werden, einen verarbeiteten Fehlerstatus zurückgeben und den UI-Status aktualisieren, anstatt nicht verarbeitete Ablehnungen zu verursachen.
Beispielcode
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 };
}
}
Best Practices
- DO formatieren Sie
Connection-Allowlistals einzelne RFC 9651-interne Liste(...)mit durch Leerzeichen getrennten Elementen. Wenn ein nicht leererConnection-Allowlist-Header die Parsing-Prüfung für strukturierte Felder auf oberster Ebene nicht besteht (z. B. wenn(...)weggelassen wird oder einfache Anführungszeichen'...'verwendet werden), schlägt der Browser mit einer leeren Zulassungsliste fehl und blockiert alle ausgehenden Netzwerkverbindungen. Verwenden SieConnection-Allowlist: (), wenn Sie den gesamten Netzwerkzugriff nach dem Laden absichtlich widerrufen möchten. Lokale Schemas (data:,blob:,about:undfilesystem:) bleiben zulässig. - DO omit the trailing slash on origin-wide URL patterns (
"https://api.example.com") or use an explicit wildcard ("https://api.example.com/*") so subpaths like/v1/usersare permitted. Entfernen Sie abschließende Schrägstriche, die vonnew URL(origin).hrefhinzugefügt wurden, bevor Sie Muster serialisieren. - LASSEN Sie
WebSocket-Endpunkte (ws://undwss://) mit den Mustern"http://..."und"https://..."inConnection-Allowlistzu, da der Browser WebSocket-Schemas vor dem Abgleich in HTTP/HTTPS normalisiert. - Fügen Sie erwartete externe Navigationsziele (
<a href>,<form action>, OAuth-/SSO-Anbieter, Zahlungsabwicklung) der Zulassungsliste eines eingeschränkten Dokuments hinzu. Beachten Sie, dass die Standardeinstellungredirects=blocksogar3xx-Weiterleitungen mit derselben Quelle blockiert, sofern nicht; redirects=allowangegeben ist. - Setzen Sie
response-origin- oder Parameterwerte für innere Listen (; report-to=conn-reports,; redirects=allow,; webrtc=allow) NICHT in Anführungszeichen und trennen Sie Elemente innerhalb von(...)nicht durch Kommas. - Verwenden Sie KEINE benutzerdefinierten regulären Ausdrucksgruppen wie
(\d+)oder relative Pfade wie"/api/*"in URL-Mustern. Der Netzwerkdienst-Parser des Browsers lehnt Regex-Gruppen und Muster ohne Schema und Hostnamen ab. - Verlassen Sie sich in der Produktion NICHT auf das experimentelle Attribut
<iframe connectionAllowlist="...">oder den eingebetteten HeaderAllow-Connection-Allowlist-Fromfür die Erzwingung, da die eingebettete Erzwingung in Browsern, die ausgeliefert werden, nicht standardmäßig aktiviert ist.
Fallback-Strategie
Da Connection-Allowlist auf der Browser-Netzwerkschicht über einen HTTP-Antwortheader erzwungen wird, kann es nicht in clientseitigem JavaScript polyfilled werden. Wenn Ihr Baseline-Ziel Browser umfasst, die Connection-Allowlist noch nicht unterstützen, kombinieren Sie Connection-Allowlist mit einem Content-Security-Policy-Header (CSP) (und einem <meta http-equiv="Content-Security-Policy">-Element für Abruf- und Unterressourcenbeschränkungen auf Dokumentebene, wenn HTTP-Antwortheader nicht konfiguriert werden können):
- Additive Defense-in-Depth: Unterstützende Browser erzwingen beide Richtlinien gleichzeitig.
Connection-Allowlistblockiert Exfiltrationskanäle, die nicht durch CSP abgedeckt sind (dns-prefetch,preconnect, WebRTC und Weiterleitungen), während CSPfetch(), Unterressourcen und<form action>-Navigationen in Browsern ohneConnection-Allowlisteinschränkt. form-actionundworker-srcexplizit abdecken: In CSP wirddefault-srcnicht für<form action>-Einreichungen verwendet. Schließen Sieform-actionimmer explizit ein. Fügen Sieworker-src 'self'(ohneblob:) ein, damit eingeschränkte Seiten keineblob:-SharedWorker erstellen können, um Einschränkungen auf Dokumentebene zu umgehen.- Zulässige Ursprünge den CSP-Anweisungen pro Ressource zuordnen: Im Gegensatz zu
Connection-Allowlist(wo eine einzelne Endpunktliste verwendet wird und Inline-Markup nicht geregelt wird) schränktdefault-src 'self'in CSP jeden Ressourcentyp (connect-src,img-src,style-src,font-src,script-src) ein und blockiert Inline-<style>- und<script>-Blöcke, sofern sie nicht über Hashes oder Nonces autorisiert sind. wss://in CSPconnect-srcaufnehmen: WährendConnection-Allowlistwss://-Verbindungen mit"https://..."-Mustern abgleicht, sollten in CSPconnect-srcin browserübergreifenden Umgebungen explizitwss://-Ursprünge aufgeführt werden, wenn WebSockets verwendet werden.
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'"
/>