सामग्री पर जाएँ
CodeAuditAgent
सभी लेख

सिक्योरिटी Headers समझें: CSP, HSTS और बाक़ी सब

nonces और strict-dynamic के साथ CSP, HSTS और preload, frame-ancestors, Referrer-Policy, Permissions-Policy तथा COOP की व्यावहारिक गाइड, Next.js कॉन्फ़िग सहित।

· 7 मिनट का लेख · Lina Source LLC

सिक्योरिटी headers वे निर्देश हैं जो आपका सर्वर ब्राउज़र को देता है: सिर्फ़ ये scripts चलाओ, मुझसे सिर्फ़ HTTPS पर बात करो, दूसरी साइटों को यह पेज frame मत करने दो। वे आपके कोड के बग ठीक नहीं करते, पर यह सीमित कर देते हैं कि हमलावर किसी बग से क्या कर सकता है। सख़्त Content Security Policy के पीछे मौजूद cross-site scripting छेद उसी छेद से कहीं छोटी समस्या है जो बिना policy के हो।

इनमें से ज़्यादातर headers कॉन्फ़िगरेशन की एक लाइन हैं। अपवाद है CSP, जिसके लिए योजना चाहिए। यह गाइड बताती है कि हर header क्या करता है, एक सामान्य वेब ऐप के लिए समझदार values क्या हैं, और इन्हें प्रोडक्शन तोड़े बिना कैसे रोल आउट करें।

Content-Security-Policy

CSP ब्राउज़र को बताता है कि scripts, styles, images, frames और connections के कौन-से स्रोत अनुमत हैं। इसका मुख्य काम injected scripts को चलने से रोकना है। डोमेन की allowlist साफ़ रास्ता लगती है, पर उसका bypasses का लंबा इतिहास है: कोई भी अनुमत CDN जो यूज़र कंटेंट या लाइब्रेरी के पुराने वर्ज़न होस्ट करता है, हमलावर के नियंत्रण वाला script लोड करने के लिए इस्तेमाल हो सकता है।

Nonces और strict-dynamic

जो तरीक़ा टिकता है वह है nonce-आधारित policy। सर्वर हर response के लिए एक रैंडम value बनाता है, उसे CSP header में डालता है और render किए गए हर script tag पर nonce attribute के रूप में जोड़ता है। Injected script tags को nonce पता नहीं होता, इसलिए वे ब्लॉक हो जाते हैं। 'strict-dynamic' जोड़ने से किसी भरोसेमंद script द्वारा लोड किए गए scripts आगे और scripts लोड कर सकते हैं, जिससे हर डोमेन गिनाए बिना bundlers और tag managers काम करते रहते हैं।

Content-Security-Policy:
  default-src 'self';
  script-src 'nonce-4AEemGb0xJptoIGFP3Nd' 'strict-dynamic';
  style-src 'self' 'nonce-4AEemGb0xJptoIGFP3Nd';
  img-src 'self' data: https:;
  connect-src 'self';
  object-src 'none';
  base-uri 'none';
  frame-ancestors 'none';
  form-action 'self';
  upgrade-insecure-requests

यह header एक ही लाइन में भेजा जाता है; यहाँ पढ़ने की सुविधा के लिए तोड़ा गया है। उस policy के कुछ directives दिखने से ज़्यादा करते हैं। object-src 'none' पुराने plugin कंटेंट को ब्लॉक करता है। base-uri 'none' किसी injected base tag को relative script URLs मोड़ने से रोकता है। form-action 'self' injected forms को कहीं और credentials post करने से रोकता है। nonce अप्रत्याशित होना चाहिए और हर response पर नया, जिसका मतलब है कि उसका इस्तेमाल करने वाले पेज static cache से नहीं परोसे जा सकते।

Report-Only के साथ रोल आउट करें

आँख मूँदकर डिप्लॉय की गई सख़्त CSP कुछ न कुछ तोड़ेगी: कोई analytics स्निपेट, कोई inline event handler, कोई third-party widget। Policy पहले Content-Security-Policy-Report-Only के रूप में भेजें। ब्राउज़र कुछ लागू नहीं करता पर report-to या report-uri directive में बताए endpoint पर हर उल्लंघन रिपोर्ट कर देता है। कुछ समय रिपोर्ट इकट्ठा करें, जो वाजिब है उसे ठीक करें या अनुमति दें, फिर header का नाम बदलकर enforce पर ले जाएँ। लागू करने के बाद भी reporting चालू रखें, क्योंकि नए उल्लंघन या तो regressions हैं या हमले। रिपोर्ट्स में ब्राउज़र एक्सटेंशन से आने वाले शोर की उम्मीद रखें, जो पेजों में अपने scripts डालते हैं; क्या अनुमति देनी है यह तय करने से पहले उन्हें स्रोत के आधार पर छाँट दें।

Strict-Transport-Security

HSTS ब्राउज़र को कहता है कि एक तय अवधि तक आपके डोमेन के लिए HTTPS ही इस्तेमाल करे, भले ही यूज़र http:// टाइप करे या कोई पुराना लिंक क्लिक करे। इससे वह अंतराल बंद होता है जिसमें कोई नेटवर्क हमलावर पहली सादी HTTP request पकड़कर HTTPS पर redirect हटा सकता था। ब्राउज़र यह header तभी मानते हैं जब वह HTTPS पर आए, और वे HSTS डोमेन पर यूज़र्स को certificate errors से आगे क्लिक करने भी नहीं देते, जो इसका मक़सद तो है पर certificate की मियाद ख़त्म हो जाए तो जोखिम भी।

छोटे max-age से शुरू करें, जैसे एक दिन, पुष्टि करें कि कुछ नहीं टूटा, फिर उसे एक या दो साल तक बढ़ाएँ। includeSubDomains यह नियम हर subdomain तक फैला देता है, इसलिए पहले पक्का करें कि वे सब वैध HTTPS परोसते हैं, जिनमें उसी डोमेन पर पुरानी मार्केटिंग साइटें और internal टूल भी शामिल हैं।

preload directive, ब्राउज़र preload सूची में सबमिशन के साथ मिलकर, आपके डोमेन को ब्राउज़रों में पका देता है ताकि पहली ही विज़िट भी HTTPS से हो। इसके लिए कम से कम एक साल का max-age और includeSubDomains चाहिए। इसे एकतरफ़ा दरवाज़ा मानें: सूची से हटना संभव तो है पर यूज़र्स तक पहुँचने में लंबा समय लगता है, और इस बीच जो भी subdomain HTTPS नहीं कर सकता वह अपहुँच हो जाता है।

एक-लाइन वाले headers

  • X-Content-Type-Options: nosniff। ब्राउज़रों को आपके घोषित किए गए से अलग content type अनुमान लगाने से रोकता है, जिससे text के रूप में परोसी गई अपलोड फ़ाइल script की तरह नहीं चलती।
  • frame-ancestors (CSP में) और X-Frame-Options: DENY। तय करते हैं कि आपके पेज कौन frame में embed कर सकता है, जो clickjacking के ख़िलाफ़ बचाव है। आधुनिक ब्राउज़र frame-ancestors इस्तेमाल करते हैं और दोनों सेट होने पर X-Frame-Options नज़रअंदाज़ करते हैं; पुराने क्लाइंट्स के लिए X-Frame-Options रखे रहें। अगर आप अपने ही पेज frame करते हैं तो 'self' या SAMEORIGIN इस्तेमाल करें।
  • Referrer-Policy: strict-origin-when-cross-origin। दूसरी साइटों को सिर्फ़ आपका origin भेजता है, पूरा path और query string नहीं। इससे URLs में मौजूद tokens और IDs third parties तक नहीं पहुँचतीं। ख़ास तौर पर संवेदनशील पेजों के लिए no-referrer इस्तेमाल करें।
  • Permissions-Policy। उन ब्राउज़र फ़ीचर्स को बंद कर देता है जिनका आप इस्तेमाल नहीं करते, जैसे camera=(), microphone=(), geolocation=() और payment=(), ताकि injected या embedded कोड उन्हें माँग न सके।
  • Cross-Origin-Opener-Policy: same-origin। आपके पेज को उसके अपने browsing context group में रखता है, ताकि किसी दूसरी साइट द्वारा खोली गई window उसका reference न रख सके। अगर आप OAuth या पेमेंट popups पर निर्भर हैं तो same-origin-allow-popups इस्तेमाल करें।
  • Cross-Origin-Resource-Policy: same-origin या same-site। ब्राउज़रों को बताता है कि दूसरे origins को आपके responses images, scripts या दूसरे subresources के रूप में लोड न करने दें। अगर आप assets किसी sibling subdomain से परोसते हैं तो same-site इस्तेमाल करें, और कहीं और embed होने के लिए बने assets के लिए cross-origin।

आप X-XSS-Protection छोड़ सकते हैं। जिस filter को यह नियंत्रित करता था, वह आधुनिक ब्राउज़रों से हटा दिया गया है, और CSP उसकी जगह है। अगर कोई scanner ज़िद करे, तो उसे 0 सेट कर दें। इसी तरह, उन headers से बचें जो सिर्फ़ हमलावर के लिए जानकारी जोड़ते हैं: X-Powered-By और विस्तृत Server banners आपका फ़्रेमवर्क और वर्ज़न मुफ़्त में बता देते हैं। Next.js में पहला हटाने के लिए कॉन्फ़िग में poweredByHeader: false सेट करें।

एक Next.js कॉन्फ़िगरेशन

Static headers next.config.ts में रहते हैं, जहाँ headers() फ़ंक्शन उन्हें हर route पर लागू करता है। नीचे की values उस ऐप के लिए समझदार डिफ़ॉल्ट हैं जो ख़ुद को frames में embed नहीं करता, camera या location इस्तेमाल नहीं करता, और अपने ही assets परोसता है। इन्हें आँख मूँदकर कॉपी करने के बजाय अपने ऐप के असल काम के हिसाब से समायोजित करें।

// next.config.ts
import type { NextConfig } from "next";

const securityHeaders = [
  {
    key: "Strict-Transport-Security",
    value: "max-age=63072000; includeSubDomains",
  },
  { key: "X-Content-Type-Options", value: "nosniff" },
  { key: "X-Frame-Options", value: "DENY" },
  { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
  {
    key: "Permissions-Policy",
    value: "camera=(), microphone=(), geolocation=(), payment=()",
  },
  { key: "Cross-Origin-Opener-Policy", value: "same-origin" },
  { key: "Cross-Origin-Resource-Policy", value: "same-origin" },
];

const nextConfig: NextConfig = {
  async headers() {
    return [{ source: "/(.*)", headers: securityHeaders }];
  },
};

export default nextConfig;

CSP को हर request पर नया nonce चाहिए, इसलिए वह middleware में सेट होता है। Next.js request के Content-Security-Policy header से nonce पढ़ता है और rendering के दौरान उसे फ़्रेमवर्क के अपने scripts पर लागू करता है। अपने script tags के लिए आप इसे किसी server component में headers().get("x-nonce") से पढ़ सकते हैं।

// middleware.ts (Next.js 16 में फ़ाइल proxy.ts है और फ़ंक्शन proxy)
import { NextResponse, type NextRequest } from "next/server";

export function middleware(request: NextRequest) {
  const nonce = Buffer.from(crypto.randomUUID()).toString("base64");
  const csp = [
    "default-src 'self'",
    `script-src 'self' 'nonce-${nonce}' 'strict-dynamic'`,
    `style-src 'self' 'nonce-${nonce}'`,
    "img-src 'self' blob: data:",
    "object-src 'none'",
    "base-uri 'self'",
    "form-action 'self'",
    "frame-ancestors 'none'",
    "upgrade-insecure-requests",
  ].join("; ");

  const requestHeaders = new Headers(request.headers);
  requestHeaders.set("x-nonce", nonce);
  requestHeaders.set("Content-Security-Policy", csp);

  const response = NextResponse.next({ request: { headers: requestHeaders } });
  response.headers.set("Content-Security-Policy", csp);
  return response;
}

export const config = {
  matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
};

Nonce के साथ render होने वाले पेज dynamically render होने चाहिए, क्योंकि static पेज हर विज़िटर के लिए एक ही nonce दोहराएगा। रोलआउट के दौरान दोनों जगह header का नाम बदलकर Content-Security-Policy-Report-Only करें और एक reporting endpoint जोड़ें। डेवलपमेंट में React को अपने डीबगिंग फ़ीचर्स के लिए script-src में 'unsafe-eval' चाहिए हो सकता है; उसे सिर्फ़ तब जोड़ें जब NODE_ENV development हो।

पुष्टि कैसे करें

  • होम पेज, किसी dynamic पेज, किसी API route और किसी static asset पर curl -sI https://your-domain.example/ चलाएँ और हर एक पर headers पढ़ें। सिर्फ़ HTML पेजों पर सेट headers एक आम कमी है।
  • ब्राउज़र डेवलपर टूल्स खोलें। CSP उल्लंघन console में directive और ब्लॉक किए गए resource के साथ दिखते हैं, और Network टैब असल में परोसे गए headers दिखाता है।
  • अपने CDN या reverse proxy के पीछे भी जाँचें, और origin पर भी। Proxies कभी-कभी headers हटा देते, दोहरा देते या override कर देते हैं, और दो विरोधी CSP headers दोनों लागू होते हैं, इसलिए प्रभावी policy दोनों से सख़्त होती है।
  • जल्दी दूसरी राय के लिए Mozilla HTTP Observatory जैसा कोई पब्लिक checker इस्तेमाल करें, और कमज़ोर CSP directives पकड़ने के लिए Google का CSP Evaluator।
  • CI में एक टेस्ट जोड़ें जो अहम routes पर request करे और पुष्टि करे कि headers मौजूद हैं, ताकि कोई कॉन्फ़िग रीफ़ैक्टर उन्हें चुपचाप न गिरा दे।

Headers कॉन्फ़िगरेशन हैं, और ठीक इसीलिए वे खिसकते हैं: कोई नया route handler जो अपना response ख़ुद बनाता है, कोई proxy नियम जो डिफ़ॉल्ट override कर देता है, कोई रिलीज़ अनब्लॉक करने के लिए 'unsafe-inline' से ढीली की गई CSP। इनका रिव्यू कोड की तरह करें। CodeAuditAgent बाक़ी कोड के साथ-साथ किसी पब्लिक रिपॉज़िटरी या पेस्ट किए गए स्निपेट की कॉन्फ़िगरेशन भी पढ़ता है, और ग़ायब या कमज़ोर किए गए headers को severity, कोट की गई कॉन्फ़िग और सुझाए गए फ़िक्स के साथ रिपोर्ट करता है।

काम का एक वाजिब क्रम: एक-लाइन वाले headers आज, छोटे max-age के साथ HSTS इसी हफ़्ते, और nonce-आधारित CSP को Report-Only मोड में जैसे ही आप रिपोर्ट इकट्ठा कर सकें।