ब्लॉग पर वापस

आपकी एनालिटिक्स स्क्रिप्ट ही आपकी Content-Security-Policy में छेद है

एक सख्त CSP, XSS को बंद कर देती है। एक थर्ड-पार्टी एनालिटिक्स टैग उसे फिर से खोल देता है। यहाँ बताया गया है कि host allowlists और गायब SRI आपकी पॉलिसी को क्यों कमज़ोर करते हैं — और एक फर्स्ट-पार्टी ट्रैकर इसे कैसे ठीक करता है।

एक Content-Security-Policy, क्रॉस-साइट स्क्रिप्टिंग के खिलाफ सबसे प्रभावी बचाव है। जिस पल आप एक थर्ड-पार्टी एनालिटिक्स टैग जोड़ते हैं, आपको आमतौर पर उसमें एक छेद करना पड़ता है। यही छेद वह जगह है जहाँ वास्तविक दुनिया के अधिकांश CSP bypass रहते हैं।

इसका कारण संरचनात्मक है, आकस्मिक नहीं। निगरानी-केंद्रित एनालिटिक्स स्क्रिप्ट्स को इस तरह डिज़ाइन किया जाता है कि वे रनटाइम पर, उन डोमेन्स से और कोड लोड करें जिन्हें आप नियंत्रित नहीं करते। एक सख्त CSP का अस्तित्व ही ठीक इसी चीज़ को रोकने के लिए है।

Host allowlists किसी स्क्रिप्ट पर भरोसा करने का कमज़ोर तरीका है

किसी एनालिटिक्स वेंडर को अनुमति देने का पुराना तरीका था एक host allowlist:

Content-Security-Policy: script-src 'self' https://www.google-analytics.com https://www.googletagmanager.com;

OWASP अब इस पैटर्न की सिफारिश नहीं करता। Allowlists को आसानी से bypass किया जा सकता है: कोई भी अनुमत origin जो किसी JSONP endpoint, एक open redirect, या यूज़र द्वारा अपलोड की गई स्क्रिप्ट्स को होस्ट करता है, एक XSS vector बन जाता है। टैग मैनेजर इसे और बदतर बना देते हैं, क्योंकि उनका अस्तित्व ही ठीक इसलिए है कि वे मनमाने origins से और स्क्रिप्ट्स inject करें।

मौजूदा अग्रणी प्रथा है एक strict CSP जो प्रति-response एक nonce और strict-dynamic पर बनी होती है:

Content-Security-Policy: script-src 'nonce-r4nd0m' 'strict-dynamic'; object-src 'none'; base-uri 'none';

strict-dynamic के साथ, ब्राउज़र उन स्क्रिप्ट्स पर भरोसा करता है जिन्हें किसी पहले से भरोसेमंद स्क्रिप्ट ने लोड किया हो — और यही वह व्यवहार है जिस पर एक टैग मैनेजर निर्भर करता है। यह वेंडर के लिए सुविधाजनक है और आपके लिए ख़तरनाक: एक बार समझौता हुआ टैग अब किसी भी चीज़ को लोड करने की अंतर्निहित अनुमति रखता है।

SRI उन्हीं स्क्रिप्ट्स पर काम नहीं करता जिन्हें इसकी सबसे ज़्यादा ज़रूरत है

Subresource Integrity ब्राउज़र को किसी fetch की गई फ़ाइल को execute करने से पहले उसे एक cryptographic hash के विरुद्ध सत्यापित करने देता है:

<script
  src="https://cdn.example.com/tracker.js"
  integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
  crossorigin="anonymous"></script>

SRI, sha256, sha384, और sha512 को सपोर्ट करता है, और किसी भी mismatch पर ब्राउज़र उस resource को अस्वीकार कर देता है। यह एक समझौता हुए CDN के खिलाफ एक साफ-सुथरा बचाव है — static, version-pinned फ़ाइलों के लिए।

एनालिटिक्स के लिए यह विफल हो जाता है। वेंडर टैग जानबूझकर परिवर्तनशील होते हैं: एक ही URL पर मौजूद फ़ाइल हर बार बदल जाती है जब वेंडर नए फ़ीचर्स भेजता है, इसलिए एक pinned hash अगले deploy पर collection को तोड़ देगा। व्यवहार में टीमें SRI को ठीक उन्हीं स्क्रिप्ट्स से हटा देती हैं जिनकी पेज तक सबसे व्यापक पहुँच होती है। अक्टूबर 2025 में supply-chain जोखिम के पुनर्मूल्यांकन ने इसी कारण से असुरक्षित SRI implementation को high severity आँका।

Trusted Types फरवरी 2026 में न्यूनतम स्तर ऊँचा कर देते हैं

समस्या के DOM-XSS वाले हिस्से का अब एक ब्राउज़र-स्तरीय जवाब मौजूद है। Baseline फरवरी 2026 से, Trusted Types मौजूदा ब्राउज़र्स में उपलब्ध हैं। आप enforcement को इस तरह सक्षम करते हैं:

Content-Security-Policy: require-trusted-types-for 'script'; trusted-types default;

उसके बाद, Element.innerHTML जैसे DOM sinks कच्ची strings को अस्वीकार कर देते हैं और केवल वही मान स्वीकार करते हैं जो किसी पंजीकृत policy द्वारा बनाए गए हों:

const policy = trustedTypes.createPolicy("default", {
  createHTML: (input) => DOMPurify.sanitize(input),
});
el.innerHTML = policy.createHTML(userInput); // ok
el.innerHTML = userInput;                     // throws TypeError

यह वास्तव में मज़बूत है। यह वैसा ही नियम भी है जिसका legacy एनालिटिक्स और टैग-मैनेजर कोड नियमित रूप से उल्लंघन करते हैं, क्योंकि वे स्क्रिप्ट्स innerHTML में लिखती हैं और <script> nodes को साधारण strings के रूप में inject करती हैं। Trusted Types को चालू करने का अक्सर मतलब होता है पहले अपने एनालिटिक्स वेंडर को बंद करना।

एक फर्स्ट-पार्टी ट्रैकर एक सख्त पॉलिसी से लड़ने के बजाय उसमें फ़िट हो जाता है

यह टकराव तब गायब हो जाता है जब एनालिटिक्स स्क्रिप्ट छोटी हो, स्व-निहित हो, और आपके अपने origin से सर्व की जाती हो। एक ट्रैकर जो और कोई कोड लोड नहीं करता, उसे न तो strict-dynamic की ज़रूरत होती है और न ही आपकी allowlist पर किसी वेंडर host की:

Content-Security-Policy: script-src 'self'; object-src 'none'; base-uri 'none'; require-trusted-types-for 'script';

बस इतना ही। कोई थर्ड-पार्टी origin नहीं, कोई nonce escalation नहीं, किसी टैग मैनेजर के लिए कोई अपवाद नहीं तराशा गया।

Monoid का ट्रैकर लगभग 2 KB का है, इसकी कोई dependencies नहीं हैं, और यह कभी किसी DOM-XSS sink को कॉल नहीं करता। यह न innerHTML लिखता है, न स्क्रिप्ट्स inject करता है, और न ही cookies या storage पढ़ता या सेट करता है। यह प्रति pageview /collect पर एक keepalive request भेजता है और बाकी समय निष्क्रिय रहता है:

fetch('/collect', {
  method: 'POST',
  body: JSON.stringify({ site_id, path, referrer, screen_w }),
  keepalive: true,
});

चूँकि फ़ाइल static है, आप इस पर SRI लागू कर सकते हैं और यह कभी नहीं टूटेगी — एक pinned hash तब तक मान्य रहता है जब तक आप स्क्रिप्ट को अपडेट करने का चुनाव न करें। और चूँकि पहचान एक one-way दैनिक hash है, SHA-256(IP | UA | SALT_SECRET | YYYY-MM-DD), इसलिए कोई cross-site profiling है ही नहीं जिसे एक ढीली CSP leak भी कर सके।

यह पैटर्न एनालिटिक्स से आगे भी सामान्यीकृत होता है: आप अपनी allowlist से जो भी स्क्रिप्ट हटाते हैं, वह एक पूरी श्रेणी का XSS bypass है जिसे आप उसके साथ हटा देते हैं। सबसे सुरक्षित थर्ड-पार्टी स्क्रिप्ट वही है जिसे आप लोड नहीं करते।

स्रोत

Comments

Loading comments…