Nagłówek odpowiedzi HTTP Connection-Allowlist ogranicza komunikację dokumentu lub skryptu service worker tylko do określonej listy punktów końcowych sieci. W przeciwieństwie do standardu Content Security Policy (CSP), który wymaga utrzymywania oddzielnych dyrektyw dla poszczególnych zasobów (connect-src, img-src, script-src, font-src, form-action) i nie zarządza spekulacyjnymi ani innymi niż Fetch kanałami sieciowymi, takimi jak <link rel="dns-prefetch">, <link rel="preconnect"> czy WebRTC, Connection-Allowlist wymusza stosowanie jednej listy dozwolonych w warstwie sieciowej we wszystkich połączeniach wychodzących inicjowanych przez kontekst (fetch(), XMLHttpRequest, zasoby podrzędne, navigator.sendBeacon(), <a ping>, nawigacja najwyższego poziomu i nawigacja w ramkach, wskazówki dotyczące zasobów, reguły spekulacji, WebSocket, WebTransport i WebRTC).
Jak to zrobić?
- Zserializuj listę dozwolonych jako listę wewnętrzną RFC 9651
(...): sformatujConnection-Allowlist(lubConnection-Allowlist-Report-Only) jako pojedynczą listę elementów rozdzielonych spacjami w nawiasach. Uwzględnij tokenresponse-originbez cudzysłowu, aby zezwolić na źródło dokumentu lub skryptu, a następnie ciągi znakówURLPatternw cudzysłowie dla każdego prawidłowego zewnętrznego punktu końcowego, z którym łączy się kontekst lub do którego przechodzi (interfejsy API, sieci CDN obrazów/czcionek i wychodzące elementy docelowe<a href>,<form action>, OAuth lub przekierowania płatności). Podczas dynamicznego tworzenia wzorców z konfiguracji lub obiektówURLznormalizujws:/wss:dohttp:/https:i usuń końcowe ukośniki z adresów URL zawierających tylko źródło (ponieważnew URL('https://api.example.com').hrefdołącza/, co ogranicza dopasowanieURLPatterntylko do ścieżki głównej/). - Zakres oprogramowania pośredniczącego według
Sec-Fetch-Desti pracowników: przeglądarki uwzględniająConnection-Allowlisttylko w odpowiedziach dokumentu/nawigacji i odpowiedziach skryptu pracownika (wartościSec-Fetch-Dest:document,iframe,worker,sharedworkeriserviceworker). Podczas zmieniania nagłówków wedługSec-Fetch-DestuwzględnijVary: Sec-Fetch-Dest, aby udostępnione pamięci podręczne nie wyświetlały odpowiedzi bez nagłówka. Używaj pasującej listy dozwolonych w skryptach Dedicated i Shared Worker, aby skrypty na przejętych stronach nie mogły wyodrębniać danych przezworker.postMessage(). Skrypt service worker pobiera dane z sieci w przypadku braku trafień w pamięci podręcznej i wstępnego wczytywania nawigacji, dlatego skonfiguruj jego nagłówekConnection-Allowlistjako nadzbiór list dozwolonych wszystkich dokumentów, które kontroluje. - Skonfiguruj raportowanie naruszeń i wdrażanie etapowe lub podwójne: zdefiniuj nagłówek
Reporting-Endpointsi dołącz parametr; report-to=<endpoint-name>bez cudzysłowu po zamykającym). Podczas początkowego wdrażania możesz wysyłać tylkoConnection-Allowlist-Report-Onlylub wysyłać zarównoConnection-Allowlist(egzekwowanie podstawowej listy dozwolonych), jak iConnection-Allowlist-Report-Only(kontrola bardziej rygorystycznej listy dozwolonych) w tej samej odpowiedzi. Raporty o naruszeniach są dostarczane z"type": "connection-allowlist"i treścią{ url, connection, allowlist, disposition }(gdzieconnectionto zablokowany docelowy adres URL lub"webrtc", adispositionto"enforce"lub"report"). - Uwzględnij domyślne przekierowanie i blokowanie WebRTC oraz obsługuj błędy klienta: domyślnie
Connection-Allowlistblokuje wszystkie przekierowania HTTP3xx(redirects=block, nawet podczas przekierowywania między adresami URL z listy dozwolonych lub adresami URL źródła) i cały ruchRTCPeerConnection(webrtc=block). Dodaj; redirects=allowtylko wtedy, gdy zaufane punkty końcowe wymagają przekierowań po stronie serwera (początkowe żądanie musi być zgodne z listą dozwolonych) lub; webrtc=allow, gdy wymagany jest WebRTC. Na kliencie opakuj wychodzące wywołaniafetch()wtry...catchi obsługuj zdarzeniaWebSocketerror, aby żądania odrzucone z powoduTypeError(net::ERR_NETWORK_ACCESS_REVOKED) zwracały obsługiwany stan błędu i aktualizowały stan interfejsu, zamiast powodować nieobsługiwane odrzucenia.
Przykładowy kod
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 };
}
}
Sprawdzone metody
- DO formatuje
Connection-Allowlistjako pojedynczą listę wewnętrzną RFC 9651(...)elementów rozdzielonych spacjami. Jeśli niepusty nagłówekConnection-Allowlistnie przejdzie analizy pola strukturalnego najwyższego poziomu (np. pominięto(...)lub użyto pojedynczych cudzysłowów'...'), przeglądarka nie otworzy się z pustą listą dozwolonych i zablokuje wszystkie wychodzące połączenia sieciowe. UżyjConnection-Allowlist: (), jeśli chcesz celowo unieważnić cały dostęp do sieci po wczytaniu (schematy lokalnedata:,blob:,about:ifilesystem:pozostają dozwolone). - POMIŃ ukośnik na końcu wzorców adresów URL obejmujących całe źródło (
"https://api.example.com") lub użyj jawnego symbolu wieloznacznego ("https://api.example.com/*"), aby dozwolone były ścieżki podrzędne, takie jak/v1/users. Usuń końcowe ukośniki dodane przeznew URL(origin).hrefprzed serializacją wzorców. - DODAJ do listy dozwolonych punkty końcowe
WebSocket(ws://iwss://) przy użyciu wzorców"http://..."i"https://..."wConnection-Allowlist, ponieważ przeglądarka normalizuje schematy WebSocket do HTTP/HTTPS przed dopasowaniem. - DO include expected external navigation targets (
<a href>,<form action>, OAuth/SSO providers, payment checkouts) in a restricted document's allowlist, and remember that the defaultredirects=blockblocks even same-origin3xxredirects unless; redirects=allowis specified. - NIE umieszczaj wartości parametrów
response-originani wartości parametrów listy wewnętrznej (; report-to=conn-reports,; redirects=allow,; webrtc=allow) w cudzysłowie ani nie oddzielaj elementów wewnątrz(...)przecinkami. - NIE używaj niestandardowych grup wyrażeń regularnych, takich jak
(\d+), ani ścieżek względnych, takich jak"/api/*", we wzorcach adresów URL. Parser usługi sieciowej przeglądarki odrzuca grupy wyrażeń regularnych i wzorce bez schematu i nazwy hosta. - NIE polegaj jeszcze na eksperymentalnym atrybucie
<iframe connectionAllowlist="...">ani na wbudowanym nagłówku egzekwowaniaAllow-Connection-Allowlist-Fromw środowisku produkcyjnym, ponieważ wbudowane egzekwowanie nie jest domyślnie włączone w przeglądarkach wysyłkowych.
Strategia zastępcza
Ponieważ zasady Connection-Allowlist są egzekwowane w warstwie sieciowej przeglądarki za pomocą nagłówka odpowiedzi HTTP, nie można ich uzupełnić w kodzie JavaScript po stronie klienta. Jeśli Twój cel Baseline obejmuje przeglądarki, które nie obsługują jeszcze Connection-Allowlist, połącz Connection-Allowlist z nagłówkiem Content-Security-Policy (CSP) (oraz elementem <meta http-equiv="Content-Security-Policy"> w przypadku ograniczeń pobierania i pobierania zasobów podrzędnych na poziomie dokumentu, gdy nie można skonfigurować nagłówków odpowiedzi HTTP):
- Dodatkowa dogłębna obrona: obsługiwane przeglądarki egzekwują obie zasady jednocześnie –
Connection-Allowlistblokuje kanały eksfiltracji, które nie są objęte zasadami CSP (dns-prefetch,preconnect, WebRTC i przekierowania), a zasady CSP ograniczająfetch(), zasoby podrzędne i nawigację<form action>w przeglądarkach bezConnection-Allowlist. - Uwzględnij
form-actioniworker-src: w CSPdefault-srcnie zastępuje<form action>; zawsze uwzględniajform-action. Uwzględnijworker-src 'self'(z wyjątkiemblob:), aby strony z ograniczeniami nie mogły tworzyć SharedWorkersblob:w celu obejścia ograniczeń na poziomie dokumentu. - Mapowanie dozwolonych źródeł na dyrektywy CSP dotyczące poszczególnych zasobów: w przeciwieństwie do
Connection-Allowlist(który używa jednej listy punktów końcowych i nie zarządza znacznikami wbudowanymi)default-src 'self'w CSP ogranicza każdy typ zasobu (connect-src,img-src,style-src,font-src,script-src) i blokuje wbudowane bloki<style>i<script>, chyba że są autoryzowane za pomocą skrótów lub liczb jednorazowych. - Uwzględnij
wss://w CSPconnect-src:Connection-Allowlistdopasowuje połączeniawss://do wzorców"https://...", ale CSPconnect-srcw środowiskach z różnymi przeglądarkami powinien wyraźnie wymieniać źródławss://, gdy używane są WebSockets.
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'"
/>