Chrome の拡張機能システムでは、かなり厳格なデフォルトのコンテンツ セキュリティ ポリシー(CSP)が適用されます。ポリシーの制限は単純です。スクリプトは行外に配置し、
JavaScript ファイルとインライン イベント ハンドラは、addEventListener
を使用するように変換する必要があります。eval()
は、
無効です。
ただし、さまざまなライブラリで、パフォーマンスの最適化と表現の容易さのために、eval()
や eval
のような構造(new Function()
など)が使用されていることを認識しています。テンプレート ライブラリは、
このような実装方法になりがちです一方(Angular.js など)は CSP をサポートしており、
多くの一般的なフレームワークは、Google Cloud と互換性のあるメカニズムにまだ更新できていません。
拡張機能eval
の距離の短い世界。そのため、この機能のサポートを削除することは、デベロッパーにとって想定よりも問題が多いことが判明しました。
このドキュメントでは、これらのライブラリをプロジェクトに含めるための安全なメカニズムとして、サンドボックス化について説明します。 セキュリティを損なうこともありません。
サンドボックスを使用する理由
eval
は拡張機能内で危険です。実行されるコードは、拡張機能の高権限環境内のすべてのものにアクセスできるためです。以下を実現できる強力な chrome.*
API が多数用意されています。
ユーザーのセキュリティとプライバシーに深刻な影響を与えるため。単純なデータの引き出しは最小限です
提供されているソリューションは、eval
がどちらのコードにもアクセスせずにコードを実行できるようにするサンドボックスです。
拡張機能の高価値 API に対して行われます。データも API も不要です。
これは、拡張機能パッケージ内の特定の HTML ファイルをサンドボックス化してリストすることで実現しています。
サンドボックス化されたページが読み込まれるたびに、そのページは一意のオリジンに移動され、chrome.*
API へのアクセスが拒否されます。このサンドボックス化されたページを iframe
経由で拡張機能に読み込むと、次のことができます。
Kubernetes にメッセージを渡し、そのメッセージに対してなんらかの方法で処理し、
表示されます。このシンプルなメッセージ メカニズムにより、eval
ドリブンを安全に含めるために必要なすべての機能が提供されます。
ワークフロー内のコードを実行します
サンドボックスを作成して使用する
すぐにコードを試したい場合は、サンドボックス サンプル拡張機能を入手して開始してください。これは、Handlebars テンプレート ライブラリ上に構築された小さなメッセージング API の動作例であり、開始に必要なものがすべて揃っています。詳しく説明を希望される方は、このサンプルを一緒に見ていきましょう。
マニフェスト内のファイルを一覧表示する
サンドボックス内で実行する必要がある各ファイルは、sandbox
プロパティを追加して拡張機能マニフェストにリストする必要があります。これは重要なステップであり、忘れやすいため、
サンドボックス化されたファイルがマニフェストにリストされています。このサンプルでは、巧妙に「sandbox.html」という名前のファイルをサンドボックス化しています。マニフェスト エントリは次のようになります。
{
...,
"sandbox": {
"pages": ["sandbox.html"]
},
...
}
サンドボックス化されたファイルを読み込む
サンドボックス化されたファイルで何か面白いことをするには、拡張機能のコードによってアクセスできるコンテキストでファイルを読み込む必要があります。ここでは、sandbox.html がファイルに読み込まれ、
iframe
を介した拡張機能ページ。ページの JavaScript ファイルには、ブラウザ アクションがクリックされるたびに、ページ上の iframe
を見つけてその contentWindow
で postMessage()
を呼び出して、サンドボックスにメッセージを送信するコードが含まれています。メッセージは、context
、templateName
、command
の 3 つのプロパティを含むオブジェクトです。context
と command
については後ほど説明します。
service-worker.js:
chrome.action.onClicked.addListener(() => {
chrome.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
がレンダリング用に直接テンプレートに渡されます。レンダリングされた HTML
後で拡張機能がそれを使用して有用な処理を行えるように、拡張機能のページに返されます。
<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
で興味深い処理が行われます
渡したデータを確認できます。ここでは、通知を介してエコー出力しますが、
この HTML を拡張機能の UI の一部として安全に使用できます。サンドボックス内でレンダリングされたコンテンツは信頼できるため、innerHTML
経由で挿入しても重大なセキュリティ リスクは発生しません。
このメカニズムによりテンプレートの作成は単純ですが、もちろんテンプレートに限定されません。制限なし 厳格なコンテンツ セキュリティ ポリシーですぐに利用できないコードは、サンドボックス化できます。 拡張機能のコンポーネントをサンドボックス化し、正常に実行される プログラムの各部分を、実行に必要な最小限の権限に制限します。 必要があります。Google 提供の安全なウェブアプリと Chrome 拡張機能の作成に関するプレゼンテーション I/O 2012 では、これらの手法の実例をいくつか紹介しています。 あります。