Créer une expérience enrichie sur le Web actuel implique presque inévitablement des composants de représentation vectorielle continue et du contenu sur lesquels vous n'avez aucun contrôle réel. Les widgets tiers peuvent stimuler l'engagement et jouer un rôle essentiel dans l'expérience utilisateur, et le contenu généré par l'utilisateur est parfois encore plus important que le contenu natif d'un site. Abstention de l’une ou l’autre n’est pas vraiment une option, mais augmentent le risque que quelque chose de incorrectTM se produise sur votre site. Chaque que vous intégrez (chaque annonce, chaque widget de réseau social) vecteur d'attaque pour ceux dont l'intention est malveillante:
Content Security Policy (CSP) peut atténuer les risques associés à ces deux types de contenus la possibilité d'ajouter à la liste blanche des sources de script et d'autres contenus. Il s'agit d'une étape importante dans la bonne direction, mais il est important de noter que la protection offerte par la plupart des directives CSP est binaire: la ressource est autorisée ou non. Parfois, il serait utile de dire « Je ne suis pas je fait confiance à cette source de contenu, mais c'est très joli ! Intégrer s'il te plaît, mais ne le laisse pas endommager mon site. »
moindre privilège
Concrètement, nous cherchons un moyen d'accorder aux contenus n'intégrer que le niveau de capacité minimum nécessaire pour accomplir son travail. Si un widget n'a pas besoin d'ouvrir une nouvelle fenêtre, ce qui supprime l'accès à window.open blessée. Si Flash n'est pas nécessaire, la désactivation de la prise en charge des plug-ins problème. Si nous suivons le principe du moins possible, nous assurons une sécurité optimale. le droit d'accès, et de bloquer chaque fonctionnalité qui n'est pas directement pertinente par rapport à celle que nous aimerions utiliser. Résultat : nous n'avons plus besoin de croire aveuglément qu'un élément du contenu intégré ne bénéficiera pas des privilèges qu'il ne devrait pas utiliser. Il n'aura tout simplement pas accès à la fonctionnalité en premier lieu.
Les éléments iframe
constituent la première étape d'élaboration d'un framework adapté à ce type de solution.
Le chargement d'un composant non approuvé dans une iframe
permet de mesurer la séparation
entre votre application et le contenu à charger. Contenu encadré
n'aura pas accès au DOM de votre page ni aux données que vous avez stockées localement.
pouvoir dessiner à des positions arbitraires sur la page ; sa portée est limitée
contour du cadre. Cependant, la séparation n'est pas vraiment solide. La page contenue
propose tout de même un certain nombre d'options en cas de comportement agaçant ou malveillant: la lecture automatique
les vidéos, les plug-ins et les pop-ups sont la partie visible de l'iceberg.
Attribut sandbox
de l'élément iframe
nous donne exactement ce dont nous avons besoin pour
resserrer les restrictions sur le contenu encadré. Nous pouvons
demander au navigateur de charger le contenu d'un frame spécifique avec un accès
en n'autorisant que le sous-ensemble des capacités nécessaires pour faire quoi que ce soit
le travail doit faire.
Faire un petit test, mais vérifier
"Tweet" de Twitter est un excellent exemple de fonctionnalité pouvant être de façon sécurisée dans votre site via un bac à sable. Twitter vous permet d'intégrer via un iFrame par le code suivant:
<iframe src="https://platform.twitter.com/widgets/tweet_button.html"
style="border: 0; width:130px; height:20px;"></iframe>
Pour déterminer les éléments à verrouiller, examinons attentivement les capacités requis par le bouton. Le code HTML chargé dans le frame exécute un peu de à partir des serveurs Twitter et génère une fenêtre pop-up contenant l'interface de tweeting. Cette interface a besoin d'accéder au compte Twitter des cookies pour associer le tweet au compte approprié et doit pouvoir pour envoyer le formulaire pour tweeter. C'est à peu près tout ; le cadre n'a pas besoin de de plug-ins, il n'a pas besoin de naviguer dans la fenêtre de premier niveau ni le nombre d'autres bits de fonctionnalité. Puisqu'il n'a pas besoin de ces privilèges, supprimons-les en bac à sable au contenu du frame.
Le bac à sable fonctionne sur la base d'une liste blanche. Nous commençons par supprimer
les autorisations possibles, puis réactivez les fonctionnalités individuelles en ajoutant
des indicateurs spécifiques à la configuration du bac à sable. Pour le widget Twitter, nous avons
ont décidé d'activer JavaScript, les pop-ups, l'envoi de formulaires et l'utilisation
les cookies. Pour ce faire, ajoutez un attribut sandbox
à iframe
avec le paramètre
la valeur suivante:
<iframe sandbox="allow-same-origin allow-scripts allow-popups allow-forms"
src="https://platform.twitter.com/widgets/tweet_button.html"
style="border: 0; width:130px; height:20px;"></iframe>
Et voilà ! Nous avons fourni au cadre toutes
les capacités dont il a besoin, et
le navigateur lui refusera l'accès aux privilèges que nous n'avons pas
l'accordez explicitement via la valeur de l'attribut sandbox
.
Contrôle précis des fonctionnalités
Dans l'exemple ci-dessus, nous avons vu quelques-uns des indicateurs de bac à sable possibles. explorer les rouages internes de l'attribut un peu plus en détail.
Dans le cas d'un iFrame avec un attribut de bac à sable vide, le document encadré sera entièrement en bac à sable, auquel cas il sera soumis aux restrictions suivantes:
- JavaScript ne s'exécutera pas dans le document encadré. Cela inclut non seulement JavaScript explicitement chargé via des tags de script, mais également des gestionnaires d'événements intégrés et les URL javascript:. Cela signifie également que le contenu contenu dans des tags noscript s'affiche exactement comme si l'utilisateur avait désactivé le script lui-même.
- Le document encadré est chargé dans une origine unique, ce qui signifie que tous les vérifications de l'origine commune échouent. des origines uniques ne correspondent jamais à d'autres origines, même eux-mêmes. Cela signifie, entre autres, que le document ne comporte aucun l'accès aux données stockées dans les cookies d'une origine quelconque ou tout autre mécanisme de stockage ; (stockage DOM, base de données indexée, etc.).
- Le document encadré ne peut pas créer de fenêtres ni de boîtes de dialogue (via
window.open
outarget="_blank"
, par exemple). - Impossible d'envoyer des formulaires.
- Les plug-ins ne se chargent pas.
- Le document encadré ne peut naviguer que dans lui-même, et non dans son parent de premier niveau.
Si vous définissez
window.top.location
, une exception est générée, et un clic sur le lien avectarget="_top"
n'aura aucun effet. - Fonctionnalités qui se déclenchent automatiquement (éléments de formulaire sélectionnés automatiquement, lecture automatique) vidéos, etc.) sont bloquées.
- Impossible d'activer le verrouillage du pointeur.
- L'attribut
seamless
est ignoré dans l'élémentiframes
du document encadré.
C'est plutôt draconien : un document est chargé dans un iframe
entièrement exécuté en bac à sable.
présente en effet très peu de risques. Bien sûr, elle ne peut pas non plus être très utile: vous
peut s'en sortir avec un bac à sable complet pour du contenu statique, mais la plupart
vous devrez parfois
vous détendre un peu.
À l'exception des plug-ins, chacune de ces restrictions peut être levée en ajoutant un indicateur à la valeur de l'attribut sandbox. Les documents en bac à sable ne peuvent jamais exécuter des plug-ins, car les plug-ins sont du code natif sans bac à sable, mais tout le reste reste correct. jeu:
allow-forms
permet l'envoi de formulaires.allow-popups
autorise les pop-ups (c'est un choc !).allow-pointer-lock
permet (surprise !) de verrouiller le pointeur.allow-same-origin
permet au document de conserver son origine. pages chargées dehttps://example.com/
conservera l'accès aux données de cette origine.allow-scripts
permet l'exécution JavaScript et permet également aux fonctionnalités de se déclenchent automatiquement (car leur implémentation est simple via JavaScript).allow-top-navigation
permet au document de sortir du cadre en en naviguant dans la fenêtre racine.
En gardant cela à l'esprit, nous pouvons évaluer exactement pourquoi nous avons obtenu ensemble d'indicateurs de bac à sable dans l'exemple Twitter ci-dessus:
allow-scripts
est obligatoire, car la page chargée dans le frame exécute une du code JavaScript pour gérer les interactions des utilisateurs.allow-popups
est obligatoire, car le bouton fait apparaître un formulaire de tweet dans une nouvelle fenêtre.- Le champ
allow-forms
est obligatoire, car le formulaire pour tweeter doit pouvoir être envoyé. allow-same-origin
est nécessaire, car les cookies de twitter.com le seraient sinon être inaccessibles et l'utilisateur n'a pas pu se connecter pour publier le formulaire.
Il est important de noter que les indicateurs
de bac à sable appliqués à un frame
appliquer à toutes les fenêtres ou
cadres créés dans le bac à sable. Cela signifie que nous avons
d'ajouter allow-forms
au bac à sable du frame, même si le formulaire n'existe
dans la fenêtre que
le cadre apparaît.
Une fois l'attribut sandbox
en place, le widget n'obtient que les autorisations dont il
nécessaires, et des fonctionnalités telles que les plug-ins, la navigation supérieure et le verrouillage du pointeur restent
bloqués. Nous avons réduit le risque d'intégration du widget, sans aucun effet négatif.
Tout le monde y gagne.
Séparation des privilèges
Ils exécutent des contenus tiers dans un système de bac à sable afin d'exécuter leur code non approuvé dans un un environnement à faible privilège est de toute évidence bénéfique. Mais qu'en est-il votre propre code ? Tu as confiance en soi, n'est-ce pas ? Pourquoi se soucier du bac à sable ?
Je changerais cette question: si votre code n'a pas besoin de plug-ins, pourquoi en donner l'accès aux plug-ins ? Au mieux, c'est un privilège que vous n'utilisez jamais, au pire, vecteur potentiel pour les attaquants d’avoir un pied dans la porte. Le code de chacun a de bugs. De plus, pratiquement toutes les applications sont vulnérables, d'une certaine façon à l'exploitation. ou une autre. Avec le bac à sable de votre propre code, même si un attaquant réussit perturbe votre application, celui-ci ne dispose pas d'un accès complet l'origine de l'application ; il ne pourra faire que ce que l'application pourrait faire. C'était mauvais, mais pas aussi mauvais qu'il pourrait l'être.
Vous pouvez réduire encore davantage le risque en divisant votre application en éléments logiques et en bac à sable chaque élément avec le minimum de privilèges possible. Cette technique est très courante dans le code natif: Chrome, par exemple, se brise tout seul. dans un processus de navigateur à privilèges élevés ayant accès au disque dur local et peuvent établir des connexions réseau, ainsi que de nombreux processus de moteur de rendu à faible privilège qui faire le gros du travail en analysant du contenu non fiable. Les moteurs de rendu n'ont pas besoin de toucher le disque, le navigateur s'occupe de leur donner toutes les informations dont ils ont besoin pour pour afficher une page. Même si un pirate informatique intelligent trouve un moyen de corrompre un moteur de rendu, n'est pas allé très loin, car le moteur de rendu ne peut pas générer beaucoup d'intérêt tout seul: tous les accès à privilèges élevés doivent être acheminés via le processus du navigateur. Les attaquants devront trouver plusieurs trous dans différentes parties du système afin de causer des dégâts, ce qui réduit considérablement le risque de pwn du produit.
Mise en bac à sable sécurisée de eval()
Grâce au système de bac à sable et
L'API postMessage
, la
la réussite de ce modèle est assez simple à appliquer au Web. Des morceaux de
votre application peut résider dans des iframe
en bac à sable et le document parent peut
pour établir la communication entre eux en publiant des messages et en écoutant
des réponses. Ce type de structure garantit que les exploits
l'application ne doit pas causer de dommages. Cela a aussi l'avantage de vous forcer
créer des points d'intégration clairs, de sorte que vous sachiez exactement où vous devez être
veillez à valider les entrées et les sorties. Prenons l'exemple d'un jouet,
juste pour voir comment
cela pourrait fonctionner.
Evalbox est une application très intéressante qui prend une chaîne et l'évalue en tant que JavaScript. Ouah, n’est-ce pas ? Juste ce que que vous attendiez depuis toutes ces longues années. C'est un jeu d'enfant application. En effet, autoriser l'exécution de JavaScript arbitraire implique et toutes les données qu'une origine offre sont prêtes à être récupérées. Nous allons atténuer le risque de Bad ThingsTM se produisant en s'assurant que le code est exécuté dans un bac à sable ce qui le rend beaucoup plus sûr. Nous allons parcourir le code à l'envers, en commençant par le contenu du cadre:
<!-- frame.html -->
<!DOCTYPE html>
<html>
<head>
<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>
</head>
</html>
À l'intérieur du cadre, nous avons un document minimal qui écoute simplement les messages
de son parent en vous connectant à l'événement message
de l'objet window
.
Chaque fois que le parent exécute postMessage sur le contenu de l'iFrame, cet événement
se déclenchera, ce qui nous donnera accès à la chaîne
exécuter.
Dans le gestionnaire, nous récupérons l'attribut source
de l'événement, qui est le parent.
fenêtre. Nous l'utiliserons pour renvoyer le résultat
de notre dur labeur une fois que nous aurons
terminé. Ensuite, nous ferons le plus gros du travail,
en transmettant les données qui nous ont été données
eval()
Cet appel a été encapsulé dans un bloc try, car il s'agit d'opérations interdites
dans un iframe
en bac à sable génère fréquemment des exceptions DOM. nous allons attraper
et signalez plutôt un message d'erreur. Enfin, nous publions le résultat
à la fenêtre parent. C’est quelque chose
assez simple.
Le parent est tout aussi simple. Nous allons créer une petite UI avec un textarea
.
pour le code et un button
pour l'exécution. Nous allons extraire frame.html
via un
iframe
en bac à sable, autorisant uniquement l'exécution de script:
<textarea id='code'></textarea>
<button id='safe'>eval() in a sandboxed frame.</button>
<iframe sandbox='allow-scripts'
id='sandboxed'
src='frame.html'></iframe>
Maintenant, nous allons connecter
les choses pour l'exécution. Tout d'abord, nous écouterons
les réponses de
les iframe
et les alert()
à nos utilisateurs. Il s'agit vraisemblablement d'une véritable application
ferait quelque chose
de moins agaçant:
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);
});
Nous allons maintenant connecter un gestionnaire d'événements aux clics sur button
. Lorsque l'utilisateur
nous récupérons le contenu actuel de l'élément textarea
et nous le transmettons
frame pour l'exécution:
function evaluate() {
var frame = document.getElementById('sandboxed');
var code = document.getElementById('code').value;
// Note that we're sending the message to "*", rather than some specific
// origin. Sandboxed iframes which lack the 'allow-same-origin' header
// don't have an origin which you can target: you'll have to send to any
// origin, which might alow some esoteric attacks. Validate your output!
frame.contentWindow.postMessage(code, '*');
}
document.getElementById('safe').addEventListener('click', evaluate);
Facile, n'est-ce pas ? Nous avons créé une API d'évaluation très simple, et nous pouvons être sûrs le code évalué n'a pas accès à des informations sensibles telles que des cookies ou DOM. De même, le code évalué ne peut pas charger de plug-ins, afficher de nouvelles fenêtres, ou toute autre activité agaçante ou malveillante.
Vous pouvez faire de même pour votre propre code en décomposant les applications monolithiques en composants à application unique. Chacun d'eux peut être encapsulé dans une API de messagerie simple, comme ce que nous avons écrit ci-dessus. La fenêtre parent à privilèges élevés peut faire office contrôleur et coordinateur, en envoyant des messages dans des modules spécifiques qui ont chacun le moins de droits possible pour faire leur travail, en écoutant les résultats et en veillant à ce que chaque module soit alimenté uniquement avec les informations dont il a besoin.
Notez toutefois que vous devez faire preuve d'une grande prudence lorsque vous traitez du contenu encadré
ayant la même origine que le parent. Si une page sur
https://example.com/
encadre une autre page de la même origine avec un bac à sable.
qui inclut les options allow-same-origin et allow-scripts, puis
la page encadrée peut atteindre le parent et supprimer l'attribut "sandbox"
entièrement.
Jouez dans votre bac à sable
Le système de bac à sable est désormais disponible dans différents navigateurs: Firefox 17+,
Internet Explorer 10+ et Google Chrome (caniuse, bien sûr, dispose d'une version
tableau d'assistance). Application de sandbox
à iframes
que vous incluez vous permet d'accorder certains privilèges au
du contenu qu'ils affichent, uniquement les droits nécessaires
de votre contenu pour qu'il fonctionne correctement. Cela vous donne la possibilité de réduire le risque
associées à l'inclusion de contenu tiers, en plus de ce qui est
déjà possible avec Content Security
Règlement.
De plus, le bac à sable est une technique efficace pour réduire le risque attaquant sera en mesure d’exploiter les trous dans votre propre code. En séparant un une application monolithique en un ensemble de services en bac à sable, chacun responsable petit fragment de fonctionnalité autonome, les attaquants seront obligés de ne pas ne compromettre que des frames spécifiques contenu, mais aussi leur contrôleur. Il s'agit d'un une tâche beaucoup plus difficile, d'autant plus que le contrôleur peut être considérablement réduit dans le champ d'application. Vous pouvez consacrer vos efforts liés à la sécurité à ce code si vous demandez au navigateur de vous aider pour le reste.
Cela ne veut pas dire que le bac à sable est une solution complète au problème de la sécurité sur Internet. Il offre une défense en profondeur et, à moins que vous n'ayez de contrôler l'expérience utilisateur vous ne pouvez pas encore compter sur la prise en charge des navigateurs vos utilisateurs (si vous contrôlez vos utilisateurs, dans un environnement d'entreprise, par exemple, hourrah !). Un jour... Mais pour l'instant, le bac à sable est une autre couche une protection renforcée pour renforcer vos défenses, mais il ne s'agit pas d'une défense complète, vous pouvez vous fier uniquement. Pourtant, les couches sont excellentes. Je vous suggère d'utiliser 1.
Documentation complémentaire
"Séparation des privilèges dans les applications HTML5" est un article intéressant qui traite de la conception d'un petit cadre, et son application à trois applications HTML5.
Le système de bac à sable est encore plus flexible lorsqu'il est associé à deux autres nouveaux cadres iFrame. attributs:
srcdoc
, etseamless
. Le premier vous permet de remplir un cadre avec du contenu sans la surcharge de une requête HTTP, et cette dernière permet au style de circuler dans le contenu encadré. La prise en charge des navigateurs est assez mauvaise pour le moment (Chrome et WebKit nuisibles). mais ce sera une combinaison intéressante à l'avenir. Vous pourriez, Par exemple, vous pouvez ajouter des commentaires au bac à sable sur un article via le code suivant:<iframe sandbox seamless srcdoc="<p>This is a user's comment! It can't execute script! Hooray for safety!</p>"></iframe>