„eval“ in Chrome-Erweiterungen verwenden

Das Erweiterungssystem von Chrome erzwingt eine relativ strikte standardmäßige Content Security Policy (CSP). Die Richtlinieneinschränkungen sind einfach: Das Skript muss in separate JavaScript-Dateien müssen Inline-Event-Handler zur Verwendung von addEventListener konvertiert werden und eval() ist deaktiviert. Für Chrome-Apps gelten noch strengere Richtlinien. Wir sind mit den Sicherheitseigenschaften dieser Richtlinien sehr zufrieden.

Wir wissen jedoch, dass eine Vielzahl von Bibliotheken eval()- und eval-ähnliche Konstrukte verwendet, z. B.: new Function() für die Leistungsoptimierung und einen einfachen Ausdruck. Vorlagenbibliotheken sind besonders anfällig für diese Art der Implementierung. Einige, z. B. Angular.js, unterstützen CSP auswärts. viele gängige Frameworks noch nicht auf einen Mechanismus aktualisiert, der mit Erweiterungen Welt mit weniger als eval. Die Einstellung der Unterstützung dieser Funktion hat sich daher für Entwickler als problematischer als erwartet erwiesen.

In diesem Dokument wird Sandboxing als sicherer Mechanismus vorgestellt, mit dem Sie diese Bibliotheken in Ihre Projekte einbinden können, ohne die Sicherheit zu gefährden. Der Begriff Erweiterungen wird im Folgenden aus Gründen der Übersichtlichkeit verwendet. Das Konzept gilt aber auch für Anwendungen.

Warum Sandbox?

eval ist innerhalb einer Erweiterung gefährlich, da der von ihm ausgeführte Code Zugriff auf alles im Erweiterung mit umfangreichen Berechtigungen. Es stehen zahlreiche leistungsstarke chrome.* APIs zur Verfügung, die die Sicherheit und den Datenschutz eines Nutzers erheblich beeinträchtigen; Eine einfache Daten-Exfiltration ist für uns am wenigsten sorgen. Die angebotene Lösung ist eine Sandbox, in der eval Code ausführen kann, ohne auf den die Daten der Erweiterung oder die hochwertigen APIs der Erweiterung. Keine Daten, keine APIs, kein Problem.

Dazu werden bestimmte HTML-Dateien im Erweiterungspaket als Sandbox-Dateien aufgeführt. Wenn eine Seite mit Sandbox geladen wird, wird sie an einen eindeutigen Ursprung verschoben und der Zugriff auf chrome.* APIs wird ihr verweigert. Wenn wir diese Sandbox-Seite über eine iframe in unsere Erweiterung laden, können wir ihr Nachrichten übergeben, sie auf diese Nachrichten reagieren lassen und darauf warten, dass sie uns ein Ergebnis zurückgibt. Dieser einfache Nachrichtenmechanismus gibt uns alles, was wir brauchen, um eval-gesteuerte im Workflow unserer Erweiterung.

Sandbox erstellen und verwenden

Wenn Sie direkt mit dem Code beginnen möchten, laden Sie die Beispielerweiterung für die Sandbox herunter und legen Sie los. Es handelt sich um ein funktionierendes Beispiel für eine kleine Messaging-API, die auf der Handlebars-Template-Bibliothek basiert. Sie sollten damit alles haben, was Sie für den Einstieg benötigen. Für diejenigen, die noch etwas mehr Erklärung benötigen, gehen wir das Beispiel hier gemeinsam durch.

Dateien im Manifest auflisten

Jede Datei, die in einer Sandbox ausgeführt werden soll, muss im Manifest der Erweiterung aufgeführt sein. Dazu fügen Sie eine sandbox-Eigenschaft hinzu. Dies ist ein wichtiger Schritt, der leicht vergessen wird. Überprüfen Sie daher, die in der Sandbox ausgeführte Datei im Manifest aufgeführt ist. In diesem Beispiel wird die Datei „sandbox.html“ in einer Sandbox ausgeführt. Der Manifesteintrag sieht so aus:

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

Datei aus der Sandbox laden

Damit wir etwas Interessantes mit der Datei in der Sandbox tun können, müssen wir sie in einem Kontext laden, in dem sie vom Code der Erweiterung angesprochen werden kann. Hier wurde sandbox.html über eine iframe in die Ereignisseite (eventpage.html) der Erweiterung geladen. eventpage.js enthält Code, der jedes Mal, wenn auf die Browseraktion geklickt wird, eine Nachricht an die Sandbox sendet. Dazu wird die iframe auf der Seite gefunden und die postMessage-Methode auf ihrer contentWindow ausgeführt. Die Nachricht ist ein Objekt mit zwei Eigenschaften: context und command. Wir gehen gleich auf beide ein.

chrome.browserAction.onClicked.addListener(function() {
 var iframe = document.getElementById('theFrame');
 var message = {
   command: 'render',
   context: {thing: 'world'}
 };
 iframe.contentWindow.postMessage(message, '*');
});
Allgemeine Informationen zur postMessage API finden Sie in der postMessage-Dokumentation auf MDN. Es ist ziemlich vollständig und lohnt sich zu lesen. Beachten Sie insbesondere, dass Daten nur dann hin- und hergeleitet werden können, wenn sie serialisierbar sind. Funktionen sind das beispielsweise nicht.

Etwas Gefährliches tun

Wenn sandbox.html geladen wird, wird die Handlebars-Bibliothek geladen und eine Inline erstellt und kompiliert. wie in Handlebars vorschlägt:

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

Das scheitert nicht! Handlebars.compile verwendet zwar letztendlich new Function, aber es funktioniert und wir haben eine kompilierte Vorlage in templates['hello'].

Ergebnis zurückgeben

Wir stellen diese Vorlage zur Verwendung zur Verfügung, indem wir einen Nachrichten-Listener einrichten, der Befehle akzeptiert aus der Ereignisseite aus. Anhand der übergebenen command wird bestimmt, was getan werden soll. Sie könnten sich vorstellen, mehr als nur zu rendern, z. B. Vorlagen zu erstellen. Vielleicht verwalten Sie sie in einigen und die context wird zum Rendern direkt an die Vorlage übergeben. Der gerenderte HTML-Code an die Ereignisseite zurück, damit die Erweiterung später sinnvolle Aktionen durchführen kann:

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

Auf der Veranstaltungsseite erhalten wir diese Nachricht und verwenden html etwas Interessantes. die uns übergeben wurden. In diesem Fall geben wir es einfach über eine Desktopbenachrichtigung aus. Es ist aber durchaus möglich, diesen HTML-Code sicher als Teil der Benutzeroberfläche der Erweiterung zu verwenden. Einfügen über innerHTML stellt kein erhebliches Sicherheitsrisiko dar, da selbst ein vollständiger Kompromittierung der Sandbox durch einige clevere Angriffe keine gefährlichen Skript- oder Plug-in-Inhalte den Kontext der Erweiterung mit hohen Berechtigungen.

Dieser Mechanismus vereinfacht die Erstellung von Vorlagen, ist aber natürlich nicht darauf beschränkt. Jeder Code, der unter einer strengen Content Security Policy nicht standardmäßig funktioniert, kann in einer Sandbox ausgeführt werden. Tatsächlich ist es oft sinnvoll, Komponenten Ihrer Erweiterungen, die richtig ausgeführt werden würden, in einer Sandbox zu platzieren, um jedes Teil Ihres Programms auf die kleinste Anzahl von Berechtigungen zu beschränken, die für die ordnungsgemäße Ausführung erforderlich sind. Google-Präsentation Write Secure Web Apps and Chrome Extensions Bei der I/O 2012 finden Sie einige gute Beispiele für diese Techniken in der Praxis. .