Menggunakan eval di ekstensi Chrome

Sistem ekstensi Chrome menerapkan Kebijakan Keamanan Konten (CSP) default yang cukup ketat. Pembatasan kebijakannya sangat jelas: skrip harus dipindahkan keluar baris menjadi File JavaScript, pengendali peristiwa inline harus dikonversi untuk menggunakan addEventListener, dan eval() adalah dinonaktifkan. Aplikasi Chrome memiliki kebijakan yang lebih ketat, dan kami cukup puas dengan properti keamanan yang disediakan oleh kebijakan ini.

Namun, kami menyadari bahwa berbagai library menggunakan konstruksi seperti eval() dan eval seperti new Function() untuk pengoptimalan performa dan kemudahan ekspresi. Library template terutama 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. Oleh karena itu, menghapus dukungan untuk fungsi tersebut terbukti lebih bermasalah dari yang diharapkan bagi developer.

Dokumen ini memperkenalkan sandbox sebagai mekanisme aman untuk menyertakan library ini dalam project Anda tanpa mengorbankan keamanan. Untuk singkatnya, kami akan menggunakan istilah ekstensi secara keseluruhan, tapi konsep ini berlaku sama untuk aplikasi.

Mengapa menggunakan 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 ekstensi. Tidak ada data, tidak ada API, tidak masalah.

Kami melakukannya dengan mencantumkan file HTML tertentu di dalam paket ekstensi sebagai yang di-sandbox. Setiap kali dimuat, halaman dengan sandbox akan dipindahkan ke asal unik, dan akan ditolak aksesnya ke API chrome.*. Jika kita memuat halaman yang di-sandbox ini ke dalam ekstensi melalui iframe, kita bisa meneruskan pesannya, membiarkannya bertindak terhadap pesan itu dengan cara tertentu, dan menunggunya memberikan kembali hasil pengujian tersebut. Mekanisme pesan sederhana ini memberi kita semua yang diperlukan untuk menyertakan kode yang didorong eval dengan aman dalam alur kerja ekstensi.

Membuat dan menggunakan sandbox.

Jika Anda ingin langsung mempelajari kode, ambil ekstensi contoh sandboxing dan mulai. 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 seperti sedikit penjelasan lebih lanjut, mari kita kita bahas contoh itu bersama-sama di sini.

Mencantumkan file dalam manifes

Setiap file yang harus dijalankan di dalam sandbox harus dicantumkan dalam manifes ekstensi dengan menambahkan properti sandbox. Ini adalah langkah yang penting, dan mudah dilupakan, jadi periksa kembali apakah file yang telah di-sandbox akan tercantum di 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 {i>sandbox<i}, kita perlu mengunggahnya dalam konteks di mana dapat ditangani oleh kode ekstensi. Di sini, sandbox.html telah dimuat ke dalam Halaman Peristiwa ekstensi (eventpage.html) melalui iframe. eventpage.js berisi kode yang mengirim pesan ke sandbox setiap kali tindakan browser diklik dengan mencari iframe di halaman, dan menjalankan metode postMessage di contentWindow. Pesan adalah objek yang berisi dua properti: context dan command. Kita akan membahas keduanya sebentar lagi.

chrome.browserAction.onClicked.addListener(function() {
 var iframe = document.getElementById('theFrame');
 var message = {
   command: 'render',
   context: {thing: 'world'}
 };
 iframe.contentWindow.postMessage(message, '*');
});
Untuk informasi umum tentang postMessage API, lihat dokumentasi postMessage di MDN . Konten ini cukup lengkap dan layak dibaca. Secara khusus, perlu diperhatikan bahwa data hanya dapat diteruskan bolak-balik jika dapat diserialisasi. Fungsi, misalnya, tidak bersifat umum.

Melakukan sesuatu yang berbahaya

Saat dimuat, sandbox.html akan memuat library Handlebars, serta membuat dan mengompilasi template inline dengan cara yang disarankan 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>

Langkah ini tidak gagal! Meskipun Handlebars.compile akhirnya menggunakan new Function, semuanya berfungsi persis seperti yang diharapkan, dan kita mendapatkan template yang dikompilasi di templates['hello'].

Meneruskan hasilnya kembali

Kami akan menyediakan template ini untuk digunakan dengan menyiapkan pemroses pesan yang menerima perintah dari Halaman Peristiwa. Kita akan menggunakan command yang diteruskan untuk menentukan apa yang harus dilakukan (Anda dapat membayangkan melakukan lebih dari sekadar merender; mungkin membuat template? Mungkin mengelolanya dengan cara tertentu?), dan context akan diteruskan ke template secara langsung untuk rendering. HTML yang dirender akan diteruskan kembali ke Halaman Peristiwa sehingga ekstensi dapat melakukan sesuatu yang berguna dengannya nanti:

<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>

Kembali di Halaman Peristiwa, kita akan menerima pesan ini, dan melakukan sesuatu yang menarik dengan data html yang telah diteruskan kepada kita. Dalam hal ini, kita hanya akan menggemakannya melalui Notifikasi Desktop, tetapi HTML ini dapat digunakan dengan aman sebagai bagian dari UI ekstensi. Menyisipkannya melalui innerHTML tidak menimbulkan risiko keamanan yang signifikan, karena bahkan kompromi lengkap terhadap kode sandbox melalui beberapa serangan cerdas tidak akan dapat memasukkan konten plugin atau skrip berbahaya ke dalam konteks ekstensi dengan izin tinggi.

Mekanisme ini membuat pembuatan template menjadi mudah, tetapi tentu saja tidak terbatas pada pembuatan template. Setiap kode yang tidak berfungsi secara otomatis berdasarkan Kebijakan Keamanan Konten yang ketat dapat di-sandbox. Faktanya, sering kali berguna untuk membuat sandbox komponen ekstensi yang akan berjalan dengan benar guna membatasi setiap bagian program ke kumpulan hak istimewa terkecil yang diperlukan agar dapat dijalankan dengan benar. 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.