Korumalı alana alınmış iframe'lerde eval() işlevini 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 taşınarak ayrı JavaScript dosyaları, satır içi etkinlik işleyiciler addEventListener kullanacak şekilde dönüştürülmelidir ve eval() devre dışı bırakıldı.

Bununla birlikte, çeşitli kütüphanelerin eval() ve eval benzeri yapıları kullandığının farkındayız: Performans optimizasyonu ve ifade kolaylığı için new Function(). Şablon kitaplıkları özellikle bu tür bir uygulamaya eğilimlidir. Bazıları (ör. Angular.js) CSP'yi kutudan çıkar çıkmaz desteklese de popüler çerçevelerin çoğu henüz uzantıların eval'siz dünyasıyla uyumlu bir mekanizmaya güncellenmemiştir. Bu nedenle, söz konusu işlev için desteğin kaldırılması daha fazla beklenenden fazla soruna neden olabilir.

Bu dokümanda, bu kitaplıkları projelerinize dahil etmek için güvenli bir mekanizma olarak korumalı alan kullanımı tanıtılmaktadır. hiçbir zaman ödün vermeden kullanabilirsiniz.

Neden korumalı alan kullanmalı?

eval, yürüttüğü bir kod uzantıdaki her şeye erişebildiğinden bu uzantı içinde tehlikeli bir durum eklentisinin yüksek izinli ortamını yönetebilirsiniz. Şunları yapabilecek bir grup güçlü chrome.* API'si mevcuttur: Kullanıcının güvenliğini ve gizliliğini ciddi şekilde etkileme; endişelenmemizin en az olduğu şey basit veri hırsızlığı. Sunulan çözüm, eval'nin uzantının verilerine veya yüksek değerli API'lerine erişmeden kod yürütebileceği bir korumalı alan sağlar. Veri yok, API yok, sorun değil.

Bu işlemi, uzantı paketinin içindeki belirli HTML dosyalarının korumalı alana alınmış olarak listeleyerek gerçekleştiririz. Korumalı alana alınmış bir sayfa yüklendiğinde benzersiz bir kaynağa taşınır ve reddedilir chrome.* API'lerine erişim. Bu korumalı alan sayfasını bir iframe aracılığıyla uzantımıza yüklersek ona mesaj iletebilir, bu mesajlar üzerinde bir şekilde işlem yapmasına izin verebilir ve bize bir sonuç döndürmesini bekleyebiliriz. Bu basit mesajlaşma mekanizması, uzantımızın iş akışına eval tarafından yönlendirilen kodu güvenli bir şekilde dahil etmek için ihtiyacımız olan her şeyi bize sağlar.

Korumalı alan oluşturma ve kullanma

Doğrudan koda girmek istiyorsanız korumalı alan oluşturma örnek uzantısını alın ve kapalı'ya dokunun. Herkese açık kullanıcı adlarının üst kısmında oluşturulmuş küçük bir mesajlaşma API'sinin çalışan örneğidir. sadece şablon oluşturma kitaplığıdır ve başlamanız için gereken her şeyi sağlar. Konuyu biraz daha ayrıntılı bir şekilde öğrenmek isteyenler için bu örneği birlikte inceleyelim.

Manifest'teki dosyaları listele

Korumalı alan içinde çalışması gereken her dosya, sandbox mülk. Bu kritik bir adımdır ve kolayca unutulabilir. Bu nedenle, korumalı alanınızdaki dosyanızın manifest dosyasında listelenip listelenmediğini tekrar kontrol edin. Bu örnekte, "sandbox.html" olarak adlandırılan dosyayı korumalı alana alıyoruz. Manifest girişi şu şekilde görünür:

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

Korumalı alandaki dosyayı yükleme

Korumalı alan dosyası ile ilginç bir şey yapmak için dosyayı, uzantının kodu tarafından erişilebileceği bir bağlamda yüklememiz gerekir. Burada, sandbox.html bir iframe aracılığıyla uzantı sayfasına yüklendi. Sayfanın JavaScript dosyasında, tarayıcı işlemi tıklandığında sayfadaki iframe öğesini bulup contentWindow öğesinde postMessage() çağrısı yaparak korumalı alana mesaj gönderen bir kod bulunur. Mesaj, context, templateName ve command olmak üzere üç özellik içeren bir nesnedir. Birazdan context ve command konularını inceleyeceğiz.

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, '*');
  });

Tehlikeli bir şey yapma

sandbox.html yüklendiğinde Gider çubuğu kitaplığını yükler ve bir satır içi oluşturup derler Gidiş Çubuklarının önerdiği şekilde bir şablon oluşturun:

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>

Bu işlem başarısız olmaz. Handlebars.compile, new Function'u kullansa da işler tam olarak beklendiği gibi işler ve templates['hello'] biçiminde derlenmiş bir şablon elde ederiz.

Sonucu geri verme

Komutları kabul eden bir mesaj dinleyici oluşturarak bu şablonu kullanıma sunacağız farklı olabilir. Yapılması gerekenleri belirlemek için iletilen command belgesini kullanırız ( yalnızca görüntüleme değil, şablonlar hazırlıyor mu? Belki bunları belirli bir şekilde?) ve context, oluşturma için doğrudan şablona aktarılır. Oluşturulan HTML , uzantının daha sonra yararlı bir şeyler yapabilmesi için uzantı sayfasına geri gönderilir:

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

Uzantı sayfasına geri döndüğünüzde, bu mesajı alırız ve html ile ilginç bir şey yaparız. iletilmiş olabilir. Bu durumda, bu durumu yalnızca bir bildirim aracılığıyla yansıtırı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. Ekleme yöntemi: innerHTML, oluşturulan içeriğe güvendiğimiz için önemli bir güvenlik riski oluşturmaz. sağlayabilirsiniz.

Bu mekanizma, şablon oluşturmayı kolaylaştırır ancak şablon oluşturmayla sınırlı değildir. Herhangi biri katı bir İçerik Güvenliği Politikası altında anında çalışmayan bir kod korumalı alana alınabilir; bir listesini oluştur: Aslında uzantılarınızın sırayla çalışacak bileşenlerini korumalı alana almak genellikle yararlıdır. ve programınızın her bir parçasını mümkün olan en küçük ayrıcalıklarla ve düzgün şekilde yürütülmesini sağlar. Google I/O 2012'deki Güvenli Web Uygulamaları ve Chrome Uzantıları Yazma sunumu, bu tekniklerin kullanıma yönelik bazı iyi örneklerini sunar ve 56 dakikanızı ayırmaya değerdir.