सब-रिसोर्स इंटिग्रिटी, सीओआरएस, और रेफ़रर पॉलिसी की मदद से, बाहरी सब-रिसोर्स को सुरक्षित करना

तीसरे पक्ष के सीडीएन या बाहरी ऑरिजिन से स्क्रिप्ट, स्टाइलशीट, फ़ॉन्ट या इमेज लोड करते समय, सब-रिसोर्स इंटिग्रिटी (एचटीएमएल में integrity और सीएसएस url() में integrity()) की मदद से, उनकी क्रिप्टोग्राफ़िक इंटिग्रिटी की पुष्टि करें. साथ ही, क्रॉस-ऑरिजिन क्रेडेंशियल हैंडलिंग (crossorigin और cross-origin()) को कॉन्फ़िगर करें और आउटगोइंग Referer हेडर (referrerpolicy और referrer-policy()) को सीमित करें. दस्तावेज़-लेवल पर कॉन्टेंट की सुरक्षा के लिए नीति और आउटबाउंड कनेक्शन की अनुमति वाली सूचियों के लिए, वेब सुरक्षा और कनेक्शन की अनुमति वाली सूचियों की मदद से, आउटबाउंड नेटवर्क कनेक्शन को सीमित करना देखें.

1. ऐसी ऐसेट के लिए क्रिप्टोग्राफ़िक इंटिग्रिटी डाइजेस्ट जनरेट करें जिनमें बदलाव न किया जा सकता हो

सब-रिसोर्स इंटिग्रिटी (एसआरआई) यह पक्का करती है कि ब्राउज़र किसी बाहरी ऐसेट को सिर्फ़ तब रेंडर या लागू करे, जब उसके बाइट आपके ऐप्लिकेशन से तय किए गए क्रिप्टोग्राफ़िक हैश से मैच करते हों.

  • डाइजेस्ट को बिल्ड या रिलीज़ के समय कंप्यूट करें, न कि लाइव और अविश्वसनीय रिस्पॉन्स से: अपने बिल्ड या रिलीज़ के चरण के दौरान, पहले से मौजूद अच्छी लोकल ऐसेट बाइट से इंटिग्रिटी हैश जनरेट करें या पैकेज के लेखक की ओर से पब्लिश किए गए आधिकारिक डाइजेस्ट को कॉपी करें. इसके बाद, उन्हें अपने एचटीएमएल, सीएसएस या JavaScript में हार्डकोड करें. रनटाइम के दौरान, ब्राउज़र में तीसरे पक्ष का यूआरएल फ़ेच न करें, ताकि उसके integrity हैश का हिसाब लगाया जा सके. ऐसा करने पर, रिमोट सर्वर से मिलने वाले बाइट पर भरोसा किया जाता है और एसआरआई को नुकसान पहुंचता है.
  • डिफ़ॉल्ट रूप से sha384- का इस्तेमाल करें: इंटिग्रिटी मेटाडेटा को <algorithm>-<base64-digest> के तौर पर फ़ॉर्मैट करें. इसके लिए, sha384- (सुझाया गया डिफ़ॉल्ट), sha512- या sha256- का इस्तेमाल करें.
  • बाहरी सब-रिसोर्स को ऐसे यूआरएल पर पिन करें जिनमें बदलाव न किया जा सके और जो वर्शन वाले हों: तीसरे पक्ष की ऐसेट को हमेशा ऐसे यूआरएल का इस्तेमाल करके रेफ़र करें जिनमें बदलाव न किया जा सके, जो वर्शन-पिन किए गए हों या कॉन्टेंट-एड्रेस वाले हों (जैसे, https://cdn.example.com/ui@2.4.0/app.min.js). इसके बजाय, ऐसे यूआरएल (latest/app.min.js) का इस्तेमाल न करें जिनमें बदलाव किया जा सकता है. इनसे आपकी साइट, सप्लाई-चेन में होने वाले ऐसे बदलावों के लिए एक्सपोज़ हो जाती है जिनका पता नहीं चलता. साथ ही, रिमोट फ़ाइल के अपडेट होते ही एसआरआई काम करना बंद कर देता है. अगर आपको तीसरे पक्ष की ऐसी स्क्रिप्ट लोड करनी है जिसमें बदलाव किया जा सकता है और जो अपनी जगह पर ही अपडेट होती है (जैसे, वेंडर का Analytics टैग), तो उस यूआरएल से integrity को हटा दें. इसके बजाय, कॉन्टेंट की सुरक्षा के लिए नीति (strict-dynamic / नॉन्स) या iframe सैंडबॉक्सिंग का इस्तेमाल करके, उस यूआरएल को प्रतिबंधित करें.
  • जब कोई एंडपॉइंट कई मान्य पेलोड दिखा सकता है, तब स्पेस का इस्तेमाल करके कई डाइजेस्ट को अलग-अलग करें: एसआरआई के साथ काम करने वाले सभी ब्राउज़र, sha256-, sha384-, और sha512- को लागू करते हैं. अगर कोई यूआरएल, सही तरीके से कई जाने-माने पेलोड में से कोई एक पेलोड दिखाता है, तो एक ही एल्गोरिदम के कई डाइजेस्ट की सूची बनाएं. जैसे, A/B टेस्ट, सीडीएन रोलआउट या कॉन्टेंट-नेगोशिएटेड बिल्ड के दौरान. इन डाइजेस्ट को व्हाइटस्पेस से अलग करें. उदाहरण के लिए, integrity="sha384-variantA... sha384-variantB...". अगर ब्राउज़र, मौजूद सबसे मज़बूत एल्गोरिदम के किसी भी डाइजेस्ट से मैच करता है, तो ब्राउज़र रिस्पॉन्स को स्वीकार कर लेता है.
  • सीओआरएस हेडर के साथ क्रॉस-ऑरिजिन सबरिसॉर्स उपलब्ध कराना: ब्राउज़र, क्रॉस-ऑरिजिन रिस्पॉन्स की इंटिग्रिटी की पुष्टि सिर्फ़ तब कर सकते हैं, जब वह सीओआरएस-रीडेबल हो. जैसे, बिना नाम वाले अनुरोधों के लिए Access-Control-Allow-Origin: * या क्रेडेंशियल वाले अनुरोधों के लिए Access-Control-Allow-Credentials: true के साथ अनुरोध करने वाला सटीक ऑरिजिन.
# Generate a sha384- SRI hash at build time from a known-good local file
printf "sha384-%s\n" "$(openssl dgst -sha384 -binary app.min.js | openssl base64 -A)"
// Build-time / tooling helper (Node.js 19+, Deno, Bun) to compute an SRI string from known-good local bytes
export async function computeIntegrityMetadata(bytes) {
  const rawBytes =
    typeof bytes === 'string' ? new TextEncoder().encode(bytes) : bytes;
  const digestBuffer = await crypto.subtle.digest('SHA-384', rawBytes);
  const binary = Array.from(new Uint8Array(digestBuffer), (b) =>
    String.fromCharCode(b),
  ).join('');
  return `sha384-${btoa(binary)}`;
}

2. एचटीएमएल स्क्रिप्ट, स्टाइलशीट, और प्रीलोड को सुरक्षित करना

डॉक्यूमेंट-लेवल पर Referrer-Policy: strict-origin-when-cross-origin एचटीटीपी रिस्पॉन्स हेडर सेट करें या <meta name="referrer" content="strict-origin-when-cross-origin"> को <head> में शामिल करें. इससे, हर अनुरोध के लिए रेफ़रर मॉडिफ़ायर को हटाने वाला कोई भी सब-रिसोर्स या सीएसएस url(), क्रॉस-ऑरिजिन अनुरोधों पर संवेदनशील पाथ और क्वेरी पैरामीटर को हटा देता है.

इसके बाद, बाहरी <script>, <link rel="stylesheet">, <link rel="preload">, और <link rel="modulepreload"> एलिमेंट पर, हमेशा integrity, crossorigin, और referrerpolicy को एक साथ सेट करें:

  • integrity="sha384-...": अगर रिस्पॉन्स बाइट, उम्मीद के मुताबिक डाइजेस्ट से मैच नहीं होती हैं, तो यह रिसॉर्स को लागू होने या इस्तेमाल होने से रोकता है. ब्लॉक किए गए <link rel="stylesheet"> के लिए, linkElement.sheet, null रहता है और error इवेंट ट्रिगर होता है.
  • crossorigin="anonymous" (सार्वजनिक सीडीएन ऐसेट के लिए सुझाया गया डिफ़ॉल्ट) या crossorigin="use-credentials": जब भी किसी क्रॉस-ऑरिजिन रिसॉर्स पर integrity सेट किया जाता है, तब यह ज़रूरी होता है. crossorigin को शामिल न करने पर, ब्राउज़र no-cors मोड में संसाधन को फ़ेच करता है. इससे एक ऐसा अपारदर्शी रिस्पॉन्स मिलता है जिसकी हैश-पुष्टि नहीं की जा सकती. साथ ही, संसाधन को ब्लॉक कर दिया जाता है.
  • referrerpolicy="no-referrer" (तीसरे पक्ष के सीडीएन के लिए डिफ़ॉल्ट तौर पर सुझाया गया) या referrerpolicy="strict-origin-when-cross-origin": यह दस्तावेज़ की रेफ़रर नीति को बदल देता है, ताकि तीसरे पक्ष के होस्ट को Referer अनुरोध के हेडर में, ऑरिजिन या पाथ की कोई भी जानकारी लीक न हो.
<!-- Document-level baseline referrer policy in <head> (or via Referrer-Policy HTTP header) -->
<meta name="referrer" content="strict-origin-when-cross-origin" />

<!-- External stylesheet with SRI, anonymous CORS, and no referrer -->
<link
  rel="stylesheet"
  href="https://cdn.example.com/ui@2.4.0/styles.css"
  integrity="sha384-bs0EksOYBbU/g0IAHMXbSyH1Mgw83GKn+EZlSI+VE+zVchXmSbLwhVLrPzsOWnc5"
  crossorigin="anonymous"
  referrerpolicy="no-referrer"
/>

<!-- External script with SRI, anonymous CORS, and no referrer -->
<script
  src="https://cdn.example.com/ui@2.4.0/bundle.min.js"
  integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
  crossorigin="anonymous"
  referrerpolicy="no-referrer"
></script>

fetch() की मदद से, प्रोग्राम के हिसाब से बाहरी सब-रिसोर्स फ़ेच करते समय, मैच होने वाले integrity, mode, credentials, और referrerPolicy विकल्प पास करें. डाइजेस्ट मैच न होने पर, TypeError के साथ प्रॉमिस अस्वीकार कर दिया जाता है:

const response = await fetch('https://cdn.example.com/ui@2.4.0/manifest.json', {
  integrity: 'sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC',
  mode: 'cors',
  credentials: 'omit',
  referrerPolicy: 'no-referrer',
});

3. url() मॉडिफ़ायर की मदद से, सीएसएस इमेज, फ़ॉन्ट, और इंपोर्ट की गई स्टाइलशीट को सुरक्षित करना

सीएसएस url() के अनुरोध मॉडिफ़ायर (cross-origin(), referrer-policy(), और integrity()), सीएसएस से लोड किए गए संसाधनों के फ़ेच मोड, रेफ़रर हेडर, और सब-रिसोर्स इंटिग्रिटी की पुष्टि को कंट्रोल करते हैं. इनमें background-image, content, mask-image, border-image-source, filter, @font-face src, और @import नियम शामिल हैं. ये सीधे सीएसएस से कंट्रोल किए जाते हैं:

मॉडिफ़ायर फ़ंक्शन आर्ग्युमेंट का सिंटैक्स और अनुमति वाली वैल्यू मकसद
cross-origin(<ident>) बिना कोटेशन मार्क वाला कीवर्ड: anonymous (सुझाया गया डिफ़ॉल्ट) या use-credentials यह CORS का इस्तेमाल करके सीएसएस रिसॉर्स फ़ेच करता है और यह कंट्रोल करता है कि क्रेडेंशियल भेजे जाएं या नहीं. क्रॉस-ऑरिजिन सीएसएस रिसॉर्स पर, integrity() के साथ हमेशा शामिल करें.
referrer-policy(<ident>) बिना कोटेशन मार्क वाला कीवर्ड: no-referrer (तीसरे पक्ष की ऐसेट के लिए सुझाया गया डिफ़ॉल्ट कीवर्ड), strict-origin-when-cross-origin, origin, same-origin, strict-origin, origin-when-cross-origin, no-referrer-when-downgrade या unsafe-url इस सीएसएस अनुरोध के लिए, दस्तावेज़ की रेफ़रर नीति को बदल देता है.
integrity("<string>") कोट की गई स्ट्रिंग: "sha384-<base64>" (या स्पेस से अलग किए गए डाइजेस्ट "sha384-... sha384-...") यह रेंडर या लागू करने से पहले, फ़ेच किए गए सीएसएस रिसॉर्स की पुष्टि, अनुमानित क्रिप्टोग्राफ़िक डाइजेस्ट के हिसाब से करता है.

url() मॉडिफ़ायर बनाते समय, सीएसएस सिंटैक्स और सुरक्षा से जुड़े इन नियमों का पालन करें:

  • यूआरएल स्ट्रिंग को url("...") के अंदर कोट करें: अनुरोध मॉडिफ़ायर सिर्फ़ तब मान्य होते हैं, जब url(...) के अंदर मौजूद यूआरएल, कोट किया गया <string> हो. बिना कोट किए गए यूआरएल, मॉडिफ़ायर का इस्तेमाल नहीं कर सकते.
  • हर मॉडिफ़ायर टाइप को हर url() के लिए ज़्यादा से ज़्यादा एक बार तय करें: url("..." ...) में cross-origin(), referrer-policy(), और integrity() को किसी भी क्रम में जोड़ा जा सकता है. हालांकि, एक ही मॉडिफ़ायर टाइप को दो बार दोहराना (जैसे कि दो integrity() मॉडिफ़ायर) अमान्य है. एक से ज़्यादा हैश देने के लिए, उन्हें एक ही integrity("sha384-... sha384-...") स्ट्रिंग में स्पेस देकर अलग करें.
  • क्रॉस-ऑरिजिन integrity() को हमेशा cross-origin(anonymous) के साथ जोड़ें: @font-face src और mask-image पहले से ही डिफ़ॉल्ट रूप से, पहचान छिपाने वाले cors मोड में फ़ेच करते हैं. हालांकि, background-image, content, border-image-source, और @import डिफ़ॉल्ट रूप से no-cors मोड में फ़ेच करते हैं. इससे ऐसा रिस्पॉन्स मिलता है जिसे हैश-वेरिफ़ाई नहीं किया जा सकता. साथ ही, बाहरी एसवीजी filter डिफ़ॉल्ट रूप से same-origin मोड में फ़ेच करता है. इससे क्रॉस-ऑरिजिन यूआरएल पूरी तरह से ब्लॉक हो जाते हैं. cross-origin(anonymous) (या cross-origin(use-credentials)) को शामिल करने से, हर सीएसएस प्रॉपर्टी में सीओआरएस की पुष्टि एक जैसी होती है.
  • @import और @font-face नियमों में, मॉडिफ़ायर को url("...") के अंदर रखें: cross-origin(), referrer-policy(), और integrity() को url("..." ...) के अंदर रखें. वहीं, format("woff2") और tech(...) जैसे फ़ॉन्ट डिस्क्रिप्टर को url(...) के बाहर रखें.
@import url(
  "https://cdn.example.com/theme@1.2.0/tokens.css"
  cross-origin(anonymous)
  referrer-policy(no-referrer)
  integrity("sha384-bs0EksOYBbU/g0IAHMXbSyH1Mgw83GKn+EZlSI+VE+zVchXmSbLwhVLrPzsOWnc5")
);

@font-face {
  font-family: "BrandSans";
  src: url(
    "https://cdn.example.com/fonts@1.0.0/brand-sans.woff2"
    cross-origin(anonymous)
    referrer-policy(no-referrer)
    integrity("sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC")
  ) format("woff2");
  font-display: swap;
}

.verified-graphic {
  background-image: url(
    "https://cdn.example.com/media@1.0.0/badge.svg"
    cross-origin(anonymous)
    referrer-policy(no-referrer)
    integrity("sha384-Iw/0p59dP96nGTzHvf2XIfqcBwBW7RM6JfOij8uZxyg5azfNaOOUW8SQy6H/hU91")
  );
}

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

अगर आपका बेसलाइन टारगेट, अब तक सीएसएस url() के रिक्वेस्ट मॉडिफ़ायर (cross-origin(), referrer-policy() या integrity()) के साथ काम नहीं करता है, तो दस्तावेज़-लेवल की नीतियों को कैस्केडिंग सीएसएस डिक्लेरेशन के साथ जोड़ें. इससे सब-रिसॉर्स काम करते रहेंगे और सुरक्षित रहेंगे:

  1. जब अलग-अलग ब्राउज़र पर एसआरआई की ज़रूरत हो, तब सीएसएस @import के बजाय एचटीएमएल <link rel="stylesheet"> को प्राथमिकता दें: <link rel="stylesheet"> पर subresource-integrity और referrer-policy सभी ब्राउज़र पर उपलब्ध हैं. इसलिए, जब भी हर ब्राउज़र में इंटिग्रिटी की पुष्टि करना ज़रूरी हो, तब बाहरी स्टाइलशीट को @import के बजाय <link rel="stylesheet" href="..." integrity="sha384-..." crossorigin="anonymous" referrerpolicy="no-referrer"> के साथ लोड करें.
  2. दस्तावेज़-लेवल के Referrer-Policy हेडर या <meta name="referrer"> पर भरोसा करें, जैसा कि दूसरे चरण में बताया गया है: जिन ब्राउज़र में सीएसएस url() के अंदर referrer-policy() काम नहीं करता है उनमें दस्तावेज़-लेवल का Referrer-Policy: strict-origin-when-cross-origin हेडर या <meta name="referrer" content="strict-origin-when-cross-origin"> एलिमेंट, क्रॉस-ऑरिजिन सीएसएस अनुरोधों पर संवेदनशील पाथ और क्वेरी पैरामीटर को अपने-आप हटा देता है.
  3. प्रोग्रेसिव एन्हांसमेंट के लिए, अलग-अलग सीएसएस डिक्लेरेशन को कैस्केड करें: जो ब्राउज़र url("..." ...) में मौजूद मॉडिफ़ायर की पहचान नहीं कर पाते वे सीएसएस पार्स करते समय पूरे डिक्लेरेशन को खारिज कर देते हैं. सबसे पहले, सामान्य url("...") का एलान करें. इसके बाद, cross-origin() और referrer-policy() का एलान करें. ऐसा उन ब्राउज़र के लिए करें जो इन दोनों मॉडिफ़ायर के साथ काम करते हैं, लेकिन integrity() के साथ नहीं. इसके बाद, integrity("sha384-...") के साथ पूरा एलान करें:

    • कॉमा लगाकर अलग की गई @font-face src लिस्ट में, कभी भी पुष्टि नहीं किए गए url() को शामिल न करें: एक ही src डिस्क्रिप्टर में src: url("font.woff2" cross-origin(anonymous) integrity("sha384-...")), url("font.woff2"); लिखने से, रनटाइम के दौरान सब-रिसोर्स इंटिग्रिटी की जांच नहीं होती. अगर सीडीएन, छेड़छाड़ किए गए ऐसे फ़ॉन्ट का इस्तेमाल करता है जो integrity() की जांच में पास नहीं होता, तो ब्राउज़र उसे लोड न हो पाने वाली गड़बड़ी के तौर पर देखता है. साथ ही, कॉमा लगाकर अलग किए गए दूसरे url("font.woff2") पर अपने-आप स्विच हो जाता है. अलग-अलग src: डिक्लेरेशन का इस्तेमाल करने से, यह पक्का होता है कि काम करने वाले ब्राउज़र, सीएसएस पार्स करते समय बिना पुष्टि वाले डिक्लेरेशन को ओवरराइट कर दें. साथ ही, रनटाइम के दौरान छेड़छाड़ किए गए फ़ॉन्ट को ब्लॉक कर दें.
    @font-face {
      font-family: "BrandSans";
      /* 1. Parse-time fallback for browsers without url() modifier support */
      src: url("https://cdn.example.com/fonts@1.0.0/brand-sans.woff2") format("woff2");
      /* 2. Parse-time fallback for browsers supporting cross-origin() and referrer-policy() */
      src: url(
        "https://cdn.example.com/fonts@1.0.0/brand-sans.woff2"
        cross-origin(anonymous)
        referrer-policy(no-referrer)
      ) format("woff2");
      /* 3. Full protection in browsers supporting cross-origin(), referrer-policy(), and integrity() */
      src: url(
        "https://cdn.example.com/fonts@1.0.0/brand-sans.woff2"
        cross-origin(anonymous)
        referrer-policy(no-referrer)
        integrity("sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC")
      ) format("woff2");
      font-display: swap;
    }
    
    .verified-graphic {
      background-image: url("https://cdn.example.com/media@1.0.0/badge.svg");
      background-image: url(
        "https://cdn.example.com/media@1.0.0/badge.svg"
        cross-origin(anonymous)
        referrer-policy(no-referrer)
      );
      background-image: url(
        "https://cdn.example.com/media@1.0.0/badge.svg"
        cross-origin(anonymous)
        referrer-policy(no-referrer)
        integrity("sha384-Iw/0p59dP96nGTzHvf2XIfqcBwBW7RM6JfOij8uZxyg5azfNaOOUW8SQy6H/hU91")
      );
    }
    
  4. JavaScript में CSS.supports() का इस्तेमाल करके, सीएसएस url() मॉडिफ़ायर के साथ काम करने वाली सुविधा का पता लगाएं:

export function getCssUrlModifierSupport() {
  const supportsCrossOriginAndReferrer =
    typeof CSS !== 'undefined' &&
    typeof CSS.supports === 'function' &&
    CSS.supports(
      'background-image',
      'url("https://example.com/a.svg" cross-origin(anonymous) referrer-policy(no-referrer))',
    );

  const supportsUrlIntegrity =
    typeof CSS !== 'undefined' &&
    typeof CSS.supports === 'function' &&
    CSS.supports(
      'background-image',
      'url("https://example.com/a.svg" cross-origin(anonymous) referrer-policy(no-referrer) integrity("sha384-Iw/0p59dP96nGTzHvf2XIfqcBwBW7RM6JfOij8uZxyg5azfNaOOUW8SQy6H/hU91"))',
    );

  return { supportsCrossOriginAndReferrer, supportsUrlIntegrity };
}