Chrome 擴充功能系統會強制執行相當嚴格的預設內容安全政策 (CSP)。
政策限制非常簡單明瞭:指令碼必須從線上移至獨立項目
JavaScript 檔案、內嵌事件處理常式必須轉換為 addEventListener
,而 eval()
為
已停用。Chrome 應用程式有更嚴格的政策,我們很滿意這些政策提供的安全性屬性。
不過,我們也瞭解,許多程式庫都會使用 eval()
和 eval
類似的結構 (例如 new Function()
),以便提升效能並簡化表達方式。模板化程式庫特別容易採用這種實作方式。雖然部分 (例如 Angular.js) 支援 CSP
目前許多熱門架構尚未更新為相容的機制
擴充功能勇闖 eval
的世界。因此,對於開發人員而言,移除這項功能的支援比預期更麻煩。
本文件介紹採用沙箱機制,方便你在專案中加入這些程式庫 而不會犧牲安全性為了簡化說明,我們會在整篇文章中使用「擴充功能」一詞,但這個概念同樣適用於應用程式。
為什麼要採用沙箱機制?
eval
對擴充功能內不安全,因為執行的程式碼能夠存取
排除擴充功能的高權限環境我們提供多種功能強大的 chrome.*
API,這些 API 可能會嚴重影響使用者的安全性和隱私權;簡單的資料外洩只是其中最輕微的問題。優惠解決方案是沙箱,可讓 eval
在不存取
擴充功能的資料或擴充功能的高價值 API。沒有資料、沒有 API,也沒問題。
我們會將擴充功能套件中的特定 HTML 檔案列為沙箱檔案,藉此達成這項目標。每當載入沙箱網頁時,系統都會將該網頁移至獨特來源,並拒絕存取 chrome.*
API。如果我們透過 iframe
將這個沙箱頁面載入擴充功能,就可以傳遞訊息,讓擴充功能以某種方式對這些訊息採取行動,然後等待它傳回結果。這個簡單的訊息機制讓我們能安全納入以 eval
為主的所有功能
將程式碼寫入擴充功能工作流程中
建立及使用沙箱。
若要直接參閱程式碼,請取得沙箱擴充功能範例, 關閉。這是建構在控制列之上的小型訊息 API 實際範例 現在應該能取得所有範本資料,方便您踏出第一步。如想進一步瞭解,我們可以一起查看這個範例。
列出資訊清單中的檔案
凡是應該在沙箱中執行的檔案,都必須在擴充功能資訊清單中列出
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
API 的一般資訊,請參閱 postMessage
說明文件。這份文件相當完整,值得一讀。特別注意,只有可序列化的資料才能來回傳遞。例如函式就不是。從事危險活動
載入 sandbox.html
時,它會載入 Handlebars 程式庫,並按照 Handlebars 建議的方式建立及編譯內嵌範本:
<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
會直接傳入範本進行算繪。經過算繪的 HTML 會傳回至活動頁面,以便擴充功能日後執行實用操作:
<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
資料執行有趣的操作。在這種情況下,我們會透過桌面通知回應,但完全可以安全地將這段 HTML 用於擴充功能的 UI。插入方式:
innerHTML
不會帶來重大安全性風險,即使是沙箱模式完全破壞也一樣
一旦受到一些巧妙的攻擊,就無法將危險的指令碼或外掛程式內容
進階權限的擴充功能背景資訊
這個機制可讓您輕鬆建立範本,但當然不限於範本。任何在嚴格的內容安全政策下無法立即運作的程式碼,都可以進行沙箱處理;事實上,將擴充功能的元件沙箱處理,通常會會正確執行,以便將程式的每個部分限制為執行所需的最小權限集。2012 年 Google I/O 大會的「Writing Secure Web Apps and Chrome Extensions」簡報提供了一些實用的例子,值得花 56 分鐘的時間觀看。