Spielen Sie sicher in Sandbox-IFrames

Um eine umfassende Erfahrung im heutigen Web zu erstellen, müssen fast unvermeidlich Komponenten und Inhalte eingebettet werden, über die Sie keine wirkliche Kontrolle haben. Widgets von Drittanbietern können das Engagement fördern und eine wichtige Rolle für die allgemeine Benutzererfahrung spielen. Benutzergenerierte Inhalte sind manchmal sogar wichtiger als die nativen Inhalte einer Website. Auf beides zu verzichten ist eigentlich keine Option, aber beide erhöhen das Risiko, dass auf Ihrer Website etwas Schlimmes passiert. Jedes Widget, das Sie einbetten - jede Anzeige, jedes Social-Media-Widget - ist ein potenzieller Angriffsvektor für Personen mit böswilliger Absicht:

Content Security Policy (CSP) kann die mit diesen beiden Arten von Inhalten verbundenen Risiken mindern, indem Sie spezifisch vertrauenswürdige Quellen für Skripte und andere Inhalte auf die Whitelist setzen können. Dies ist ein wichtiger Schritt in die richtige Richtung, aber es ist erwähnenswert, dass der Schutz, den die meisten CSP-Richtlinien bieten, binär ist: Die Ressource ist zulässig oder nicht. Es gibt Zeiten, in denen es nützlich wäre zu sagen: „Ich bin mir nicht sicher, ob ich dieser Quelle von Inhalten wirklich vertraue , aber sie ist soooo hübsch! Betten Sie es bitte ein, Browser, aber lassen Sie es meine Website nicht beschädigen. “

Am wenigsten Privileg

Im Wesentlichen suchen wir nach einem Mechanismus, der es uns ermöglicht, Inhalte zu gewähren, in die wir nur das Mindestmaß an Fähigkeiten einbetten, die für die Ausführung ihrer Aufgabe erforderlich sind. Wenn ein Widget nicht braucht ein neues Fenster öffnet, Wegnehmen Zugang zu window.open nicht schaden kann. Wenn kein Flash erforderlich ist, sollte das Deaktivieren der Plugin-Unterstützung kein Problem darstellen. Wir sind so sicher wie möglich, wenn wir dem Prinzip der geringsten Berechtigungen folgen und jede einzelne Funktion blockieren, die für die Funktionalität, die wir verwenden möchten, nicht direkt relevant ist. Das Ergebnis ist, dass wir nicht mehr blind darauf vertrauen müssen, dass einige eingebettete Inhalte nicht die Berechtigungen nutzen, die sie nicht nutzen sollten. Es wird einfach überhaupt keinen Zugriff auf die Funktionalität haben.

iframeElemente sind der erste Schritt in Richtung eines guten Rahmens für eine solche Lösung. Das Laden einer nicht vertrauenswürdigen Komponente in eine iframebietet ein Maß für die Trennung zwischen Ihrer Anwendung und dem Inhalt, den Sie laden möchten. Der gerahmte Inhalt hat weder Zugriff auf das DOM Ihrer Seite oder auf Daten, die Sie lokal gespeichert haben, noch kann er an beliebige Positionen auf der Seite zeichnen. Der Umfang ist auf den Umriss des Rahmens beschränkt. Die Trennung ist jedoch nicht wirklich robust. Die enthaltene Seite bietet noch eine Reihe von Optionen für ärgerliches oder böswilliges Verhalten: Das automatische Abspielen von Videos, Plugins und Popups ist die Spitze des Eisbergs.

Das sandboxAttribut des iframeElements gibt uns genau das, was wir brauchen, um die Beschränkungen für gerahmte Inhalte zu verschärfen. Wir können den Browser anweisen, den Inhalt eines bestimmten Frames in einer Umgebung mit geringen Berechtigungen zu laden, wobei nur die Teilmenge der Funktionen zugelassen wird, die für die Ausführung der erforderlichen Arbeiten erforderlich sind.

Twust, aber überprüfen.

Die Schaltfläche „Tweet“ von Twitter ist ein hervorragendes Beispiel für Funktionen, die über eine Sandbox sicherer in Ihre Website eingebettet werden können. Mit Twitter können Sie den Button über einen Iframe mit folgendem Code einbetten :

<iframe src="https://platform.twitter.com/widgets/tweet_button.html"></iframe>

Um herauszufinden, was wir sperren können, untersuchen wir sorgfältig, welche Funktionen die Schaltfläche benötigt. Der in den Frame geladene HTML-Code führt ein wenig JavaScript von den Servern von Twitter aus und generiert beim Klicken ein Popup mit einer Tweeting-Oberfläche. Diese Schnittstelle benötigt Zugriff auf die Cookies von Twitter, um den Tweet mit dem richtigen Konto zu verknüpfen, und muss das Tweeting-Formular senden können. Das wars so ziemlich; Der Frame muss keine Plugins laden, er muss nicht durch das Fenster der obersten Ebene oder eine Reihe anderer Funktionen navigieren. Da diese Berechtigungen nicht erforderlich sind, entfernen wir sie, indem wir den Inhalt des Frames sandboxen.

Sandboxing funktioniert auf Basis einer Whitelist. Wir entfernen zunächst alle möglichen Berechtigungen und aktivieren dann die einzelnen Funktionen wieder, indem wir der Sandbox-Konfiguration bestimmte Flags hinzufügen. Für das Twitter-Widget haben wir uns entschieden, JavaScript, Popups, Formularübermittlung und die Cookies von twitter.com zu aktivieren. Wir können dies tun, indem wir dem ein sandboxAttribut iframemit dem folgenden Wert hinzufügen :


<iframe sandbox="allow-same-origin allow-scripts allow-popups allow-forms" src="https://platform.twitter.com/widgets/tweet_button.html"></iframe>

Das ist es. Wir haben dem Frame alle erforderlichen Funktionen zugewiesen, und der Browser verweigert ihm den Zugriff auf alle Berechtigungen, die wir ihm nicht explizit über den sandboxWert des Attributs gewährt haben.

Granulare Kontrolle über Funktionen

Wir haben im obigen Beispiel einige der möglichen Sandbox-Flags gesehen. Lassen Sie uns nun das Innenleben des Attributs etwas genauer untersuchen.

Bei einem Iframe mit einem leeren Sandbox-Attribut ( <iframe sandbox src="..."> </iframe>) wird das gerahmte Dokument vollständig in einer Sandbox gespeichert, wobei die folgenden Einschränkungen gelten:

Dies ist schön drakonisch, und ein Dokument, das in eine Sandbox geladen wird, iframe birgt in der Tat nur ein sehr geringes Risiko. Natürlich kann es auch nicht viel wert sein: Möglicherweise können Sie mit einer vollständigen Sandbox für statische Inhalte davonkommen, aber die meiste Zeit möchten Sie die Dinge etwas lockern.

Mit Ausnahme von Plugins kann jede dieser Einschränkungen aufgehoben werden, indem dem Wert des Sandbox-Attributs ein Flag hinzugefügt wird. Sandbox-Dokumente können niemals Plugins ausführen, da Plugins nativer Code ohne Sandbox sind, aber alles andere ist faires Spiel:

Vor diesem Hintergrund können wir genau bewerten, warum wir im obigen Twitter-Beispiel die spezifischen Sandbox-Flags erhalten haben:

Es ist wichtig zu beachten, dass die auf einen Frame angewendeten Sandbox-Flags auch für alle in der Sandbox erstellten Fenster oder Frames gelten. Dies bedeutet, dass wir allow-formsdie Sandbox des Frames erweitern müssen, obwohl das Formular nur in dem Fenster vorhanden ist, in dem der Frame angezeigt wird.

Wenn das sandboxAttribut vorhanden ist, erhält das Widget nur die erforderlichen Berechtigungen, und Funktionen wie Plugins, Top-Navigation und Zeigersperre bleiben blockiert. Wir haben das Risiko der Einbettung des Widgets ohne negative Auswirkungen verringert. Es ist ein Gewinn für alle Beteiligten.

Privilegientrennung

Das Sandboxen von Inhalten von Drittanbietern, um ihren nicht vertrauenswürdigen Code in einer Umgebung mit geringen Berechtigungen auszuführen, ist offensichtlich von Vorteil. Aber was ist mit deinem eigenen Code? Du vertraust dir selbst, richtig? Warum also über Sandboxen nachdenken?

Ich würde diese Frage umdrehen: Wenn Ihr Code keine Plugins benötigt, warum sollten Sie ihm Zugriff auf Plugins gewähren? Im besten Fall ist es ein Privileg, das Sie niemals nutzen, im schlimmsten Fall ist es ein potenzieller Vektor für Angreifer, einen Fuß in die Tür zu bekommen. Jeder Code weist Fehler auf, und praktisch jede Anwendung ist auf die eine oder andere Weise für die Ausnutzung anfällig. Das Sandboxen Ihres eigenen Codes bedeutet, dass ein Angreifer, selbst wenn er Ihre Anwendung erfolgreich untergräbt, keinen vollständigen Zugriff auf den Ursprung der Anwendung erhält . Sie können nur Dinge tun, die die Anwendung tun kann. Immer noch schlecht, aber nicht so schlecht wie es sein könnte.

Sie können das Risiko noch weiter reduzieren, indem Sie Ihre Anwendung in logische Teile aufteilen und jedes Teil mit möglichst geringen Berechtigungen sandboxen. Diese Technik ist im nativen Code sehr verbreitet: Chrome zerfällt beispielsweise in einen Browserprozess mit hohen Berechtigungen, der Zugriff auf die lokale Festplatte hat und Netzwerkverbindungen herstellen kann, sowie in viele Rendererprozesse mit niedrigen Berechtigungen, die das schwere Heben übernehmen nicht vertrauenswürdige Inhalte zu analysieren. Renderer müssen die Festplatte nicht berühren, der Browser sorgt dafür, dass sie alle Informationen erhalten, die sie zum Rendern einer Seite benötigen. Selbst wenn eine clevere Hackerin einen Weg findet, einen Renderer zu beschädigen, ist sie nicht weit gekommen, da der Renderer allein nicht viel Interessantes tun kann: Jeder Zugriff mit hohen Berechtigungen muss über den Prozess des Browsers geleitet werden.

Sicheres Sandboxeneval()

Mit Sandboxing und der postMessageAPI lässt sich der Erfolg dieses Modells relativ einfach auf das Web übertragen. Teile Ihrer Anwendung können in Sandboxen iframegespeichert sein, und das übergeordnete Dokument kann die Kommunikation zwischen ihnen vermitteln, indem Nachrichten gesendet und auf Antworten gewartet werden. Diese Art von Struktur stellt sicher, dass Exploits in einem Teil der App den minimal möglichen Schaden verursachen. Es hat auch den Vorteil, dass Sie gezwungen sind, klare Integrationspunkte zu erstellen, sodass Sie genau wissen, wo Sie bei der Validierung von Ein- und Ausgaben vorsichtig sein müssen. Lassen Sie uns ein Spielzeugbeispiel durchgehen, um zu sehen, wie das funktionieren könnte.

Evalbox ist eine aufregende Anwendung, die eine Zeichenfolge als JavaScript auswertet. Wow, richtig? Genau das, worauf Sie all die langen Jahre gewartet haben. Es ist natürlich eine ziemlich gefährliche Anwendung, da die Ausführung von beliebigem JavaScript bedeutet, dass alle Daten, die ein Ursprung zu bieten hat, zu gewinnen sind. Wir verringern das Risiko, dass Bad Things ™ auftritt, indem wir sicherstellen, dass der Code in einer Sandbox ausgeführt wird, was ihn wesentlich sicherer macht. Wir arbeiten uns von innen nach außen durch den Code, beginnend mit dem Inhalt des Frames:

   <title>Evalbox's Frame</title>
   <script>
     window.addEventListener('message', function (e) {
       var mainWindow = e.source;
       var result = '';
       try {
         result = eval(e.data);
       } catch (e) {
         result = 'eval() threw an exception.';
       }
       mainWindow.postMessage(result, event.origin);
     });
   </script>

Innerhalb des Frames haben wir ein minimales Dokument, das einfach auf Nachrichten von seinem übergeordneten Dokument wartet, indem es sich in das messageEreignis des windowObjekts einhakt. Immer wenn das übergeordnete Element postMessage für den Inhalt des Iframes ausführt, wird dieses Ereignis ausgelöst und gibt uns Zugriff auf die Zeichenfolge, die unser übergeordnetes Element ausführen soll.

Im Handler greifen wir auf das sourceAttribut des Ereignisses zu, das das übergeordnete Fenster ist. Wir werden dies verwenden, um das Ergebnis unserer harten Arbeit wieder zu senden, sobald wir fertig sind. Dann machen wir das schwere Heben, indem wir die Daten weitergeben, die uns gegeben wurden eval(). Dieser Aufruf wurde in einem Try-Block zusammengefasst, da gesperrte Vorgänge in einer Sandbox iframehäufig DOM-Ausnahmen generieren. Wir werden diese abfangen und stattdessen eine freundliche Fehlermeldung melden. Schließlich veröffentlichen wir das Ergebnis wieder im übergeordneten Fenster. Das ist ziemlich einfach.

Der Elternteil ist ähnlich unkompliziert. Wir werden eine winzige Benutzeroberfläche mit einem textarea for-Code und einer buttonfor-Ausführung erstellen und frame.htmlüber eine Sandbox zugreifen iframe, die nur die Ausführung von Skripten zulässt:

<textarea id="code"></textarea>
<button id="safe">eval() in a sandboxed frame.</button>
<iframe sandbox="allow-scripts" id="sandboxed" src="frame.html"></iframe>

Jetzt verkabeln wir die Dinge für die Ausführung. Zuerst werden wir auf Antworten von iframeund alert()für unsere Benutzer warten. Vermutlich würde eine echte Anwendung etwas weniger ärgerliches bewirken:

window.addEventListener('message',
    function (e) {
      // Sandboxed iframes which lack the 'allow-same-origin'
      // header have "null" rather than a valid origin. This means you still
      // have to be careful about accepting data via the messaging API you
      // create. Check that source, and validate those inputs!
      var frame = document.getElementById('sandboxed');
      if (e.origin === "null" && e.source === frame.contentWindow)
        alert('Result: ' + e.data);
    });

Als Nächstes schließen wir einen Ereignishandler an, um auf zu klicken button. Wenn der Benutzer klickt, greifen wir auf den aktuellen Inhalt von zu textareaund übergeben ihn zur Ausführung an den Frame:

function evaluate() {
  var frame = document.getElementById('sandboxed');
  var code = document.getElementById('code').value;
  frame.contentWindow.postMessage(code, '*');
}
document.getElementById('safe').addEventListener('click', evaluate);

Einfach richtig? Wir haben eine sehr einfache Evaluierungs-API erstellt und können sicher sein, dass der evaluierte Code keinen Zugriff auf vertrauliche Informationen wie Cookies oder DOM-Speicher hat. Ebenso kann ausgewerteter Code keine Plugins laden, keine neuen Fenster öffnen oder eine Reihe anderer lästiger oder böswilliger Aktivitäten ausführen.

Überzeugen Sie sich selbst vom Code:

Evalbox Demo

Sie können dasselbe für Ihren eigenen Code tun, indem Sie monolithische Anwendungen in Einzweckkomponenten aufteilen. Jedes kann in eine einfache Messaging-API eingebunden werden, genau wie oben beschrieben. Das übergeordnete Fenster mit hohen Berechtigungen kann als Controller und Dispatcher fungieren und Nachrichten an bestimmte Module senden, die jeweils die geringstmöglichen Berechtigungen für ihre Arbeit haben, auf Ergebnisse warten und sicherstellen, dass jedes Modul nur mit den erforderlichen Informationen versorgt wird .

Beachten Sie jedoch, dass Sie beim Umgang mit gerahmten Inhalten, die aus derselben Herkunft stammen wie die übergeordneten Inhalte, sehr vorsichtig sein müssen. Wenn eine Seite auf https://example.com/Frames eine andere Seite auf dem gleichen Ursprung mit einer Sandbox , die sowohl die enthält allow-same-originund allow-scriptsFahnen, dann kann die eingerahmte Seite reichen bis in die Eltern auf, und entfernen Sie die Sandbox - Attribut vollständig.

Spielen Sie in Ihrem Sandkasten.

Sandboxing steht Ihnen jetzt in einer Vielzahl von Browsern zur Verfügung: Firefox 17+, IE10 + und Chrome zum Zeitpunkt des Schreibens ( caniuse verfügt natürlich über eine aktuelle Support-Tabelle ). Durch Anwenden des sandbox Attributs auf iframesIhr Include können Sie dem angezeigten Inhalt bestimmte Berechtigungen erteilen, nur die Berechtigungen, die für die ordnungsgemäße Funktion des Inhalts erforderlich sind. Dies gibt Ihnen die Möglichkeit, das mit der Aufnahme von Inhalten Dritter verbundene Risiko zu verringern, und zwar über das hinaus, was mit den Richtlinien zur Inhaltssicherheit bereits möglich ist .

Darüber hinaus ist Sandboxing eine leistungsstarke Technik, um das Risiko zu verringern, dass ein cleverer Angreifer Lücken in Ihrem eigenen Code ausnutzt. Durch die Aufteilung einer monolithischen Anwendung in eine Reihe von Sandbox-Diensten, die jeweils für einen kleinen Teil der in sich geschlossenen Funktionen verantwortlich sind, müssen Angreifer nicht nur den Inhalt bestimmter Frames, sondern auch deren Controller gefährden. Dies ist eine viel schwierigere Aufgabe, zumal der Umfang des Controllers stark reduziert werden kann. Sie können Ihre sicherheitsrelevanten Anstrengungen damit verbringen , diesen Code zu überprüfen , wenn Sie den Browser um Hilfe bitten.

Das heißt nicht, dass Sandboxing eine vollständige Lösung für das Sicherheitsproblem im Internet ist. Es bietet umfassende Verteidigung, und wenn Sie nicht die Kontrolle über die Clients Ihrer Benutzer haben, können Sie sich noch nicht auf die Browserunterstützung für alle Ihre Benutzer verlassen (wenn Sie die Clients Ihrer Benutzer kontrollieren - beispielsweise eine Unternehmensumgebung - Hurra!). Eines Tages… aber im Moment ist Sandboxing eine weitere Schutzschicht, um Ihre Abwehrkräfte zu stärken. Es ist keine vollständige Verteidigung, auf die Sie sich allein verlassen können. Trotzdem sind die Schichten ausgezeichnet. Ich schlage vor, diesen zu nutzen.

Weiterführende Literatur

" Privilegientrennung in HTML5-Anwendungen " ist ein interessantes Dokument, das sich mit dem Entwurf eines kleinen Frameworks und seiner Anwendung auf drei vorhandene HTML5-Apps befasst.

Sandboxing kann noch flexibler sein, wenn es mit zwei anderen neuen Iframe-Attributen kombiniert wird:, srcdocund seamless. Ersteres ermöglicht es Ihnen, einen Frame ohne den Aufwand einer HTTP-Anforderung mit Inhalten zu füllen, und letzteres ermöglicht es, dass Stil in den gerahmten Inhalt fließt. Beide haben im Moment eine ziemlich miserable Browser-Unterstützung (Chrome- und WebKit-Nightlies). wird aber in Zukunft eine interessante Kombination sein. Sie können beispielsweise Sandbox-Kommentare zu einem Artikel über den folgenden Code erstellen:

<iframe sandbox="" seamless="" srcdoc="

This is a user's comment! It can't execute script! Hooray for safety!

"></iframe>
<h3>Ergebniss</h3> <iframe sandbox="" seamless="" srcdoc="

This is a user's comment! It can't execute script! Hooray for safety!

"></iframe>

Deprecated: Directive 'allow_url_include' is deprecated in Unknown on line 0