Korzystanie z eval w rozszerzeniach do Chrome

System rozszerzeń do Chrome wymusza dość rygorystyczne ustawienia Content Security Policy (CSP). Ograniczenia wynikające z zasad są proste: skrypt musi zostać przeniesiony poza wiersz do osobnego JavaScript, wbudowane moduły obsługi zdarzeń muszą być przekonwertowane, aby można było używać addEventListener, a eval() to wyłączono. Aplikacje Chrome mają jeszcze bardziej rygorystyczne zasady i jesteśmy dość zadowoleni z zabezpieczeń właściwości udostępnianych przez te zasady.

Zdajemy sobie sprawę, że w różnych bibliotekach używane są konstrukcje podobne do eval() i eval, takie jak new Function() ze względu na optymalizację skuteczności i swobodę wypowiedzi. Biblioteki szablonów szczególnie podatne na ten styl implementacji. Chociaż niektóre (np. Angular.js) obsługują CSP jednak wiele popularnych platform nie zostało jeszcze zaktualizowanych i nie zaoferowano w nich mechanizmu świat bez eval. Usunięcie obsługi tej funkcji okazało się więc bardziej dowodem niż oczekiwano.

Ten dokument przedstawia piaskownicę jako bezpieczny mechanizm umieszczania tych bibliotek w projektach. bez wpływu na bezpieczeństwo. Dla zwięzłości używamy terminu rozszerzenia, ale ma zastosowanie do aplikacji w równym stopniu.

Dlaczego piaskownica?

Element eval jest niebezpieczny w rozszerzeniu, ponieważ uruchamiany przez niego kod ma dostęp do wszystkich elementów z środowisko wysokiej dostępności rozszerzenia. Dostępnych jest wiele zaawansowanych interfejsów API chrome.*, które pozwalają poważnie wpływać na bezpieczeństwo i prywatność użytkowników; prosty wydobycie danych to dla nas najmniejszy problem. Oferowanym rozwiązaniem jest piaskownica, w której eval może uruchamiać kod bez dostępu do dane rozszerzenia lub wartościowe interfejsy API rozszerzenia. Nie ma danych, interfejsów API, to żaden problem.

Można to osiągnąć, umieszczając określone pliki HTML w pakiecie rozszerzeń jako środowisko umieszczone w piaskownicy. Przy każdym wczytywaniu strony w piaskownicy jest ona przenoszona do unikalnego źródła i odrzucana dostęp do interfejsów API usługi chrome.*. Jeśli wczytamy tę stronę w piaskownicy do naszego rozszerzenia za pomocą interfejsu iframe, i zaczekaj, aż przekaże nam wiadomość, wynik. Ten prosty mechanizm przesyłania wiadomości daje nam wszystko, czego potrzebujemy, aby bezpiecznie umieszczać reklamy oparte na eval w przepływie pracy naszego rozszerzenia.

Tworzenie piaskownicy i korzystanie z niej.

Jeśli chcesz od razu zagłębić się w kod, ściągnij przykładowe rozszerzenie do piaskownicy i wykonaj wyłączone. To działający przykład niewielkiego interfejsu API do przesyłania wiadomości stworzonego w górnej części kierunków kierownicy. z biblioteką szablonów. Powinien on dać Ci wszystko, czego potrzebujesz na początek. Dla tych, którzy omówimy dokładniej tę próbkę.

Wyświetl listę plików w manifeście

Każdy plik, który powinien zostać uruchomiony w środowisku piaskownicy, musi być wymieniony w manifeście rozszerzenia przez dodanie parametru sandbox. To bardzo ważna czynność, którą łatwo zapomnieć, dlatego plik znajdujący się w trybie piaskownicy jest wymieniony w pliku manifestu. W tym przykładzie sprytnie zamknęliśmy plik w piaskownicy o nazwie „sandbox.html”. Wpis w pliku manifestu wygląda tak:

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

Wczytywanie pliku w trybie piaskownicy

Aby zrobić coś interesującego z plikiem w trybie piaskownicy, musimy wczytać go w kontekście, można go zaadresować za pomocą kodu rozszerzenia. Strona sandbox.html została wczytana do sekcji Strona zdarzenia (eventpage.html) rozszerzenia za pomocą iframe. Plik eventpage.js zawiera kod który wysyła wiadomość do piaskownicy za każdym razem, gdy użytkownik kliknie działanie przeglądarki przez znalezienie iframe na stronie i uruchomienie metody postMessage na zasobie contentWindow. Wiadomość jest obiektem zawierający 2 właściwości: context i command. Obydwie możliwości omówimy za chwilę.

chrome.browserAction.onClicked.addListener(function() {
 var iframe = document.getElementById('theFrame');
 var message = {
   command: 'render',
   context: {thing: 'world'}
 };
 iframe.contentWindow.postMessage(message, '*');
});
Ogólne informacje o interfejsie postMessage API znajdziesz w dokumentacji postMessage w MDN . Jest kompletna i warto ją przeczytać. Przede wszystkim pamiętaj, że dane mogą być przekazywane tam i z powrotem tylko wtedy, gdy są serializowalne. Funkcje to nie są na przykład funkcje.

Zrób coś niebezpiecznego

Po załadowaniu sandbox.html wczytuje bibliotekę „Handlebars” i tworzy i kompiluje link w tekście w taki sposób, w jaki sugeruje to aplikacja 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>

To nie problem! Mimo że Handlebars.compile korzysta z usługi new Function, wszystko działa zgodnie z oczekiwaniami – otrzymujemy skompilowany szablon w templates['hello'].

Przekaż wynik z powrotem

Udostępnimy ten szablon do użytku po skonfigurowaniu odbiornika wiadomości, który akceptuje polecenia na stronie Wydarzenie. Na podstawie przekazanych command określimy, co należy zrobić (możesz wyobraź sobie, że możesz robić coś więcej niż tylko renderowanie. tworzy szablony? Być może w niektórych przypadkach ?), a elementy context będą przekazywane bezpośrednio do szablonu w celu renderowania. Wyrenderowany HTML zostanie zwrócony do strony wydarzenia, dzięki czemu rozszerzenie może zrobić z nim coś przydatnego później:

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

Na stronie wydarzenia otrzymamy tę wiadomość i zrobimy coś interesującego z funkcją html przekazywane przez nas dane. W tym przypadku powtórzymy to za pomocą powiadomienia na pulpicie, ale pozwala na bezpieczne użycie tego kodu HTML jako części interfejsu rozszerzenia. Wstawianie go przez Usługa innerHTML nie stanowi poważnego zagrożenia dla bezpieczeństwa, ponieważ jest nawet całkowita i nie stanowi zagrożenia dla piaskownicy za pomocą sprytnego ataku nie mogliby wstrzyknąć niebezpiecznego skryptu lub wtyczki kontekst rozszerzenia o dużych uprawnieniach.

Ten mechanizm ułatwia tworzenie szablonów, ale oczywiście nie ogranicza się do tworzenia szablonów. Dowolne kod, który nie działa od razu przy rygorystycznych zasadach Content Security Policy, może być umieszczony w piaskownicy; cale warto umieścić w piaskownicy komponenty rozszerzeń, które mogłyby działać prawidłowo, w kolejności to ograniczenie do najmniejszego zestawu uprawnień wymaganych do tego, i wykonaniu działania. Prezentacja Jak tworzyć bezpieczne aplikacje internetowe i rozszerzenia do Chrome od Google Dobrym przykładem zastosowania tej techniki w praktyce jest konferencja I/O 2012. Warto poświęcić 56 minut obecnie się znajdujesz.