Sistem ekstensi Chrome menerapkan Kebijakan Keamanan Konten (CSP) default yang cukup ketat.
Batasan kebijakannya sederhana: skrip harus dipindahkan ke luar baris ke file JavaScript terpisah, pengendali peristiwa inline harus dikonversi untuk menggunakan addEventListener
, dan eval()
dinonaktifkan.
Namun, kami menyadari bahwa berbagai library menggunakan konstruksi seperti eval()
dan eval
seperti
new Function()
untuk pengoptimalan performa dan kemudahan berekspresi. Library template
sangat rentan terhadap
gaya implementasi ini. Meskipun beberapa framework (seperti Angular.js) mendukung CSP secara langsung, banyak framework populer yang belum diupdate ke mekanisme yang kompatibel dengan dunia tanpa eval
ekstensi. Menghapus dukungan untuk fungsi tersebut terbukti lebih
bermasalah dari yang diharapkan bagi developer.
Dokumen ini memperkenalkan sandboxing sebagai mekanisme yang aman untuk menyertakan library-library ini dalam project Anda tanpa mengorbankan keamanan.
Mengapa sandbox?
eval
berbahaya di dalam ekstensi karena kode yang dieksekusi memiliki akses ke semua hal di lingkungan izin tinggi ekstensi. Ada banyak API chrome.*
canggih yang tersedia dan dapat
berdampak serius pada keamanan dan privasi pengguna; ekstraksi data sederhana adalah hal yang paling tidak perlu dikhawatirkan.
Solusi yang ditawarkan adalah sandbox tempat eval
dapat mengeksekusi kode tanpa akses ke
data ekstensi atau API bernilai tinggi dari ekstensi. Tidak ada data, tidak ada API, tidak masalah.
Kita melakukannya dengan mencantumkan file HTML tertentu di dalam paket ekstensi sebagai sandbox.
Setiap kali halaman dalam sandbox dimuat, halaman tersebut akan dipindahkan ke asal yang unik, dan akan ditolak
akses ke chrome.*
API. Jika memuat halaman dengan sandbox ini ke dalam ekstensi melalui iframe
, kita dapat
meneruskan pesan, membiarkannya bertindak berdasarkan pesan tersebut dengan cara tertentu, dan menunggunya meneruskan kembali
hasil. Mekanisme pesan yang sederhana ini memberikan semua yang kami butuhkan untuk menyertakan aman berbasis eval
dalam alur kerja ekstensi kita.
Membuat dan menggunakan sandbox
Jika Anda ingin langsung mempelajari kode, dapatkan ekstensi contoh sandboxing dan mulailah. Ini adalah contoh kerja API pesan kecil yang dibuat di atas library template Handlebars, dan akan memberi Anda semua yang diperlukan untuk memulai. Bagi Anda yang ingin mendapatkan penjelasan lebih lanjut, mari kita pelajari contoh tersebut bersama-sama di sini.
Membuat daftar file dalam manifes
Setiap file yang harus dijalankan di dalam sandbox harus tercantum dalam manifes ekstensi dengan menambahkan elemen
sandbox
. Ini adalah langkah penting, dan mudah terlupakan, jadi periksa kembali
file dengan sandbox Anda tercantum dalam manifes. Dalam contoh ini, kami meng-{i>sandbox<i} file dengan cerdas
bernama "sandbox.html". Entri manifes terlihat seperti ini:
{
...,
"sandbox": {
"pages": ["sandbox.html"]
},
...
}
Memuat file dalam sandbox
Untuk melakukan sesuatu yang menarik dengan file dengan sandbox, kita perlu memuat file tersebut dalam konteks yang dapat ditangani oleh kode ekstensi. Di sini, sandbox.html telah dimuat ke dalam
halaman ekstensi melalui iframe
. File javaScript halaman berisi kode yang mengirimkan pesan
ke dalam sandbox setiap kali tindakan browser diklik dengan mencari iframe
di halaman, dan memanggil postMessage()
di contentWindow
. Pesan adalah objek yang berisi tiga properti: context
, templateName
, dan command
. Kita akan membahas context
dan command
sebentar lagi.
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, '*');
});
Lakukan hal berbahaya
Saat sandbox.html
dimuat, library ini akan memuat library Handlebars, serta membuat dan mengompilasi sebuah inline
seperti yang disarankan oleh 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>
Ini tidak gagal! Meskipun Handlebars.compile
akhirnya menggunakan new Function
, semuanya berfungsi
seperti yang diharapkan, dan kita akan mendapatkan template yang dikompilasi di templates['hello']
.
Meneruskan hasilnya kembali
Kita akan menyediakan template ini untuk digunakan dengan menyiapkan pemroses pesan yang menerima perintah
dari halaman ekstensi. Kita akan menggunakan command
yang diteruskan untuk menentukan apa yang harus dilakukan (Anda dapat
bayangkan melakukan lebih
dari sekadar {i>rendering<i}; mungkin membuat {i>template<i}? Mungkin mengelolanya dengan cara
tertentu?), dan context
akan diteruskan ke template secara langsung untuk rendering. HTML yang dirender
akan diteruskan kembali ke halaman ekstensi sehingga ekstensi dapat melakukan sesuatu yang bermanfaat di lain waktu:
<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>
Kembali ke halaman ekstensi, kita akan menerima pesan ini, dan melakukan sesuatu yang menarik dengan data html
yang telah diteruskan kepada kita. Dalam hal ini, kita hanya akan menayangkannya melalui notifikasi, tetapi
Anda dapat menggunakan HTML ini dengan aman sebagai bagian dari UI ekstensi. Menyisipkannya melalui
innerHTML
tidak menimbulkan risiko keamanan yang signifikan karena kami memercayai konten yang telah dirender
dalam sandbox.
Mekanisme ini membuat pembuatan template menjadi mudah, tetapi tentu saja tidak terbatas pada pembuatan template. Apa saja kode yang tidak berfungsi secara langsung dalam Kebijakan Keamanan Konten yang ketat dapat di-sandbox; inci Namun, sering kali ada baiknya untuk melakukan sandbox komponen ekstensi Anda yang akan berjalan dengan benar dalam urutan untuk membatasi setiap bagian dari program Anda ke rangkaian hak istimewa terkecil yang diperlukan agar dapat berjalan dengan baik. Presentasi Menulis Aplikasi Web dan Ekstensi Chrome yang Aman dari Google I/O 2012 memberikan beberapa contoh bagus tentang penerapan teknik ini, dan Anda akan menghabiskan waktu 56 menit untuk menontonnya.