अनुमति वाली सूची का इस्तेमाल करके, आउटबाउंड नेटवर्क कनेक्शन पर पाबंदी लगाना

Connection-Allowlist एचटीटीपी रिस्पॉन्स हेडर, किसी दस्तावेज़ या वर्कर को सिर्फ़ नेटवर्क एंडपॉइंट की साफ़ तौर पर बताई गई सूची के साथ कम्यूनिकेट करने की अनुमति देता है. कॉन्टेंट की सुरक्षा के लिए नीति (सीएसपी) के लिए, हर संसाधन के लिए अलग-अलग डायरेक्टिव (connect-src, img-src, script-src, font-src, form-action) को मैनेज करना ज़रूरी होता है. साथ ही, यह नीति <link rel="dns-prefetch">, <link rel="preconnect"> या WebRTC जैसे अनुमानित या नॉन-फ़ेच नेटवर्क चैनलों को मैनेज नहीं करती. इसके उलट, Connection-Allowlist कॉन्टेक्स्ट (fetch(), XMLHttpRequest, सब-रिसोर्स, navigator.sendBeacon(), <a ping>, टॉप-लेवल और फ़्रेम नेविगेशन, रिसॉर्स हिंट, अनुमानित नियम, WebSocket, WebTransport, और WebRTC) से शुरू किए गए सभी आउटबाउंड कनेक्शन के लिए, नेटवर्क-लेयर की एक ही अनुमति वाली सूची लागू करता है.

इन्हें कैसे लागू करें

  1. अनुमति वाली सूची को RFC 9651 इनर लिस्ट (...) के तौर पर क्रम से लगाएं: Connection-Allowlist (या Connection-Allowlist-Report-Only) को स्पेस से अलग किए गए आइटम की ब्रैकेट वाली सूची के तौर पर फ़ॉर्मैट करें. दस्तावेज़ या वर्कर के अपने ऑरिजिन को अनुमति देने के लिए, बिना कोटेशन वाला टोकन response-origin शामिल करें. इसके बाद, हर उस मान्य बाहरी एंडपॉइंट के लिए डबल-कोटेशन वाली URLPattern स्ट्रिंग शामिल करें जिससे कॉन्टेक्स्ट कनेक्ट होता है या जिस पर नेविगेट करता है (एपीआई, इमेज/फ़ॉन्ट सीडीएन, और आउटबाउंड <a href>, <form action>, OAuth या पेमेंट रीडायरेक्ट टारगेट). कॉन्फ़िगरेशन या URL ऑब्जेक्ट से डाइनैमिक तौर पर पैटर्न बनाते समय, ws:/wss: को http:/https: पर नॉर्मलाइज़ करें. साथ ही, सिर्फ़ ऑरिजिन वाले यूआरएल के आखिर में मौजूद स्लैश हटाएं. ऐसा इसलिए, क्योंकि new URL('https://api.example.com').href, / को जोड़ता है. इससे URLPattern मैचिंग सिर्फ़ रूट पाथ / तक सीमित हो जाती है.
  2. मिडलवेयर को Sec-Fetch-Dest के हिसाब से स्कोप करें और वर्कर को कवर करें: ब्राउज़र, दस्तावेज़/नेविगेशन रिस्पॉन्स और वर्कर स्क्रिप्ट रिस्पॉन्स (Sec-Fetch-Dest वैल्यू document, iframe, worker, sharedworker, और serviceworker) पर सिर्फ़ Connection-Allowlist का पालन करते हैं. Sec-Fetch-Dest के हिसाब से हेडर बदलते समय, Vary: Sec-Fetch-Dest को शामिल करें, ताकि शेयर की गई कैश मेमोरी, बिना हेडर वाले रिस्पॉन्स न दिखाए. डेडिकेटेड और शेयर किए गए वर्कर स्क्रिप्ट पर, अनुमति वाली सूची उपलब्ध कराएं. इससे, हैक किए गए पेज की स्क्रिप्ट, worker.postMessage() के ज़रिए डेटा को बाहर नहीं भेज पाएंगी. सर्विस वर्कर, कैश मेमोरी में मौजूद न होने वाले डेटा और नेविगेशन प्रीलोड के लिए, अपने नेटवर्क फ़ेच करता है. इसलिए, इसके Connection-Allowlist को उन सभी दस्तावेज़ों की अनुमति वाली सूचियों के सुपरसेट के तौर पर कॉन्फ़िगर करें जिन्हें यह कंट्रोल करता है.
  3. उल्लंघन की रिपोर्टिंग और चरणबद्ध या डुअल रोलआउट को कॉन्फ़िगर करना: Reporting-Endpoints हेडर तय करें और बंद करने वाले ) के बाद, बिना कोटेशन वाला ; report-to=<endpoint-name> पैरामीटर जोड़ें. शुरुआती रोल आउट के दौरान, सिर्फ़ Connection-Allowlist-Report-Only भेजा जा सकता है. इसके अलावा, एक ही रिस्पॉन्स पर दोनों Connection-Allowlist (अनुमति वाली बेसलाइन सूची लागू करना) और Connection-Allowlist-Report-Only (अनुमति वाली ज़्यादा सख्त सूची की जांच करना) भेजे जा सकते हैं. उल्लंघन की रिपोर्ट, "type": "connection-allowlist" और मुख्य हिस्से { url, connection, allowlist, disposition } के साथ डिलीवर की जाती हैं. यहां connection, ब्लॉक किया गया डेस्टिनेशन यूआरएल या "webrtc" है. साथ ही, disposition, "enforce" या "report" है.
  4. डिफ़ॉल्ट रीडायरेक्ट और WebRTC को ब्लॉक करने की सुविधा को ध्यान में रखें. साथ ही, क्लाइंट की गड़बड़ियों को ठीक करें: डिफ़ॉल्ट रूप से, Connection-Allowlist सभी 3xx एचटीटीपी रीडायरेक्ट (redirects=block, अनुमति वाली सूची में शामिल या एक ही ऑरिजिन वाले यूआरएल के बीच रीडायरेक्ट करते समय भी) और सभी RTCPeerConnection ट्रैफ़िक (webrtc=block) को ब्लॉक करता है. ; redirects=allow को सिर्फ़ तब जोड़ें, जब भरोसेमंद एंडपॉइंट को सर्वर-साइड रीडायरेक्ट की ज़रूरत हो (शुरुआती अनुरोध अब भी अनुमति वाली सूची से मैच होना चाहिए) या ; webrtc=allow को तब जोड़ें, जब WebRTC की ज़रूरत हो. क्लाइंट पर, आउटबाउंड fetch() कॉल को try...catch में रैप करें और WebSocket error इवेंट को मैनेज करें. इससे, TypeError (net::ERR_NETWORK_ACCESS_REVOKED) की वजह से अस्वीकार किए गए अनुरोध, गड़बड़ी की स्थिति को मैनेज करते हैं और यूज़र इंटरफ़ेस (यूआई) की स्थिति को अपडेट करते हैं. ऐसा करने पर, अनुरोधों को अस्वीकार किए जाने की वजह से होने वाली गड़बड़ियों को मैनेज किया जा सकता है.

कोड का उदाहरण

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 };
  }
}

सबसे सही तरीके

  • Connection-Allowlist को स्पेस से अलग किए गए आइटम की सिंगल RFC 9651 इनर लिस्ट (...) के तौर पर फ़ॉर्मैट करें. अगर कोई Connection-Allowlist हेडर खाली नहीं है और वह टॉप-लेवल स्ट्रक्चर्ड फ़ील्ड को पार्स नहीं कर पाता है, तो ब्राउज़र अनुमति वाली सूची को खाली करके बंद हो जाता है. साथ ही, वह आउटबाउंड नेटवर्क के सभी कनेक्शन ब्लॉक कर देता है. उदाहरण के लिए, (...) को छोड़ना या सिंगल कोट '...' का इस्तेमाल करना. Connection-Allowlist: () का इस्तेमाल तब करें, जब आपको लोड होने के बाद, नेटवर्क का सारा ऐक्सेस जान-बूझकर रद्द करना हो. हालांकि, लोकल स्कीम data:, blob:, about:, और filesystem: को अनुमति मिलती रहेगी.
  • पूरे ऑरिजिन पर लागू होने वाले यूआरएल पैटर्न ("https://api.example.com") के आखिर में मौजूद स्लैश को हटाएं या साफ़ तौर पर वाइल्डकार्ड ("https://api.example.com/*") का इस्तेमाल करें, ताकि /v1/users जैसे सबपाथ को अनुमति दी जा सके. पैटर्न को क्रम से लगाने से पहले, new URL(origin).href से जोड़े गए ट्रेलिंग स्लैश हटाएं.
  • WebSocket (ws:// और wss://) एंडपॉइंट को अनुमति वाली सूची में शामिल करें. इसके लिए, Connection-Allowlist में "http://..." और "https://..." पैटर्न का इस्तेमाल करें. ऐसा इसलिए, क्योंकि ब्राउज़र, मैच करने से पहले WebSocket स्कीम को एचटीटीपी/एचटीटीपीएस में नॉर्मलाइज़ करता है.
  • ज़रूर शामिल करें: पाबंदी वाले दस्तावेज़ की अनुमति वाली सूची में, बाहरी नेविगेशन के संभावित टारगेट (<a href>, <form action>, OAuth/एसएसओ की सुविधा देने वाली कंपनियां, पेमेंट चेकआउट). साथ ही, याद रखें कि डिफ़ॉल्ट redirects=block, एक ही ऑरिजिन वाले 3xx रीडायरेक्ट को भी ब्लॉक कर देता है. ऐसा तब तक होता है, जब तक ; redirects=allow की जानकारी न दी गई हो.
  • response-origin या इनर-लिस्ट पैरामीटर वैल्यू (; report-to=conn-reports, ; redirects=allow, ; webrtc=allow) को कोटेशन मार्क में रैप न करें. इसके अलावा, (...) में मौजूद आइटम को कॉमा लगाकर अलग न करें.
  • यूआरएल पैटर्न में, (\d+) जैसे कस्टम रेगुलर एक्सप्रेशन ग्रुप या "/api/*" जैसे रिलेटिव पाथ का इस्तेमाल न करें. ब्राउज़र का नेटवर्क-सर्विस पार्सर, बिना स्कीम और होस्टनेम वाले रेगुलर एक्सप्रेशन ग्रुप और पैटर्न को अस्वीकार कर देता है.
  • फ़िलहाल, प्रोडक्शन में एक्सपेरिमेंट के तौर पर उपलब्ध <iframe connectionAllowlist="..."> एट्रिब्यूट या Allow-Connection-Allowlist-From एम्बेड किए गए एनफ़ोर्समेंट हेडर पर भरोसा न करें. ऐसा इसलिए, क्योंकि शिपिंग ब्राउज़र में एम्बेड किया गया एनफ़ोर्समेंट, डिफ़ॉल्ट रूप से चालू नहीं होता.

फ़ॉलबैक रणनीति

Connection-Allowlist को एचटीटीपी रिस्पॉन्स हेडर के ज़रिए ब्राउज़र नेटवर्क लेयर पर लागू किया जाता है. इसलिए, इसे क्लाइंट-साइड JavaScript में पॉलीफ़िल नहीं किया जा सकता. अगर आपके बेसलाइन टारगेट में ऐसे ब्राउज़र शामिल हैं जो अब तक Connection-Allowlist के साथ काम नहीं करते, तो Connection-Allowlist को Content-Security-Policy (सीएसपी) हेडर के साथ जोड़ें. साथ ही, अगर एचटीटीपी रिस्पॉन्स हेडर कॉन्फ़िगर नहीं किए जा सकते, तो दस्तावेज़-लेवल पर फ़ेच करने और सब-रिसोर्स से जुड़ी पाबंदियों के लिए, <meta http-equiv="Content-Security-Policy"> एलिमेंट जोड़ें:

  • मज़बूत सुरक्षा: इस सुविधा के साथ काम करने वाले ब्राउज़र, दोनों नीतियों को एक साथ लागू करते हैं. Connection-Allowlist, सीएसपी के अलावा अन्य एक्सफ़िल्ट्रेशन चैनलों (dns-prefetch, preconnect, WebRTC, और रीडायरेक्ट) को ब्लॉक करता है. वहीं, सीएसपी उन ब्राउज़र में fetch(), सब-रिसोर्स, और <form action> नेविगेशन को ब्लॉक करता है जिनमें Connection-Allowlist नहीं है.
  • form-action और worker-src को साफ़ तौर पर शामिल करें: सीएसपी में, <form action> सबमिशन के लिए default-src का इस्तेमाल नहीं किया जाता; हमेशा form-action को साफ़ तौर पर शामिल करें. worker-src 'self' को शामिल करें (blob: को छोड़कर), ताकि पाबंदी वाले पेज, दस्तावेज़-लेवल की पाबंदियों को बायपास करने के लिए blob: SharedWorkers को स्पॉन न कर सकें.
  • अनुमति वाले ऑरिजिन को हर संसाधन के सीएसपी डायरेक्टिव से मैप करें: Connection-Allowlist, एक एंडपॉइंट सूची का इस्तेमाल करता है और इनलाइन मार्कअप को कंट्रोल नहीं करता. इसके उलट, सीएसपी में default-src 'self' हर तरह के संसाधन (connect-src, img-src, style-src, font-src, script-src) को प्रतिबंधित करता है. साथ ही, इनलाइन <style> और <script> ब्लॉक को तब तक ब्लॉक करता है, जब तक कि उन्हें हैश या नॉन्स के ज़रिए अनुमति न दी जाए.
  • सीएसपी connect-src में wss:// को शामिल करें: Connection-Allowlist, "https://..." पैटर्न के हिसाब से wss:// कनेक्शन मैच करता है. हालांकि, क्रॉस-ब्राउज़र एनवायरमेंट में सीएसपी connect-src को WebSockets का इस्तेमाल करते समय, wss:// ऑरिजिन को साफ़ तौर पर लिस्ट करना चाहिए.
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'"
/>