प्रतिनिधि इंटरव्यू विषय

Frontend Interview: आप postMessage के साथ क्रॉस-ओरिजिन iframe संचार को कैसे सुरक्षित करते हैं?

फ्रंटएंडकठिन
Offer.cc संपादकीय टीमप्रकाशित अपडेट किया गया

प्रश्न

एक होस्ट पेज को postMessage के माध्यम से एक क्रॉस-ओरिजिन पेमेंट iframe के साथ द्विदिशी (bidirectionally) संचार करना होगा। होस्ट इनिशियलाइज़ेशन डेटा भेजता है, जबकि iframe तत्परता (readiness), आकार परिवर्तन और भुगतान पूरा होने की रिपोर्ट करता है। आप दुर्भावनापूर्ण पेजों या गलत विंडो को फ़र्ज़ी कमांड भेजने या डेटा प्राप्त करने से कैसे रोकेंगे, और आप लोडिंग, रीप्ले, नेविगेशन और विफलता रिकवरी को कैसे संभालेंगे?

Prompt और लागू परिदृश्य

होस्ट https://app.example.com पर चलता है और https://pay.example.net से एक पेमेंट iframe एम्बेड करता है। विजेट तैयार होने के बाद, होस्ट लोकेल, थीम और एक अल्पकालिक (short-lived) पेमेंट-सेशन संदर्भ भेजता है। iframe ready, ऊंचाई में परिवर्तन, उपयोगकर्ता द्वारा रद्दीकरण और भुगतान फ़्लो के पूरा होने की रिपोर्ट करता है। पेज में कई सेम-ओरिजिन iframes हो सकते हैं, और लोड होने के दौरान विजेट रीडायरेक्ट हो सकता है या फिर से बनाया जा सकता है।

द्विदिशी postMessage प्रोटोकॉल और फ़्रंटएंड कार्यान्वयन डिज़ाइन करें। गंतव्य विंडो, प्राप्तकर्ता पहचान, संदेश का आकार (shape), इनिशियलाइज़ेशन समय, डुप्लिकेट और पुन: क्रमबद्धता (reordering), नेविगेशन, क्लीनअप और सुरक्षा परीक्षणों को कवर करें। एक समापन (completion) संदेश केवल UI को रीफ़्रेश करने के लिए प्रेरित करता है; होस्ट सर्वर को आधिकारिक पेमेंट सिस्टम के साथ अंतिम भुगतान स्थिति की पुष्टि करनी चाहिए।

यह प्रश्न सीनियर फ़्रंटएंड, वेब प्लेटफ़ॉर्म, फ़्रंटएंड आर्किटेक्चर और एप्लिकेशन-सुरक्षा भूमिकाओं के लिए उपयुक्त है। इसके मुख्य कौशल ब्राउज़र सेम-ओरिजिन नीति, क्रॉस-डॉक्यूमेंट मैसेजिंग, क्लाइंट ट्रस्ट सीमाएं और एसिंक्रोनस प्रोटोकॉल डिज़ाइन हैं, इसलिए इसकी श्रेणी frontend है। CORS यह नियंत्रित करता है कि ब्राउज़र स्क्रिप्ट क्रॉस-ओरिजिन नेटवर्क प्रतिक्रियाओं को पढ़ सकती हैं या नहीं। यह postMessage गंतव्य बाधाओं या संदेश-श्रोता सत्यापन की जगह नहीं लेता है।

इंटरव्यूअर क्या मूल्यांकन करता है

पहला संकेत यह है कि क्या उम्मीदवार प्रेषक और प्राप्तकर्ता दोनों की सुरक्षा करता है। प्रेषक एक सटीक targetOrigin की आपूर्ति करता है ताकि लक्ष्य विंडो के किसी अन्य ओरिजिन पर नेविगेट करने के बाद डेटा डिलीवर न हो। प्राप्तकर्ता प्रत्येक संदेश पर event.origin की जांच करता है और जब उसके पास एक अपेक्षित विंडो संदर्भ होता है, तो वह event.source की जांच करता है। केवल एक पक्ष को लागू करने से अभी भी प्रकटीकरण (disclosure) या जालसाजी (forgery) का रास्ता खुला रहता है।

दूसरा संकेत यह है कि क्या उम्मीदवार मैसेजिंग को अज्ञात इनपुट वाले सार्वजनिक प्रवेश बिंदु के रूप में मानता है। ओरिजिन जांच पास करना केवल उस ओरिजिन की पहचान करता है जिसमें कोड चला था। यह पेलोड के आकार को साबित नहीं करता है, न ही यह किसी विश्वसनीय ओरिजिन पर XSS या दोषपूर्ण कोड को खतरनाक कमांड भेजने से रोकता है। एक मजबूत उत्तर unknown से event.data को सीमित करने से पहले एक वर्ज़न वाला लिफाफ़ा (envelope), अनुमत संदेश प्रकार, फ़ील्ड बाधाएं, आकार सीमाएं और स्थिति संक्रमण (state transitions) को परिभाषित करता है।

तीसरा संकेत एसिंक्रोनस समय को संभालना है। iframe द्वारा अपना लिसनर पंजीकृत करने से पहले भेजा गया इनिशियलाइज़ेशन संदेश चुपचाप गायब हो जाता है। एक विश्वसनीय फ़्लो पहले पैरेंट लिसनर को पंजीकृत करता है, iframe लोड करता है, चाइल्ड को ready की घोषणा करने देता है, और सत्यापन के बाद ही init भेजता है। संदेश ID, एक चैनल ID, टाइमआउट और इडेम्पोटेंट स्थिति संक्रमण रिट्राई, डुप्लिकेट और विलंबित संदेशों को संभालते हैं।

अंत में, इंटरव्यूअर सर्वर-साइड ट्रस्ट सीमा की तलाश करता है। एक सत्यापित iframe से प्राप्त complete संदेश अभी भी भुगतान का प्रमाण नहीं है। फ़्रंटएंड को होस्ट सर्वर से क्वेरी करने के लिए बिना संवेदनशील विवरण वाले परिणाम संदर्भ का उपयोग करना चाहिए। CSP, frame-src, frame-ancestors, और न्यूनतम sandbox अनुमतियां जोखिम को कम करती हैं, लेकिन वे संदेश-स्तरीय पहचान और डेटा सत्यापन की जगह नहीं लेती हैं।

पहले स्पष्ट करने योग्य प्रश्न

  • क्या दोनों ओरिजिन निश्चित हैं? दो निश्चित ओरिजिन सटीक तुलना की अनुमति देते हैं। यदि होस्ट ग्राहक कस्टम डोमेन का समर्थन करता है, तो भुगतान पृष्ठ को अपने सर्वर से इस सत्र के लिए अनुमत पैरेंट ओरिजिन प्राप्त करना होगा। इसे उस पर भरोसा नहीं करना चाहिए जो भी event.origin पहले दिखाई दे।
  • चैनल के माध्यम से कौन सा संवेदनशील डेटा जाता है? थीम और ऊंचाई कम जोखिम वाले हैं। भुगतान क्रेडेंशियल्स, व्यक्तिगत डेटा या पुन: प्रयोज्य वाहक टोकन (bearer tokens) को ब्रॉडकास्ट-शैली के इंटरफ़ेस के माध्यम से यात्रा नहीं करनी चाहिए। यदि किसी क्षमता को पार करना ही है, तो एक अल्पकालिक, ऑडियंस-बाउंड, प्रतिसंहरणीय (revocable), न्यूनतम-विशेषाधिकार प्राप्त संदर्भ का उपयोग करें।
  • भुगतान पृष्ठ को कौन एम्बेड कर सकता है? भुगतान पृष्ठ को frame-ancestors के साथ एम्बेडर्स को प्रतिबंधित करना चाहिए। यदि कई वैध किरायेदार (tenants) हैं, तो सर्वर-साइड पर एक सटीक नीति तैयार करें या प्रत्येक साइट को अनुमति देने के बजाय एक नियंत्रित एम्बेडिंग प्रवेश बिंदु का उपयोग करें।
  • एक पृष्ठ पर कितने सेम-ओरिजिन iframes मौजूद हो सकते हैं? एक के साथ, ओरिजिन और अपेक्षित contentWindow पीयर का पता लगाता है। कई इंस्टेंसेस के लिए अलग-अलग विंडो संदर्भ और चैनल-बाउंड प्रोटोकॉल स्थिति की आवश्यकता होती है; केवल-ओरिजिन ब्रॉडकास्ट हैंडलर अपर्याप्त है।
  • कौन से संदेश संवेदनशील क्रियाओं को ट्रिगर करते हैं? resize लेआउट को सीधे प्रभावित कर सकता है। complete, रिफंड, ऑर्डर सबमिशन या खाता परिवर्तनों के लिए सर्वर प्राधिकरण और आधिकारिक स्थिति लुकअप की आवश्यकता होती है। एक संदेश एक संकेत वहन करता है, व्यावसायिक अधिकार नहीं।
  • विफलता के बाद क्या पुन: प्रयास (retry) किया जा सकता है? ready और init को इडेम्पोटेंट रूप से पुन: प्रयास किया जा सकता है। पुन: भेजे गए संदेश को भुगतान सबमिशन को दो बार निष्पादित नहीं करना चाहिए। परिभाषित करें कि संदेश ID, टर्मिनल स्थिति और सर्वर इडेम्पोटेन्सी कुंजी का स्वामी कौन है।

30-सेकंड उत्तर रूपरेखा

"मैं postMessage को एक ट्रस्ट सीमा के पार एक एसिंक्रोनस API के रूप में मानूँगा। चाइल्ड द्वारा ready भेजने से पहले पैरेंट अपना लिसनर पंजीकृत करता है और iframe के contentWindow को संग्रहीत करता है। प्रत्येक संदेश के लिए, पैरेंट सटीक ओरिजिन, अपेक्षित स्रोत और रनटाइम स्कीमा की पुष्टि करता है, फिर एक सटीक targetOrigin का उपयोग करके init के साथ उत्तर देता है। प्रोटोकॉल में एक वर्ज़न, चैनल ID और संदेश ID होता है, जबकि एक स्टेट मशीन हैंडशेक से पहले के संदेशों, डुप्लिकेट, पुन: क्रमबद्धता और टर्मिनल स्थिति के बाद परिवर्तनों को अस्वीकार करती है। complete केवल होस्ट को अपने सर्वर के माध्यम से आधिकारिक भुगतान स्थिति की जांच करने के लिए प्रेरित करता है। मैं दुर्भावनापूर्ण ओरिजिन, गलत सेम-ओरिजिन iframe, विकृत डेटा, रीप्ले और नेविगेशन रेस का परीक्षण करूँगा, फिर CSP और न्यूनतम iframe अनुमतियों के साथ जोखिम को कम करूँगा।"

चरण-दर-चरण गहन विश्लेषण

चरण एक: दोनों दिशाओं में ट्रस्ट सीमा का मानचित्रण करें

पैरेंट द्वारा भेजे जाने से पहले, उसके दो प्रश्न होते हैं: क्या इसका Window संदर्भ इच्छित iframe का है, और क्या लक्ष्य दस्तावेज़ में अभी भी अपेक्षित ओरिजिन है जब कॉल चलती है। targetOrigin दूसरे प्रश्न को संबोधित करता है। यदि लक्ष्य कहीं और नेविगेट कर गया है, तो ब्राउज़र संदेश को त्याग देता है। "*" का उपयोग करने से वह प्राप्तकर्ता बाधा समाप्त हो जाती है।

प्राप्त करने में भी दो स्वतंत्र प्रश्न हैं: किस ओरिजिन ने संदेश भेजा, और उस ओरिजिन की किस विंडो ने इसे भेजा। कोई भी पृष्ठ जो वर्तमान विंडो का संदर्भ प्राप्त करता है, संदेश भेजने का प्रयास कर सकता है। उसी विश्वसनीय ओरिजिन पर एक अलग iframe भी उसी नाम से एक इवेंट उत्सर्जित कर सकता है। इसलिए प्राप्तकर्ता कम से कम निम्नलिखित की जांच करता है:

  1. event.origin पूरी तरह से पूर्ण स्कीम, होस्ट और पोर्ट के बराबर है;
  2. event.source वही संदर्भ है जो iframe का संग्रहीत contentWindow है;
  3. event.data वर्तमान प्रोटोकॉल स्थिति द्वारा अनुमत संदेश आकार से मेल खाता है।

ओरिजिन स्वीकार करने के लिए प्रत्यय नियंत्रण (suffix containment), एक रेगेक्स अंश, या indexOf का उपयोग न करें। https://pay.example.net.attacker.test एक ढीली सबस्ट्रिंग जांच पास कर सकता है। event.origin को गतिशील रूप से अनुमत सूची में भी न जोड़ें; यह हमलावर की पहली जांच को पंजीकरण में बदल देता है।

चरण दो: एक छोटा, स्पष्ट संदेश प्रोटोकॉल परिभाषित करें

संदेशों को मनमाने ऑब्जेक्ट्स या स्ट्रिंग कमांड के बजाय एक विभेदित संघ (discriminated union) के रूप में प्रस्तुत करें। एक कॉम्पैक्ट प्रोटोकॉल में शामिल हो सकते हैं:

फ़ील्डउद्देश्यसत्यापन
vप्रोटोकॉल वर्ज़नकेवल समर्थित पूर्णांक वर्ज़न स्वीकार करें
typeसंदेश प्रकारनिश्चित enum; कभी भी गतिशील फ़ंक्शन नाम का आह्वान न करें
messageIdडिडुप्लिकेशन और ऑडिट सहसंबंधगैर-रिक्त, लंबाई-सीमित, वर्तमान विंडो के भीतर अद्वितीय
channelIdएक सफल हैंडशेक को बाइंड करनाready के बाद पैरेंट द्वारा बनाया गया; प्रत्येक बाद के संदेश का मिलान होना चाहिए
व्यावसायिक फ़ील्डउस संदेश द्वारा आवश्यक न्यूनतम डेटाप्रत्येक प्रकार, सीमा और लंबाई को सत्यापित करें

चैनल मौजूद होने से पहले ready के पास कोई channelId नहीं होता है। पैरेंट इसे पहले से सत्यापित ओरिजिन और स्रोत के माध्यम से प्राप्त करता है, एक यादृच्छिक channelId बनाता है, और init भेजता है। बाद के resize, cancel, और complete संदेशों को उस चैनल ID को प्रतिध्वनित (echo) करना होगा। चैनल एक ही विंडो में एक पुराने सत्र और देर से आने वाले संदेशों को अलग करता है। यह ओरिजिन, स्रोत या सर्वर प्राधिकरण की जगह नहीं लेता है।

प्राप्तकर्ता पार्सिंग को unknown से प्रारंभ करें। यह एक सरलीकृत TypeScript पैरेंट उदाहरण है। parseWidgetMessage सख्त रनटाइम सत्यापन का प्रतिनिधित्व करता है, टाइप अभिकथन (type assertion) का नहीं:

typescript
const WIDGET_ORIGIN = "https://pay.example.net"
const frame = document.querySelector<HTMLIFrameElement>("#payment-widget")

if (!frame?.contentWindow) {
  throw new Error("Payment iframe is unavailable")
}

const widgetWindow = frame.contentWindow
let channelId: string | null = null
const seenMessageIds = new Set<string>()
let state: "loading" | "active" | "completed" | "closed" = "loading"

window.addEventListener("message", onWidgetMessage)
frame.src = "https://pay.example.net/embed"

function onWidgetMessage(event: MessageEvent<unknown>) {
  if (event.origin !== WIDGET_ORIGIN) return
  if (event.source !== widgetWindow) return

  const message = parseWidgetMessage(event.data)
  if (!message || seenMessageIds.has(message.messageId)) return
  seenMessageIds.add(message.messageId)

  if (message.type === "ready") {
    if (state !== "loading") return
    channelId = crypto.randomUUID()
    widgetWindow.postMessage(
      {
        v: 1,
        type: "init",
        messageId: crypto.randomUUID(),
        channelId,
        locale: "zh-CN",
        theme: "system",
        checkoutSessionRef: "short-lived-opaque-reference",
      },
      WIDGET_ORIGIN,
    )
    state = "active"
    return
  }

  if (state !== "active" || channelId === null || message.channelId !== channelId) return

  if (message.type === "resize") {
    frame.style.height = `${Math.min(Math.max(message.height, 240), 900)}px`
  }

  if (message.type === "complete") {
    state = "completed"
    void refreshAuthoritativePaymentStatus(message.resultRef)
  }
}

वास्तविक पार्सर गैर-मान्यता प्राप्त खतरनाक प्रकारों, बड़े आकार के स्ट्रिंग्स, गैर-परिमित संख्याओं और उन स्थिति संयोजनों को भी अस्वीकार करता है जिनकी अनुमति नहीं है। रनटाइम जांच को छोड़ने के लिए as WidgetMessage का उपयोग न करें। स्ट्रक्चर्ड क्लोन ऑब्जेक्ट्स ले जा सकता है; यह साबित नहीं करता है कि कोई ऑब्जेक्ट एप्लिकेशन प्रोटोकॉल को संतुष्ट करता है।

चाइल्ड दूसरी दिशा में समान नियम लागू करता है। यह केवल पहले से कॉन्फ़िगर किए गए या सर्वर-साइड सत्र द्वारा बाध्य एक होस्ट ओरिजिन को स्वीकार करता है, event.source === window.parent की आवश्यकता होती है, init स्कीमा को सत्यापित करने के बाद ही चैनल ID संग्रहीत करता है, और हमेशा सटीक होस्ट ओरिजिन के साथ उत्तर देता है। कोई भी पीयर केवल इसलिए सत्यापन छोड़ नहीं सकता क्योंकि वह केवल एक समकक्ष की अपेक्षा करता है।

चरण तीन: हैंडशेक और स्टेट मशीन के साथ समय की अस्पष्टता को दूर करें

HTML मानक चेतावनी देता है कि एक नया नेविगेट किया गया चाइल्ड दस्तावेज़ शायद अभी तक अपना संदेश लिसनर स्थापित नहीं कर पाया हो। उस समय भेजा गया एक पैरेंट संदेश चाइल्ड के तैयार होने के लिए कतार (queue) में प्रतीक्षा नहीं करता है। पैरेंट iframe URL सेट करने या पुष्टि करने से पहले अपना लिसनर स्थापित करता है। चाइल्ड अपना लिसनर स्थापित करता है और ready की घोषणा करता है। पैरेंट संवेदनशील इनिशियलाइज़ेशन केवल तभी भेजता है जब ready ओरिजिन, स्रोत और स्कीमा जांच पास कर लेता है।

स्टेट मशीन अनुमत क्रियाओं को स्पष्ट बनाती है:

वर्तमान स्थितिस्वीकृत संदेशबाद की स्थिति
loadingreadyactive
activeresize, cancel, completeसमान, closed, या completed
completedकोई व्यावसायिक संदेश नहीं; वैकल्पिक केवल-पढ़ने योग्य पुष्टिcompleted बनी रहेगी
closedकोई नहींclosed बनी रहेगी

पैरेंट हैंडशेक पर टाइमआउट लगा सकता है। टाइमआउट होने पर यह एक पुन: प्रयास योग्य त्रुटि दिखाता है और पुराने iframe को नष्ट कर देता है। iframe को फिर से बनाने के लिए एक नई चैनल ID, एक खाली डिडुप्लिकेशन सेट, एक अपडेटेड अपेक्षित विंडो संदर्भ और पुराने लिसनर को हटाने की आवश्यकता होती है। पुराने चैनल का पुन: उपयोग करने से पिछले पृष्ठ का विलंबित संदेश नए सत्र में प्रवेश कर सकता है।

चरण चार: डुप्लिकेट संदेशों, डुप्लिकेट व्यावसायिक क्रियाओं और सर्वर तथ्यों को अलग करें

संदेश वितरण और व्यावसायिक निष्पादन के लिए इडेम्पोटेन्सी की दो परतों की आवश्यकता होती है। फ़्रंटएंड डुप्लिकेट UI संदेश को अनदेखा करने के लिए messageId का उपयोग करता है और टर्मिनल स्थिति के बाद परिवर्तनों को अस्वीकार करने के लिए एक स्टेट मशीन का उपयोग करता है। सर्वर को डुप्लिकेट चार्ज को रोकने के लिए अभी भी एक ऑर्डर या भुगतान-प्रयास इडेम्पोटेन्सी कुंजी की आवश्यकता होती है, क्योंकि रीफ़्रेश, नेटवर्क पुन: प्रयास और कई टैब फ़्रंटएंड सेट को बायपास कर सकते हैं।

complete का केवल यही अर्थ है कि एक विश्वसनीय विंडो दावा करती है कि फ़्लो समाप्त हो गया है। पैरेंट अपने स्वयं के सर्वर पर resultRef भेजता है। सर्वर एक प्रदर्शित करने योग्य परिणाम वापस करने से पहले वर्तमान उपयोगकर्ता, ऑर्डर स्वामित्व, राशि और आधिकारिक भुगतान-प्रदाता स्थिति की पुष्टि करता है। भले ही XSS भुगतान iframe के ओरिजिन पर स्क्रिप्ट को नियंत्रित करता हो, एक जाली complete सीधे ऑर्डर को भुगतान किया गया चिह्नित नहीं कर सकता है।

संदेश लॉग में केवल प्रोटोकॉल वर्ज़न, प्रकार, एक चैनल हैश, परिणाम और अस्वीकृति का कारण होता है। उनमें भुगतान क्रेडेंशियल्स, पूर्ण व्यक्तिगत डेटा या पुन: प्रयोज्य टोकन शामिल नहीं होते हैं। डिडुप्लिकेशन सेट को सीमित करें या इसे चैनल के साथ नष्ट कर दें ताकि कोई हमला या लंबे समय तक चलने वाला पृष्ठ मेमोरी को अनिश्चित काल तक न बढ़ा सके।

चरण पाँच: नेविगेशन, एकाधिक इंस्टेंसेस और साझा ओरिजिन को संभालें

event.origin प्रेषक का ओरिजिन है जब उसने postMessage को कॉल किया था। यह गारंटी नहीं देता कि भेजने वाली विंडो का वर्तमान या भविष्य का ओरिजिन समान है। एक हैंडशेक के बाद विंडो पर हमेशा के लिए भरोसा करने के बजाय प्रत्येक संदेश को पुन: सत्यापित करें। यदि लक्ष्य iframe किसी हमलावर ओरिजिन पर नेविगेट करता है, तो एक सटीक targetOrigin बाद के पैरेंट संदेशों को रोकता है, और पैरेंट प्राप्तकर्ता नए संदेशों को अस्वीकार कर देता है क्योंकि ओरिजिन भिन्न होता है।

यदि iframe उसी विश्वसनीय ओरिजिन पर एक अलग एप्लिकेशन पर नेविगेट करता है, तो ओरिजिन जांच अभी भी पास हो जाती है। स्रोत, चैनल ID, प्रोटोकॉल वर्ज़न, संदेश प्रकार और सर्वर पुष्टि पुराने सत्रों, गलत रूटिंग और व्यावसायिक प्रभाव को सीमित कर सकते हैं, लेकिन वे उस ओरिजिन पर दुर्भावनापूर्ण कोड का विरोध नहीं कर सकते हैं। ब्राउज़र संपूर्ण ओरिजिन को एक सुरक्षा प्रिंसिपल मानता है। विभिन्न ट्रस्ट स्तरों वाले अनुप्रयोगों को अलग-अलग सबडोमेन का उपयोग करना चाहिए; URL पथ और संदेश फ़ील्ड उन्हें अलग नहीं कर सकते।

एक पृष्ठ पर कई भुगतान iframes के साथ, प्रत्येक तत्व के लिए एक अलग contentWindow, स्थिति, चैनल और हैंडलर रिकॉर्ड रखें। एक वैश्विक लिसनर इंस्टेंसेस पर रूट कर सकता है, लेकिन इसे पहले event.source संदर्भ को एक इंस्टेंस पर मैप करना होगा। इसे संदेश के अंदर आपूर्ति की गई इंस्टेंस ID पर भरोसा करके शुरू नहीं करना चाहिए।

चरण छह: iframe क्षमताओं और एम्बेडिंग संबंधों को प्रतिबंधित करें

होस्ट CSP का frame-src केवल स्वीकृत भुगतान ओरिजिन की अनुमति देता है। भुगतान साइट का frame-ancestors केवल स्वीकृत होस्ट्स को इसे एम्बेड करने की अनुमति देता है। iframe का sandbox केवल उन क्षमताओं को सक्षम करता है जिनकी भुगतान फ़्लो को वास्तव में आवश्यकता होती है, जैसे कि स्क्रिप्ट, फ़ॉर्म या विशिष्ट पॉपअप। प्रत्येक जोड़ी गई अनुमति एक परीक्षण योग्य आवश्यकता के अनुरूप होनी चाहिए।

ये नियंत्रण उत्तर देते हैं कि कौन किसे लोड कर सकता है और एम्बेडेड दस्तावेज़ में कौन सी ब्राउज़र क्षमताएं हैं। वे किसी संदेश को प्रमाणित नहीं करते हैं या "*" को सुरक्षित नहीं बनाते हैं। यदि कोई भी साइट एक प्रमाणित भुगतान विजेट को एम्बेड कर सकती है, तो एक हमलावर इसे हमलावर-नियंत्रित पृष्ठ में लोड कर सकता है और सक्रिय रूप से कमांड भेज सकता है। frame-ancestors सीधे उस रास्ते को संकीर्ण करता है।

यदि पीयर्स को हैंडशेक के बाद एक उच्च-आवृत्ति स्वतंत्र स्ट्रीम की आवश्यकता होती है, तो प्रमाणित पहला postMessage एक MessagePort स्थानांतरित कर सकता है। इसके बाद का संचार एक समर्पित पोर्ट का उपयोग करता है, जिससे वैश्विक message लिसनर्स के बीच हस्तक्षेप कम हो जाता है। प्रारंभिक पोर्ट वितरण के लिए अभी भी ओरिजिन, स्रोत और स्कीमा जांच की आवश्यकता होती है, और पोर्ट का कब्ज़ा होने से कोई सर्वर-साइड व्यावसायिक अधिकार नहीं मिलता है।

चरण सात: नकारात्मक परीक्षणों के साथ अस्वीकृति पथों को सिद्ध करें

एक सकारात्मक परीक्षण केवल यह साबित करता है कि वैध पृष्ठ काम करता है। सुरक्षा सत्यापन उन इनपुट का निर्माण करता है जिन्हें अस्वीकार किया जाना चाहिए:

परीक्षणअपेक्षित परिणाम
हमलावर ओरिजिन एक सही आकार का complete भेजता हैओरिजिन जांच इसे अस्वीकार करती है; कोई भुगतान क्वेरी नहीं चलती
विश्वसनीय ओरिजिन पर एक अन्य iframe संदेश भेजता हैस्रोत जांच इसे अस्वीकार करती है
सही ओरिजिन और स्रोत एक अज्ञात प्रकार या बड़े आकार का फ़ील्ड भेजते हैंस्कीमा सत्यापन इसे अस्वीकार करता है
वही messageId पुन: चलाया (replayed) जाता हैइसे एक बार संभाला जाता है
iframe पुनः निर्माण के बाद एक पुराना चैनल देर से संदेश भेजता हैचैनल जांच इसे अस्वीकार करती है
पैरेंट द्वारा भेजे जाने से पहले iframe किसी अन्य ओरिजिन पर नेविगेट करता हैसटीक targetOrigin के कारण संदेश त्याग दिया जाता है
complete एक अनुपलब्ध परिणाम या किसी अन्य उपयोगकर्ता के स्वामित्व वाला परिणाम वहन करता हैसर्वर प्राधिकरण और आधिकारिक लुकअप इसे अस्वीकार करते हैं
ready देर से आता है, डुप्लिकेट होता है, या कभी नहीं आता हैइडेम्पोटेंट हैंडलिंग या टाइमआउट एक पुन: प्रयास योग्य विफलता स्थिति में प्रवेश करता है

कोड समीक्षा में प्रत्येक postMessage कॉल के लिए "*", प्रत्येक message लिसनर, अस्पष्ट ओरिजिन तुलना, अनियंत्रित event.data, और ऐसे पथ जो संदेश डेटा को innerHTML में लिखते हैं, की भी खोज की जाती है। ब्राउज़र एकीकरण परीक्षण लिसनर टियरडाउन, iframe पुन: निर्माण, एकाधिक इंस्टेंसेस और रूट परिवर्तनों को कवर करते हैं।

उच्च गुणवत्ता वाला नमूना उत्तर

"मैं चैनल को एक भेजने वाली सीमा और एक प्राप्त करने वाली सीमा में विभाजित करूँगा। पैरेंट केवल उसके द्वारा संग्रहीत iframe contentWindow पर भेजता है और सटीक targetOrigin के रूप में https://pay.example.net का उपयोग करता है। आने वाले प्रत्येक संदेश के लिए, लिसनर रनटाइम स्कीमा सत्यापन से पहले पूर्ण event.origin और उसी contentWindow संदर्भ की तुलना करता है। केवल ओरिजिन अपर्याप्त है क्योंकि पृष्ठ में कई सेम-ओरिजिन iframes हो सकते हैं। केवल स्रोत अपर्याप्त है क्योंकि वह विंडो नेविगेट हो सकती थी।

मैं संदेशों का एक छोटा वर्ज़न वाला सेट परिभाषित करूँगा: ready, init, resize, cancel, और complete। पैरेंट iframe लोड करने से पहले अपना लिसनर पंजीकृत करता है। चाइल्ड अपना लिसनर स्थापित करता है और ready भेजता है; सत्यापन के बाद ही पैरेंट एक चैनल ID बनाता है और इनिशियलाइज़ेशन भेजता है। बाद का प्रत्येक संदेश समान चैनल ID और एक अद्वितीय संदेश ID वहन करता है। एक स्टेट मशीन और डिडुप्लिकेशन सेट हैंडशेक से पहले के संदेशों, डुप्लिकेट, पुन: क्रमबद्धता और पूरा होने के बाद स्थिति प्रतिगमन (state regression) को अस्वीकार करते हैं। एक हैंडशेक टाइमआउट पुराने iframe को नष्ट कर देता है, जबकि पुन: प्रयास एक नए विंडो संदर्भ और चैनल का उपयोग करता है।

एक पूर्णता संदेश कभी भी सीधे ऑर्डर को नहीं बदलता है। यह केवल एक अपारदर्शी (opaque) परिणाम संदर्भ वहन करता है। होस्ट सर्वर उपयोगकर्ता और ऑर्डर को मान्य करता है और आधिकारिक स्थिति के लिए भुगतान प्रणाली से पूछताछ करता है। इसलिए विश्वसनीय भुगतान ओरिजिन पर एक बग या XSS भी एक संदेश के साथ भुगतान को जाली नहीं बना सकता है।

मैं होस्ट frame-src, भुगतान-पृष्ठ frame-ancestors और न्यूनतम सैंडबॉक्स अनुमतियों के साथ घटक को भी प्रतिबंधित करूँगा। परीक्षण वैध फ़्लो के साथ-साथ हमलावर ओरिजिन, गलत सेम-ओरिजिन iframe, पुराने चैनल, रीप्ले, iframe नेविगेशन और रेडी टाइमआउट को कवर करेंगे। प्रत्येक अस्वीकृति पथ को संवेदनशील व्यावसायिक क्रियाओं से बचना चाहिए।"

सामान्य गलतियाँ

  • "*" के साथ भेजना → लक्ष्य विंडो किसी हमलावर पृष्ठ पर नेविगेट हो सकती है और फिर भी संवेदनशील डेटा प्राप्त कर सकती है → हमेशा सटीक अपेक्षित targetOrigin का उपयोग करें।
  • प्राप्ति पर केवल event.data.type की जांच करना → विंडो संदर्भ वाला कोई भी पृष्ठ नामित कमांड को जाली बना सकता है → डेटा पार्स करने से पहले सटीक ओरिजिन और अपेक्षित स्रोत की पुष्टि करें।
  • सबस्ट्रिंग या डोमेन प्रत्यय अंश द्वारा ओरिजिन स्वीकार करना → हमलावर डोमेन में विश्वसनीय टेक्स्ट हो सकता है → ब्राउज़र द्वारा आपूर्ति किए गए पूर्ण सामान्यीकृत ओरिजिन की तुलना करें।
  • TypeScript अभिकथन के बाद प्रसंस्करण करना → रनटाइम पर प्रकार मौजूद नहीं होते हैं, इसलिए विकृत डेटा अभी भी लॉजिक में प्रवेश करता है → unknown से शुरू करें और सख्त स्कीमा, सीमा और स्थिति सत्यापन करें।
  • पेज लोड के तुरंत बाद init भेजना → iframe लिसनर शायद पंजीकृत न हो और संदेश गायब हो जाए → चाइल्ड को ready की घोषणा करने दें, इसे मान्य करें, फिर टाइमआउट के साथ इनिशियलाइज़ करें।
  • complete को भुगतान के प्रमाण के रूप में मानना → एक फ़्रंटएंड संदेश एक आधिकारिक व्यावसायिक तथ्य नहीं है, और यहां तक कि एक विश्वसनीय ओरिजिन से भी समझौता किया जा सकता है → होस्ट सर्वर पर अधिकृत करें और आधिकारिक भुगतान स्थिति की जांच करें।
  • हैंडशेक के बाद ओरिजिन जांच रोकना → एक विंडो सत्र के दौरान नेविगेट कर सकती है और दस्तावेजों में पुराने विश्वास को ले जा सकती है → प्रत्येक संदेश को मान्य करें और एक नए चैनल के साथ फिर से बनाए गए सत्र को अलग करें।
  • यह मान लेना कि CORS या सैंडबॉक्स पहले से ही संदेशों को सुरक्षित करता है → वे नेटवर्क रीड्स और दस्तावेज़ क्षमताओं को बाधित करते हैं, संदेश पहचान या डेटा को नहीं → संदेश-स्तरीय जांच बनाए रखें और ब्राउज़र नीति को डिफेंस-इन-डेप्थ के रूप में उपयोग करें।

अनुवर्ती प्रश्न और उत्तर

अनुवर्ती एक: विजेट को हजारों ग्राहक कस्टम डोमेन का समर्थन करना चाहिए। चाइल्ड को पैरेंट ओरिजिन का पता कैसे चलता है?

पहले संदेश के event.origin पर स्वचालित रूप से भरोसा न करें। होस्ट पहले सर्वर के साथ एक एम्बेड सत्र बनाता है। सर्वर ग्राहक डोमेन के स्वामित्व की पुष्टि करता है और अनुमत पैरेंट ओरिजिन को एक अल्पकालिक सत्र से जोड़ता है। भुगतान पृष्ठ उस सत्र से अपेक्षित ओरिजिन प्राप्त करता है और भेजने और प्राप्त करने के लिए केवल उस मान का उपयोग करता है। सत्र समाप्ति, डोमेन परिवर्तन, या विंडो पुनः निर्माण के लिए पुन: प्राधिकरण की आवश्यकता होती है। एक कस्टम डोमेन जिसे सिद्ध नहीं किया जा सकता है, उसे संवेदनशील एम्बेडिंग क्षमता प्राप्त नहीं होनी चाहिए।

अनुवर्ती दो: कई सेम-ओरिजिन iframes को मैसेजिंग की आवश्यकता है। क्या channelId अकेले उन्हें रूट कर सकता है?

हैंडलर संदेश द्वारा घोषित चैनल पर भरोसा करके शुरू नहीं कर सकता। एक वैश्विक लिसनर पहले ज्ञात Window संदर्भों की रजिस्ट्री में इंस्टेंस को खोजने के लिए event.source का उपयोग करता है, फिर उस इंस्टेंस के ओरिजिन, स्थिति और चैनल ID की जांच करता है। चैनल पुराने सत्र और आउट-ऑफ़-ऑर्डर संदेशों को अस्वीकार करता है; विंडो संदर्भ ब्राउज़िंग संदर्भ की पहचान करता है। वे विभिन्न उद्देश्यों की पूर्ति करते हैं।

अनुवर्ती तीन: iframe एक ही भुगतान ओरिजिन पर लॉगिन पृष्ठ और फिर चेकआउट पृष्ठ पर जाता है। हैंडशेक कैसे काम करता है?

एक ओरिजिन पर लॉगिन और चेकआउट समान ब्राउज़र सुरक्षा प्रिंसिपल हैं; पैरेंट event.origin से पथ नहीं सीख सकता। यदि वे समान रूप से विश्वसनीय हैं, तो प्रत्येक iframe load पैरेंट को पुराने चैनल को त्यागने और लोडिंग पर लौटने के लिए मजबूर करता है। यह नए दस्तावेज़ द्वारा सत्र अनुबंध से मेल खाने वाला ready भेजने के बाद ही फिर से हैंडशेक करता है, जो पिछले दस्तावेज़ के विलंबित संदेशों को अस्वीकार करता है। यदि लॉगिन को इनिशियलाइज़ेशन डेटा नहीं देखना चाहिए या उसका ट्रस्ट स्तर कम है, तो इसे किसी अन्य ओरिजिन पर ले जाएं और चेकआउट को एक समर्पित एम्बेडिंग प्रवेश बिंदु दें। एक चैनल ID अनुपलब्ध सेम-ओरिजिन आइसोलेशन की मरम्मत नहीं कर सकता।

अनुवर्ती चार: क्या आप MessageChannel पर स्विच करने के बाद भी ओरिजिन की जांच करते हैं?

पोर्ट संदेशों में विंडो संदेशों के समान ओरिजिन फ़ील्ड नहीं होता है, इसलिए सुरक्षा इस बात पर निर्भर करती है कि शुरू में पोर्ट किसने प्राप्त किया था। इसे स्थानांतरित करने वाले पहले postMessage को सटीक ओरिजिन, स्रोत और हैंडशेक स्थिति को सत्यापित करना होगा। पोर्ट केवल वर्तमान सत्र से संबंधित है और बंद होने पर नष्ट हो जाता है। एक समर्पित पोर्ट गलत रूटिंग को कम करता है, लेकिन यह प्रारंभिक प्रमाणीकरण, संदेश स्कीमा या सर्वर प्राधिकरण की जगह नहीं लेता है।

अनुवर्ती पाँच: iframe स्वचालित ऊंचाई परिवर्तनों को नियंत्रित करता है। आप इसे पेज को अत्यधिक आकार में खींचने से कैसे रोकते हैं?

पहचान-सत्यापित resize भी एक अविश्वसनीय व्यावसायिक इनपुट है। प्रोटोकॉल केवल परिमित पूर्णांकों को स्वीकार करता है; पैरेंट उन्हें उत्पाद-परिभाषित न्यूनतम और अधिकतम ऊंचाइयों तक क्लैंप करता है और अपडेट को दर-सीमित (rate-limit) करता है। यह सीमा से बाहर या अत्यधिक संदेशों के लिए अस्वीकृति के कारणों को रिकॉर्ड करता है। यदि सामग्री को वास्तव में अधिक स्थान की आवश्यकता है, तो आंतरिक स्क्रॉलिंग का उपयोग करें या चाइल्ड को मनमाना CSS नियंत्रण देने के बजाय एक नई उत्पाद सीमा परिभाषित करें।

सार्वजनिक स्रोत

संबंधित प्रश्न