Chrome का एक्सटेंशन सिस्टम, डिफ़ॉल्ट रूप से कॉन्टेंट की सुरक्षा के लिए नीति (सीएसपी) लागू करता है.
नीति से जुड़ी पाबंदियां आसान हैं: स्क्रिप्ट को अलग
JavaScript फ़ाइलों में ले जाना होगा, इनलाइन इवेंट हैंडलर को addEventListener का इस्तेमाल करने के लिए बदलना होगा, और eval() बंद
है.
हालांकि, हम यह मानते हैं कि कई लाइब्रेरी, परफ़ॉर्मेंस को ऑप्टिमाइज़ करने और आसानी से एक्सप्रेशन बनाने के लिए, eval() और eval जैसे कंस्ट्रक्ट का इस्तेमाल करती हैं. जैसे, new Function(). टेंप्लेटिंग लाइब्रेरी में, इस तरह के कोड को लागू करने की संभावना ज़्यादा होती है. कुछ फ़्रेमवर्क (जैसे, Angular.js) CSP का इस्तेमाल करते हैं. हालांकि, कई लोकप्रिय फ़्रेमवर्क अब तक ऐसे मेकेनिज़्म पर अपडेट नहीं हुए हैं जो एक्सटेंशन के eval-लेस वर्ल्ड के साथ काम कर सके. इसलिए, डेवलपर के लिए उस सुविधा को हटाना, उम्मीद से ज़्यादा मुश्किल साबित हुआ है.
इस दस्तावेज़ में, सैंडबॉक्सिंग को एक सुरक्षित तरीके के तौर पर पेश किया गया है. इसकी मदद से, इन लाइब्रेरी को अपने प्रोजेक्ट में शामिल किया जा सकता है. इससे सुरक्षा से समझौता नहीं करना पड़ता.
सैंडबॉक्स क्यों?
eval एक्सटेंशन के लिए खतरनाक है, क्योंकि यह जिस कोड को एक्ज़ीक्यूट करता है उसके पास एक्सटेंशन के हाई-परमिशन एनवायरमेंट में मौजूद हर चीज़ का ऐक्सेस होता है. browser.* के कई ऐसे एपीआई उपलब्ध हैं जो किसी व्यक्ति की सुरक्षा और निजता पर बुरा असर डाल सकते हैं. डेटा चोरी की समस्या इनमें से एक है.
ऑफ़र किया गया समाधान एक सैंडबॉक्स है. इसमें eval, एक्सटेंशन के डेटा या एक्सटेंशन के हाई-वैल्यू एपीआई को ऐक्सेस किए बिना कोड को एक्ज़ीक्यूट कर सकता है. इंटरनेट डेटा हो या न हो, एपीआई हो या न हो, फ़िक्र की कोई बात नहीं.
हम ऐसा एक्सटेंशन पैकेज में मौजूद कुछ एचटीएमएल फ़ाइलों को सैंडबॉक्स के तौर पर लिस्ट करके करते हैं.
जब भी कोई सैंडबॉक्स किया गया पेज लोड होता है, तो उसे यूनीक ऑरिजिन पर ले जाया जाएगा. साथ ही, उसे browser.* एपीआई का ऐक्सेस नहीं दिया जाएगा. अगर हम इस सैंडबॉक्स किए गए पेज को iframe के ज़रिए अपने एक्सटेंशन में लोड करते हैं, तो हम इसे मैसेज भेज सकते हैं. साथ ही, इसे उन मैसेज पर किसी तरह की कार्रवाई करने की अनुमति दे सकते हैं. इसके अलावा, हम इसके नतीजे का इंतज़ार कर सकते हैं. मैसेज भेजने के इस आसान तरीके से, हमें वह सब कुछ मिलता है जो हमें अपने एक्सटेंशन के वर्कफ़्लो में eval-ड्राइव कोड को सुरक्षित तरीके से शामिल करने के लिए चाहिए.
सैंडबॉक्स बनाना और उसका इस्तेमाल करना
अगर आपको सीधे कोड पर जाना है, तो सैंडबॉक्सिंग का सैंपल एक्सटेंशन डाउनलोड करें और शुरू करें. यह Handlebars टेंप्लेटिंग लाइब्रेरी पर बनाया गया, छोटा मैसेजिंग एपीआई है. इससे आपको काम शुरू करने के लिए ज़रूरी सभी चीज़ें मिल जाएंगी. अगर आपको इस बारे में ज़्यादा जानकारी चाहिए, तो आइए यहां दिए गए सैंपल को एक साथ देखते हैं.
मेनिफ़ेस्ट में मौजूद फ़ाइलों की सूची
सैंडबॉक्स में चलाई जाने वाली हर फ़ाइल को एक्सटेंशन मेनिफ़ेस्ट में लिस्ट किया जाना चाहिए. इसके लिए, sandbox प्रॉपर्टी जोड़ें. यह एक ज़रूरी चरण है और इसे आसानी से भूला जा सकता है. इसलिए, पक्का करें कि आपकी सैंडबॉक्स की गई फ़ाइल, मेनिफ़ेस्ट में शामिल हो. इस सैंपल में, हम "sandbox.html" नाम की फ़ाइल को स्मार्ट तरीके से सैंडबॉक्स कर रहे हैं. मेनिफ़ेस्ट एंट्री कुछ ऐसी दिखती है:
{
...,
"sandbox": {
"pages": ["sandbox.html"]
},
...
}
सैंडबॉक्स की गई फ़ाइल लोड करना
सैंडबॉक्स की गई फ़ाइल के साथ कुछ दिलचस्प करने के लिए, हमें इसे ऐसे कॉन्टेक्स्ट में लोड करना होगा जहां एक्सटेंशन का कोड इसे ऐक्सेस कर सके. यहां, sandbox.html को iframe का इस्तेमाल करके एक्सटेंशन पेज में लोड किया गया है. पेज की JavaScript फ़ाइल में ऐसा कोड होता है जो ब्राउज़र ऐक्शन पर क्लिक किए जाने पर, सैंडबॉक्स में एक मैसेज भेजता है. इसके लिए, वह पेज पर मौजूद iframe को ढूंढता है और उसके contentWindow पर postMessage() को कॉल करता है. यह मैसेज एक ऑब्जेक्ट है. इसमें तीन प्रॉपर्टी होती हैं: context, templateName, और command. हम context और command के बारे में थोड़ी देर में बात करेंगे.
service-worker.js:
browser.action.onClicked.addListener(() => {
browser.tabs.create({
url: 'mainpage.html'
});
console.log('Opened a tab with a sandboxed page!');
});
extension-page.js:
let counter = 0;
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('reset').addEventListener('click', function () {
counter = 0;
document.querySelector('#result').innerHTML = '';
});
document.getElementById('sendMessage').addEventListener('click', function () {
counter++;
let message = {
command: 'render',
templateName: 'sample-template-' + counter,
context: { counter: counter }
};
document.getElementById('theFrame').contentWindow.postMessage(message, '*');
});
कोई खतरनाक काम करना
sandbox.html लोड होने पर, यह Handlebars लाइब्रेरी को लोड करता है. साथ ही, Handlebars के सुझाव के मुताबिक, एक इनलाइन टेंप्लेट बनाता है और उसे कंपाइल करता है:
extension-page.html:
<!DOCTYPE html>
<html>
<head>
<script src="mainpage.js"></script>
<link href="styles/main.css" rel="stylesheet" />
</head>
<body>
<div id="buttons">
<button id="sendMessage">Click me</button>
<button id="reset">Reset counter</button>
</div>
<div id="result"></div>
<iframe id="theFrame" src="sandbox.html" style="display: none"></iframe>
</body>
</html>
sandbox.html:
<script id="sample-template-1" type="text/x-handlebars-template">
<div class='entry'>
<h1>Hello</h1>
<p>This is a Handlebar template compiled inside a hidden sandboxed
iframe.</p>
<p>The counter parameter from postMessage() (outer frame) is:
</p>
</div>
</script>
<script id="sample-template-2" type="text/x-handlebars-template">
<div class='entry'>
<h1>Welcome back</h1>
<p>This is another Handlebar template compiled inside a hidden sandboxed
iframe.</p>
<p>The counter parameter from postMessage() (outer frame) is:
</p>
</div>
</script>
यह कभी फ़ेल नहीं होता! Handlebars.compile, new Function का इस्तेमाल करता है. हालांकि, चीज़ें ठीक वैसी ही काम करती हैं जैसी उम्मीद की जाती है. साथ ही, हमें templates['hello'] में कंपाइल किया गया टेंप्लेट मिलता है.
नतीजे को वापस पास करना
हम इस टेंप्लेट को इस्तेमाल करने के लिए उपलब्ध कराएंगे. इसके लिए, हम एक मैसेज लिसनर सेट अप करेंगे, जो एक्सटेंशन पेज से मिलने वाले निर्देशों को स्वीकार करेगा. हम command में पास किए गए डेटा का इस्तेमाल करके यह तय करेंगे कि क्या करना है. हालांकि, सिर्फ़ रेंडर करने के अलावा और भी काम किए जा सकते हैं. जैसे, टेंप्लेट बनाना? शायद उन्हें किसी तरह मैनेज किया जा रहा हो?), और context को सीधे टेंप्लेट में रेंडर करने के लिए पास किया जाएगा. रेंडर किया गया एचटीएमएल, एक्सटेंशन पेज पर वापस भेज दिया जाएगा, ताकि एक्सटेंशन बाद में इसका इस्तेमाल कर सके:
<script>
const templatesElements = document.querySelectorAll(
"script[type='text/x-handlebars-template']"
);
let templates = {},
source,
name;
// precompile all templates in this page
for (let i = 0; i < templatesElements.length; i++) {
source = templatesElements[i].innerHTML;
name = templatesElements[i].id;
templates[name] = Handlebars.compile(source);
}
// Set up message event handler:
window.addEventListener('message', function (event) {
const command = event.data.command;
const template = templates[event.data.templateName];
let result = 'invalid request';
// if we don't know the templateName requested, return an error message
if (template) {
switch (command) {
case 'render':
result = template(event.data.context);
break;
// you could even do dynamic compilation, by accepting a command
// to compile a new template instead of using static ones, for example:
// case 'new':
// template = Handlebars.compile(event.data.templateSource);
// result = template(event.data.context);
// break;
}
} else {
result = 'Unknown template: ' + event.data.templateName;
}
event.source.postMessage({ result: result }, event.origin);
});
</script>
एक्सटेंशन पेज पर वापस आने पर, हमें यह मैसेज मिलेगा. साथ ही, हमें मिले html डेटा का इस्तेमाल करके कुछ दिलचस्प काम किया जाएगा. इस मामले में, हम इसे सिर्फ़ सूचना के ज़रिए दिखाएंगे. हालांकि, एक्सटेंशन के यूज़र इंटरफ़ेस (यूआई) के हिस्से के तौर पर, इस एचटीएमएल का सुरक्षित तरीके से इस्तेमाल किया जा सकता है. इसे innerHTML के ज़रिए डालने से सुरक्षा से जुड़ा कोई गंभीर जोखिम नहीं होता, क्योंकि हमें उस कॉन्टेंट पर भरोसा होता है जिसे सैंडबॉक्स में रेंडर किया गया है.
इस तरीके से टेंप्लेट बनाना आसान हो जाता है. हालांकि, इसका इस्तेमाल सिर्फ़ टेंप्लेट बनाने के लिए नहीं किया जाता. सैंडबॉक्स में ऐसे कोड को रखा जा सकता है जो कॉन्टेंट सुरक्षा नीति के तहत, बॉक्स से बाहर काम नहीं करता. दरअसल, अपने एक्सटेंशन के उन कॉम्पोनेंट को सैंडबॉक्स में रखना अक्सर फ़ायदेमंद होता है जो सही तरीके से काम करते हैं. ऐसा इसलिए, ताकि आपके प्रोग्राम के हर हिस्से को, सही तरीके से काम करने के लिए ज़रूरी कम से कम विशेषाधिकारों तक सीमित किया जा सके. Google I/O 2012 के Writing Secure Web Apps and Chrome Extensions प्रज़ेंटेशन में, इन तकनीकों के कुछ बेहतरीन उदाहरण दिए गए हैं. इसे देखने में 56 मिनट लगते हैं.