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.
iframe
Elemente sind der erste Schritt in Richtung eines guten Rahmens für eine solche Lösung. Das Laden einer nicht vertrauenswürdigen
Komponente in eine iframe
bietet 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
sandbox
Attribut des iframe
Elements
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 sandbox
Attribut iframe
mit 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 sandbox
Wert 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:
- JavaScript wird im gerahmten Dokument nicht ausgeführt. Dies umfasst nicht nur JavaScript, das explizit über Skript-Tags geladen wird, sondern auch Inline-Ereignishandler und Javascript: URLs. Dies bedeutet auch, dass Inhalte, die in Noscript-Tags enthalten sind, genau so angezeigt werden, als hätte der Benutzer das Skript selbst deaktiviert.
- Das gerahmte Dokument wird in einen eindeutigen Ursprung geladen. Dies bedeutet, dass alle Überprüfungen mit demselben Ursprung fehlschlagen. Einzigartige Ursprünge entsprechen keinem anderen Ursprung, nicht einmal sich selbst. Dies bedeutet unter anderem, dass das Dokument keinen Zugriff auf Daten hat, die in Cookies eines Ursprungs oder anderen Speichermechanismen (DOM-Speicher, indizierte Datenbank usw.) gespeichert sind.
- Das gerahmte Dokument kann keine neuen Fenster oder Dialoge erstellen ( z. B. über
window.open
odertarget="_blank"
). - Formulare können nicht eingereicht werden.
- Plugins werden nicht geladen.
- Das gerahmte Dokument kann nur selbst navigieren, nicht das übergeordnete Dokument der obersten Ebene. Die Einstellung
window.top. location
löst eine Ausnahme aus, und das Klicken auf den Link mittarget="_top"
hat keine Auswirkung. - Funktionen, die automatisch ausgelöst werden (automatisch fokussierte Formularelemente, automatisch wiedergegebene Videos usw.), werden blockiert.
- Zeigersperre kann nicht erhalten werden.
- Das
seamless
Attribut wird iniframes
dem gerahmten Dokument ignoriert .
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:
allow-forms
ermöglicht das Einreichen von Formularen.allow-popups
ermöglicht Popups (window.open()
,showModalDialog()
,target=”_blank”
, etc.).allow-pointer-lock
erlaubt (Überraschung!) Zeigersperre.allow-same-origin
ermöglicht es dem Dokument, seinen Ursprung beizubehalten; Von geladene Seitenhttps://example.com/
behalten den Zugriff auf die Daten dieses Ursprungs.allow-scripts
Ermöglicht die Ausführung von JavaScript und das automatische Auslösen von Funktionen (da die Implementierung über JavaScript trivial wäre).allow-top-navigation
Ermöglicht es dem Dokument, durch Navigieren im Fenster der obersten Ebene aus dem Rahmen auszubrechen.
Vor diesem Hintergrund können wir genau bewerten, warum wir im obigen Twitter-Beispiel die spezifischen Sandbox-Flags erhalten haben:
allow-scripts
ist erforderlich, da auf der in den Frame geladenen Seite JavaScript ausgeführt wird, um die Benutzerinteraktion zu verarbeiten.allow-popups
ist erforderlich, da die Schaltfläche ein Tweeting-Formular in einem neuen Fenster öffnet.allow-forms
ist erforderlich, da das Tweeting-Formular eingereicht werden kann.allow-same-origin
ist erforderlich, da auf die Cookies von twitter.com sonst nicht zugegriffen wer den kann und der Benutzer sich nicht anmelden kann, um das Formular zu veröffentlichen.
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-forms
die Sandbox des Frames erweitern müssen, obwohl das Formular nur in dem
Fenster vorhanden ist, in dem der Frame angezeigt wird.
Wenn das sandbox
Attribut 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
postMessage
API lässt sich der Erfolg dieses Modells
relativ einfach auf das Web übertragen. Teile Ihrer Anwendung können in Sandboxen iframe
gespeichert 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 message
Ereignis des window
Objekts 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 source
Attribut 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 iframe
hä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 button
for-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 iframe
und 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 textarea
und ü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 DemoSie 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-origin
und allow-scripts
Fahnen, 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 iframes
Ihr 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:,
srcdoc
und
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="<h3>Ergebniss</h3> <iframe sandbox="" seamless="" srcdoc="This is a user's comment! It can't execute script! Hooray for safety!
"></iframe>
This is a user's comment! It can't execute script! Hooray for safety!
"></iframe>