Chrome uzantılarında değerlendirmeyi kullanma

Chrome'un uzantı sistemi, oldukça katı bir varsayılan İçerik Güvenliği Politikası (İGP) uygular. Politika kısıtlamaları basittir: Komut dosyası satır dışına, ayrı JavaScript dosyalarına taşınmalı, satır içi etkinlik işleyiciler addEventListener kullanacak şekilde dönüştürülmelidir ve eval() devre dışı bırakılmalıdır. Chrome Uygulamalarının daha da katı bir politikası vardır ve bu politikaların sağladığı güvenlik özelliklerinden çok memnunuz.

Bununla birlikte, çeşitli kitaplıkların performans optimizasyonu ve ifade kolaylığı için new Function() gibi eval() ve eval benzeri yapılar kullandığını biliyoruz. Şablon oluşturma kitaplıkları özellikle bu uygulama tarzına eğilimlidir. Bazıları (Angular.js gibi) CSP'yi kullanıma hazır olarak desteklese de birçok popüler çerçeve, uzantıların eval içermeyen dünyasıyla uyumlu bir mekanizmaya henüz güncellenmemiştir. Bu nedenle, bu işleve yönelik desteğin kaldırılmasının geliştiriciler için beklenenden daha sorun olduğu kanıtlandı.

Bu belgede, güvenlikten ödün vermeden bu kitaplıkları projelerinize dahil etmek için güvenli bir mekanizma olarak korumalı alan tanıtılmaktadır. Kısaca uzantılar terimini belge boyunca kullanacağız ancak bu kavram, uygulamalar için de aynı şekilde geçerlidir.

Neden korumalı alan?

eval, çalıştırdığı kod, uzantının yüksek izinli ortamındaki her şeye erişimi olduğundan uzantının içinde tehlikelidir. Kullanıcıların güvenliğini ve gizliliğini önemli ölçüde etkileyebilecek çok sayıda güçlü chrome.* API kullanıma sunulmuştur. Basit veri hırsızlığı, en az endişelendiğimizdir. Sunulan çözüm, eval ürününün, uzantı verilerine veya uzantının yüksek değerli API'lerine erişmeden kod çalıştırabileceği bir korumalı alan olmasıdır. Ne veri, ne API, ne de sorun.

Bunu, belirli HTML dosyalarını uzantı paketi içinde korumalı alan olarak listeleyerek yaparız. Korumalı alana alınmış bir sayfa her yüklendiğinde benzersiz bir kaynağa taşınır ve chrome.* API'lerine erişimi reddedilir. Korumalı alana alınmış bu sayfayı bir iframe aracılığıyla uzantımıza yüklersek söz konusu sayfayı mesaj iletebilir, bu mesajlar üzerinde bir şekilde hareket etmesine izin verebilir ve bize bir sonucu geri vermesini bekleyebiliriz. Bu basit mesajlaşma mekanizması, eval yönlendirmeli kodu uzantımızın iş akışına güvenli bir şekilde eklemek için ihtiyacımız olan her şeyi sağlıyor.

Korumalı alan oluşturma ve kullanma.

Doğrudan koda geçmek isterseniz lütfen korumalı alan örnek uzantısını alın ve kaldırın. Bu, Handlebars şablon kitaplığının üzerine inşa edilmiş küçük bir mesajlaşma API'sının çalışan bir örneğidir ve size başlamak için ihtiyacınız olan her şeyi sağlayacaktır. Biraz daha açıklama isteyenler için buradaki örneği birlikte inceleyelim.

Manifest'teki dosyaları listeleyin

Korumalı alan içinde çalıştırılması gereken her dosya, bir sandbox özelliği eklenerek uzantı manifest'inde listelenmelidir. Bu önemli bir adımdır ve kolayca unutulabilir. Bu nedenle, korumalı alana alınan dosyanızın manifest'te listelendiğini lütfen tekrar kontrol edin. Bu örnekte, akıllı bir şekilde "sandbox.html" adını oluşturan dosyayı korumalı alana alıyoruz. Manifest girişi şöyle görünür:

{
  ...,
  "sandbox": {
     "pages": ["sandbox.html"]
  },
  ...
}

Korumalı alana alınmış dosyayı yükle

Korumalı alana alınan dosyayla ilginç bir şey yapmak için dosyayı uzantı kodunun ele alınabileceği bir bağlamda yüklememiz gerekir. Burada sandbox.html, bir iframe aracılığıyla uzantının Etkinlik Sayfası'na (eventpage.html) yüklenmiştir. eventpage.js, tarayıcı işlemi her tıklandığında sayfada iframe öğesini bulup contentWindow üzerinde postMessage yöntemini çalıştırarak korumalı alana mesaj gönderen bir kod içerir. Mesaj iki özellik içeren bir nesnedir: context ve command. Birazdan her ikisini de ele alacağız.

chrome.browserAction.onClicked.addListener(function() {
 var iframe = document.getElementById('theFrame');
 var message = {
   command: 'render',
   context: {thing: 'world'}
 };
 iframe.contentWindow.postMessage(message, '*');
});
postMessage API hakkında genel bilgiler için MDN ile ilgili postMessage dokümanlarına göz atın. Oldukça eksiksiz ve okumaya değer. Özellikle, verilerin yalnızca seri hale getirilebilir olması durumunda karşılıklı olarak aktarılabileceğini unutmayın. Örneğin işlevler buna dahil değildir.

Tehlikeli bir şey yap

sandbox.html yüklendiğinde, Gidon kitaplığını yükler ve Gidbar'ın önerdiği şekilde bir satır içi şablon oluşturup derler:

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

Hata yok. Handlebars.compile, new Function kodunu kullansa bile işler tam olarak beklendiği gibi çalışır ve templates['hello'] içinde derlenmiş bir şablon elde ederiz.

Sonucu geri verme

Etkinlik Sayfası'ndan gelen komutları kabul eden bir mesaj işleyici oluşturarak bu şablonu kullanılabilir hale getireceğiz. Ne yapılması gerektiğini belirlemek için iletilen command bilgisini kullanacağız (yalnızca oluşturma işleminden daha fazlasını yapmayı, belki de şablon oluşturmayı düşünebilirsiniz. Belki bunları bir şekilde yönetir misiniz?) ve context, oluşturma için doğrudan şablona iletilir. Oluşturulan HTML, Etkinlik Sayfası'na geri gönderilir ve böylece uzantı daha sonra bununla ilgili faydalı bir şey yapabilir:

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

Etkinlik Sayfası'na döndüğünüzde, bu mesajı alıp ilettiğimiz html verileriyle ilginç bir şey yapacağız. Bu örnekte, bunu bir Masaüstü Bildirimi ile vurgulayacağız, ancak bu HTML'yi, uzantının kullanıcı arayüzünün bir parçası olarak güvenli bir şekilde kullanmak tamamen mümkündür. Korumalı alana alınmış kodun zekice bir saldırıyla ele alınması dahi yüksek izinli uzantı bağlamına tehlikeli komut dosyası veya eklenti içeriği ekleyemeyeceğinden, innerHTML aracılığıyla eklenmesi önemli bir güvenlik riski oluşturmaz.

Bu mekanizma, şablon oluşturmayı basitleştirir ancak elbette şablon oluşturmakla sınırlı değildir. Katı İçerik Güvenliği Politikası kapsamında çalışmayan tüm kodlar korumalı alana alınabilir. Aslında, programınızın her bir parçasını doğru yürütülmesi için gereken en küçük ayrıcalık grubuyla kısıtlamak üzere, doğru şekilde çalışacak uzantılarınızın korumalı alana alınması genellikle yararlıdır. Google I/O 2012'deki Write Secure Web Apps and Chrome Extensions (Güvenli Web Uygulamaları ve Chrome Uzantıları Yazma) sunumunda, bu tekniği uygulamalı olarak ne kadar başarılı örneklerle görebilirsiniz? Bu sunum için 56 dakikanızı ayırmanız yeterli.