Chrome एक्सटेंशन में eval का इस्तेमाल करना

Chrome का एक्सटेंशन सिस्टम, डिफ़ॉल्ट रूप से कॉन्टेंट की सुरक्षा के बारे में कुछ खास नीति (सीएसपी) लागू करता है. नीति से जुड़ी पाबंदियां आसान हैं: स्क्रिप्ट को आउट-ऑफ़-लाइन, अलग-अलग JavaScript फ़ाइलों में ले जाना चाहिए. साथ ही, इनलाइन इवेंट हैंडलर को addEventListener का इस्तेमाल करने के लिए बदला जाना चाहिए. साथ ही, eval() बंद होना चाहिए. Chrome ऐप्स की और भी ज़्यादा सख्त नीति है और हम इन नीतियों से मिलने वाली सुरक्षा प्रॉपर्टी से बहुत खुश हैं.

हालांकि, हम मानते हैं कि कई तरह की लाइब्रेरी, eval() और eval जैसे कंस्ट्रक्ट का इस्तेमाल करती हैं, जैसे कि new Function() का इस्तेमाल परफ़ॉर्मेंस को ऑप्टिमाइज़ करने और एक्सप्रेशन को आसान बनाने के लिए किया जाता है. खास तौर पर, लाइब्रेरी को इस तरह से लागू करने की ज़रूरत होती है. हालांकि, कुछ (जैसे Angular.js) सीएसपी की सुविधा देते हैं, लेकिन कई लोकप्रिय फ़्रेमवर्क अब तक एक्सटेंशन की eval-की दुनिया के साथ काम करने वाले तरीके में अपडेट नहीं हुए हैं. इसलिए, उस सुविधा को हटाने का अनुरोध, डेवलपर के लिए उम्मीद से ज़्यादा मुश्किल साबित हुआ है.

यह दस्तावेज़, सुरक्षा से समझौता किए बिना, इन लाइब्रेरी को अपने प्रोजेक्ट में शामिल करने के लिए, सैंडबॉक्स की सुविधा को एक सुरक्षित तरीके के रूप में पेश करता है. कम शब्दों के लिए, हम एक्सटेंशन शब्द का इस्तेमाल पूरे संदर्भ में करेंगे. हालांकि, यह सिद्धांत ऐप्लिकेशन पर भी लागू होता है.

सैंडबॉक्स का इस्तेमाल क्यों करें?

eval किसी एक्सटेंशन के अंदर खतरनाक है, क्योंकि इसके ज़रिए एक्ज़ीक्यूट किए जाने वाले कोड के पास एक्सटेंशन की ज़्यादा अनुमति वाली सेटिंग की सभी चीज़ों का ऐक्सेस होता है. कई बेहतरीन chrome.* एपीआई उपलब्ध हैं, जो उपयोगकर्ता की सुरक्षा और निजता पर बुरा असर डाल सकते हैं. डेटा बाहर निकाला जाना हमारी सबसे चिंता का हिस्सा नहीं है. ऑफ़र पर मिलने वाला समाधान एक सैंडबॉक्स है. इसमें eval, एक्सटेंशन के डेटा या एक्सटेंशन के ज़्यादा वैल्यू वाले एपीआई को ऐक्सेस किए बिना कोड को एक्ज़ीक्यूट कर सकता है. कोई डेटा नहीं, कोई एपीआई नहीं, कोई समस्या नहीं.

ऐसा करने के लिए, हम एक्सटेंशन पैकेज में मौजूद खास एचटीएमएल फ़ाइलों को सैंडबॉक्स के तौर पर लिस्ट करते हैं. जब भी कोई सैंडबॉक्स किया गया पेज लोड होगा, तो उसे यूनीक ऑरिजिन पर ले जाया जाएगा. साथ ही, उसे chrome.* API का ऐक्सेस नहीं दिया जाएगा. अगर हम इस सैंडबॉक्स किए गए पेज को iframe की मदद से अपने एक्सटेंशन में लोड करते हैं, तो हम इसे मैसेज भेज सकते हैं. साथ ही, उन मैसेज पर किसी तरह से कार्रवाई कर सकते हैं और तब तक इंतज़ार कर सकते हैं, जब तक हमें कोई नतीजा नहीं मिलता. मैसेज करने के इस आसान तरीके से, हमें वह सब कुछ मिलता है जिसकी मदद से हम अपने एक्सटेंशन के वर्कफ़्लो में, eval वाला कोड सुरक्षित तरीके से शामिल कर सकते हैं.

सैंडबॉक्स बनाना और उसका इस्तेमाल करना.

अगर आपको सीधे कोड का इस्तेमाल करना है, तो कृपया सैंडबॉक्सिंग सैंपल एक्सटेंशन आज़माएं और शुरू करें. यह हैंडलबार की लाइब्रेरी के ऊपर बनाए गए छोटे से मैसेजिंग एपीआई का काम करने वाला उदाहरण है. इससे आपको आगे बढ़ने में मदद मिलेगी. आप में से जो लोग इसके बारे में और जानना चाहते हैं, उन्हें हम इस सैंपल के बारे में बताते हैं.

मेनिफ़ेस्ट में फ़ाइलों की सूची बनाएं

सैंडबॉक्स में चलने वाली हर फ़ाइल के लिए, sandbox प्रॉपर्टी जोड़कर एक्सटेंशन मेनिफ़ेस्ट की सूची में शामिल होना ज़रूरी है. यह एक ज़रूरी चरण है और इसे भूलना आसान है. इसलिए, कृपया दोबारा जांच लें कि आपकी सैंडबॉक्स की गई फ़ाइल मेनिफ़ेस्ट में दी गई है या नहीं. इस नमूने में, हम बड़े पैमाने पर "sandbox.html" नाम वाली फ़ाइल को सैंडबॉक्स कर रहे हैं. मेनिफ़ेस्ट में ऐसी एंट्री दिखती है:

{
  ...,
  "sandbox": {
     "pages": ["sandbox.html"]
  },
  ...
}

सैंडबॉक्स की गई फ़ाइल लोड करें

सैंडबॉक्स की गई फ़ाइल के साथ कुछ दिलचस्प करने के लिए, हमें इसे उस संदर्भ में लोड करना होगा जहां एक्सटेंशन के कोड से इसका पता लगाया जा सके. यहां sandbox.html किसी iframe के ज़रिए एक्सटेंशन के इवेंट पेज (eventpage.html) में लोड किया गया है. eventpage.js में ऐसा कोड होता है जो पेज पर iframe को ढूंढकर और उसके contentWindow पर postMessage तरीके को चलाकर, ब्राउज़र की कार्रवाई को क्लिक करने पर सैंडबॉक्स को एक मैसेज भेजता है. यह मैसेज एक ऑब्जेक्ट है, जिसमें दो प्रॉपर्टी होती हैं: context और command. कुछ देर में, हम दोनों के बारे में बात करेंगे.

chrome.browserAction.onClicked.addListener(function() {
 var iframe = document.getElementById('theFrame');
 var message = {
   command: 'render',
   context: {thing: 'world'}
 };
 iframe.contentWindow.postMessage(message, '*');
});
postMessage एपीआई के बारे में सामान्य जानकारी के लिए, एमडीएन पर postMessage दस्तावेज़ देखें. यह काफ़ी पूरा है और पढ़ने लायक है. खास तौर पर, ध्यान रखें कि अगर डेटा को क्रम से लगाने की अनुमति है, तो ही उसे आगे-पीछे किया जा सकता है. उदाहरण के लिए, फ़ंक्शन नहीं होते.

कुछ खतरनाक करना

sandbox.html लोड होने पर, यह हैंडलबार लाइब्रेरी को लोड करता है. साथ ही, हैंडलबार के सुझाव के मुताबिक इनलाइन टेंप्लेट बनाता और कंपाइल करता है:

<script src="handlebars-1.0.0.beta.6.js"></script>
<script id="hello-world-template" type="text/x-handlebars-template">
  <div class="entry">
    <h1>Hello, !</h1>
  </div>
</script>
<script>
  var templates = [];
  var source = document.getElementById('hello-world-template').innerHTML;
  templates['hello'] = Handlebars.compile(source);
</script>

ऐसा नहीं होता है! भले ही Handlebars.compile, new Function का इस्तेमाल करना बंद कर दे, लेकिन चीज़ें उम्मीद के मुताबिक काम करती हैं. आखिर में, हमें templates['hello'] में कंपाइल किया गया टेंप्लेट मिलता है.

नतीजे को फिर से पास करें

हम एक मैसेज लिसनर सेट करके इस टेंप्लेट को इस्तेमाल के लिए उपलब्ध कराएंगे, जो इवेंट पेज से निर्देश स्वीकार करेगा. हम दिए गए command का इस्तेमाल यह तय करने के लिए करेंगे कि क्या किया जाना चाहिए (आप सिर्फ़ रेंडर करने के अलावा कई और काम करने की कल्पना कर सकते हैं; शायद टेंप्लेट बनाना चाहते हैं? शायद उन्हें किसी तरह से मैनेज करना हो?), और context को रेंडरिंग के लिए सीधे टेंप्लेट में पास कर दिया जाएगा. रेंडर किया गया एचटीएमएल, इवेंट पेज पर वापस पास कर दिया जाएगा, ताकि एक्सटेंशन बाद में इसके साथ काम कर सके:

<script>
  window.addEventListener('message', function(event) {
    var command = event.data.command;
    var name = event.data.name || 'hello';
    switch(command) {
      case 'render':
        event.source.postMessage({
          name: name,
          html: templates[name](event.data.context)
        }, event.origin);
        break;

      // case 'somethingElse':
      //   ...
    }
  });
</script>

इवेंट पेज पर वापस आने पर, हमें यह मैसेज मिलेगा. साथ ही, हम html के पास किए गए डेटा के साथ कुछ दिलचस्प गतिविधि करेंगे. इस मामले में, हम सिर्फ़ एक डेस्कटॉप सूचना के ज़रिए इसकी जानकारी देंगे, लेकिन एक्सटेंशन के यूज़र इंटरफ़ेस (यूआई) के हिस्से के रूप में इस एचटीएमएल का सुरक्षित तरीके से इस्तेमाल करना पूरी तरह से मुमकिन है. innerHTML की मदद से इसे डालने से, सुरक्षा से जुड़ा कोई बड़ा खतरा नहीं होता. यहां तक कि किसी चतुराई वाले हमले के ज़रिए सैंडबॉक्स किए गए कोड का पूरी तरह से समझौता करने से भी, हाई-अनुमति एक्सटेंशन वाले एक्सटेंशन में खतरनाक स्क्रिप्ट या प्लगिन वाले कॉन्टेंट को शामिल नहीं किया जा सकेगा.

इस तरीके की मदद से, टेंप्लेट बनाने की प्रोसेस आसान हो जाती है. हालांकि, यह सिर्फ़ टेंप्लेट के इस्तेमाल तक ही सीमित नहीं है. ऐसा कोई भी कोड जो कॉन्टेंट की सुरक्षा के बारे में सख्त नीति के तहत काम नहीं करता, उसे सैंडबॉक्स किया जा सकता है. असल में, यह अक्सर आपके एक्सटेंशन के कॉम्पोनेंट को सैंडबॉक्स करने के लिए काम का होता है, जो सही तरीके से होते हैं. ऐसा इसलिए किया जाता है, ताकि आपके प्रोग्राम के हर हिस्से को सही तरीके से काम करने के लिए ज़रूरी खास सुविधाओं वाले छोटे सेट तक सीमित किया जा सके. Google I/O 2012 की राइटिंग सिक्योर वेब ऐप्लिकेशन और Chrome एक्सटेंशन प्रज़ेंटेशन में, इन तकनीकों का इस्तेमाल करने के कुछ अच्छे उदाहरण दिए गए हैं. इन्हें अपनाने पर, आपको 56 मिनट का समय मिलेगा.