Isolation de sites pour les développeurs Web

Dans Chrome 67 pour ordinateur, une nouvelle fonctionnalité appelée isolation de sites est activée par défaut. Cet article explique en quoi consiste l'isolation de site, pourquoi elle est nécessaire et pourquoi les développeurs Web doivent en tenir compte.

Qu'est-ce que l'isolation de sites ?

Internet est fait pour regarder des vidéos de chats et gérer des portefeuilles de cryptomonnaies, entre autres. Mais vous ne voulez pas que fluffycats.example ait accès à vos précieuses cryptomonnaies. Heureusement, les sites Web ne peuvent généralement pas accéder aux données des autres dans le navigateur grâce à la règle de même origine. Toutefois, des sites Web malveillants peuvent tenter de contourner cette règle pour attaquer d'autres sites Web. De plus, des bugs de sécurité sont parfois détectés dans le code du navigateur qui applique la règle d'origine commune. L'équipe Chrome s'efforce de corriger ces bugs le plus rapidement possible.

L'isolation de site est une fonctionnalité de sécurité de Chrome qui offre une ligne de défense supplémentaire pour réduire la probabilité de réussite de ces attaques. Il garantit que les pages de différents sites Web sont toujours placées dans des processus différents, chacune s'exécutant dans un bac à sable qui limite ce que le processus est autorisé à faire. Il empêche également le processus de recevoir certains types de données sensibles provenant d'autres sites. Par conséquent, avec l'isolation de site, il est beaucoup plus difficile pour un site Web malveillant d'utiliser des attaques par canal auxiliaire spéculatives telles que Spectre pour voler des données à d'autres sites. À mesure que l'équipe Chrome finalise les mesures d'application supplémentaires, l'isolation de site est également utile même si la page d'un pirate informatique peut enfreindre certaines des règles dans son propre processus.

L'isolation de site complique la tâche des sites Web non approuvés qui tentent d'accéder à des informations de comptes sur d'autres sites Web ou de les voler. Il offre une protection supplémentaire contre divers types de bugs de sécurité, tels que les attaques par canal auxiliaire Meltdown et Spectre récentes.

Pour en savoir plus sur l'isolation de sites, consultez notre article sur le blog Google sur la sécurité.

Cross-Origin Read Blocking

Même lorsque toutes les pages intersites sont placées dans des processus distincts, les pages peuvent toujours demander légitimement des sous-ressources intersites, telles que des images et du code JavaScript. Une page Web malveillante peut utiliser un élément <img> pour charger un fichier JSON contenant des données sensibles, telles que votre solde bancaire:

<img src="https://your-bank.example/balance.json" />
<!-- Note: the attacker refused to add an `alt` attribute, for extra evil points. -->

Sans isolation de sites, le contenu du fichier JSON est stocké dans la mémoire du processus du moteur de rendu. Celui-ci remarque alors qu'il ne s'agit pas d'un format d'image valide et n'affiche pas d'image. Toutefois, le pirate informatique peut ensuite exploiter une faille telle que Spectre pour lire potentiellement ce bloc de mémoire.

Au lieu d'utiliser <img>, le pirate informatique pourrait également utiliser <script> pour valider les données sensibles en mémoire:

<script src="https://your-bank.example/balance.json"></script>

Le blocage de la lecture inter-origine (CORB, Cross-Origin Read Blocking) est une nouvelle fonctionnalité de sécurité qui empêche le contenu de balance.json d'entrer dans la mémoire du processus de rendu en fonction de son type MIME.

Voyons comment fonctionne le CORB. Un site Web peut demander deux types de ressources à un serveur:

  1. ressources de données telles que des documents HTML, XML ou JSON
  2. ressources multimédias telles que des images, du code JavaScript, du code CSS ou des polices

Un site Web peut recevoir des ressources de données à partir de sa propre origine ou d'autres origines avec des en-têtes CORS permissifs tels que Access-Control-Allow-Origin: *. D'autre part, les ressources multimédias peuvent être incluses depuis n'importe quelle origine, même sans en-têtes CORS permissifs.

CORB empêche le processus de rendu de recevoir une ressource de données multi-origines (HTML, XML ou JSON) si:

  • La ressource comporte un en-tête X-Content-Type-Options: nosniff.
  • CORS n'autorise pas explicitement l'accès à la ressource

Si l'en-tête X-Content-Type-Options: nosniff de la ressource de données multi-origines n'est pas défini, CORB tente de renifler le corps de la réponse pour déterminer s'il s'agit d'un fichier HTML, XML ou JSON. Cela est nécessaire, car certains serveurs Web sont mal configurés et diffusent des images en tant que text/html, par exemple.

Les ressources de données bloquées par la stratégie CORB sont présentées au processus comme vides, bien que la requête se produise toujours en arrière-plan. Par conséquent, une page Web malveillante a du mal à extraire des données intersites dans son processus pour les voler.

Pour une sécurité optimale et pour profiter de CORB, nous vous recommandons de suivre les conseils ci-dessous:

  • Marquez les réponses avec le bon en-tête Content-Type. (par exemple, les ressources HTML doivent être diffusées en tant que text/html, les ressources JSON avec un type MIME JSON et les ressources XML avec un type MIME XML).
  • Désactivez l'énumération à l'aide de l'en-tête X-Content-Type-Options: nosniff. Sans cet en-tête, Chrome effectue une analyse rapide du contenu pour essayer de confirmer que le type est correct. Toutefois, comme cela revient à autoriser les réponses afin d'éviter de bloquer des éléments tels que les fichiers JavaScript, il est donc préférable de procéder vous-même de manière explicite.

Pour en savoir plus, consultez l'article CORB pour les développeurs Web ou notre explication détaillée de CORB.

Pourquoi les développeurs Web devraient-ils se soucier de l'isolation de sites ?

Dans la plupart des cas, l'isolation de sites est une fonctionnalité de navigateur qui s'exécute en arrière-plan et n'est pas directement exposée aux développeurs Web. Par exemple, il n'y a pas de nouvelle API exposée sur le Web à apprendre. En général, les pages Web ne doivent pas faire la différence lorsqu'elles s'exécutent avec ou sans isolation de sites.

Toutefois, il existe quelques exceptions. L'activation de l'isolation de site comporte quelques effets secondaires subtils qui peuvent affecter votre site Web. Nous maintenons une liste des problèmes connus liés à l'isolation de site, et nous développons les plus importants ci-dessous.

La mise en page en pleine page n'est plus synchrone

Avec l'isolation de site, la mise en page de la page entière n'est plus garantie comme étant synchrone, car les cadres d'une page peuvent désormais être répartis sur plusieurs processus. Cela peut affecter les pages si elles supposent qu'un changement de mise en page se propage immédiatement à tous les cadres de la page.

Prenons l'exemple d'un site Web nommé fluffykittens.example qui communique avec un widget de réseau social hébergé sur social-widget.example:

<!-- https://fluffykittens.example/ -->
<iframe src="https://social-widget.example/" width="123"></iframe>
<script>
  const iframe = document.querySelector('iframe');
  iframe.width = 456;
  iframe.contentWindow.postMessage(
    // The message to send:
    'Meow!',
    // The target origin:
    'https://social-widget.example'
  );
</script>

Au début, la largeur de <iframe> du widget de réseau social est de 123 pixels. Toutefois, la page FluffyKittens modifie la largeur sur 456 pixels (déclenchement de la mise en page) et envoie un message au widget de réseau social, dont le code est le suivant:

<!-- https://social-widget.example/ -->
<script>
  self.onmessage = () => {
    console.log(document.documentElement.clientWidth);
  };
</script>

Chaque fois que le widget de réseau social reçoit un message via l'API postMessage, il consigne la largeur de son élément racine <html>.

Quelle valeur de largeur est enregistrée ? Avant que Chrome n'active l'isolation de sites, la réponse était 456. L'accès à document.documentElement.clientWidth force la mise en page, qui était synchrone avant l'activation de l'isolation de sites dans Chrome. Toutefois, lorsque l'isolation de sites est activée, la remise en page du widget de réseau social multi-origine s'effectue désormais de manière asynchrone dans un processus distinct. Par conséquent, la réponse peut désormais également être 123, c'est-à-dire l'ancienne valeur width.

Si une page modifie la taille d'un élément <iframe> multi-origines puis lui envoie un élément postMessage, avec l'isolation de site, la trame destinataire ne connaît peut-être pas encore sa nouvelle taille lors de la réception du message. Plus généralement, cela peut entraîner une erreur des pages si elles supposent qu'une modification de mise en page se propage immédiatement à tous les cadres de la page.

Dans cet exemple particulier, une solution plus robuste consisterait à définir la width dans le frame parent et à détecter ce changement dans la <iframe> en écoutant un événement resize.

Les gestionnaires de déchargement peuvent expirer plus souvent

Lorsqu'un frame navigue ou se ferme, l'ancien document ainsi que tous les documents de sous-cadre qui y sont intégrés exécutent leur gestionnaire unload. Si la nouvelle navigation se produit dans le même processus de rendu (par exemple, pour une navigation de même origine), les gestionnaires unload de l'ancien document et de ses sous-cadres peuvent s'exécuter pendant une durée arbitrairement longue avant d'autoriser la nouvelle navigation à s'engager.

addEventListener('unload', () => {
  doSomethingThatMightTakeALongTime();
});

Dans ce cas, les gestionnaires unload de tous les cadres sont très fiables.

Toutefois, même sans l'isolation de site, certaines navigations dans le frame principal sont inter-processus, ce qui a un impact sur le comportement du gestionnaire de déchargement. Par exemple, si vous passez de old.example à new.example en saisissant l'URL dans la barre d'adresse, la navigation new.example se produit dans un nouveau processus. Les gestionnaires de déchargement pour old.example et ses sous-cadres s'exécutent dans le processus old.example en arrière-plan, après l'affichage de la page new.example. Les anciens gestionnaires de déchargement sont arrêtés s'ils ne se terminent pas dans un délai donné. Étant donné que les gestionnaires de déchargement peuvent ne pas se terminer avant le délai avant expiration, le comportement de déchargement est moins fiable.

Avec l'isolation de sites, toutes les navigations intersites deviennent interprocessus, de sorte que les documents de différents sites ne partagent pas de processus entre eux. Par conséquent, la situation ci-dessus s'applique dans un plus grand nombre de cas, et les gestionnaires de déchargement dans les <iframe> ont souvent les comportements en arrière-plan et les délais avant expiration décrits ci-dessus.

Une autre différence résultant de l'isolation de site est le nouvel ordre parallèle des gestionnaires de déchargement : sans isolation de site, les gestionnaires de déchargement s'exécutent dans un ordre strict de haut en bas sur les cadres. Toutefois, avec l'isolation de sites, les gestionnaires de déchargement s'exécutent en parallèle dans différents processus.

Il s'agit de conséquences fondamentales de l'activation de l'isolation de site. L'équipe Chrome s'efforce d'améliorer la fiabilité des gestionnaires de déchargement pour les cas d'utilisation courants, dans la mesure du possible. Nous sommes également conscients des bugs dans lesquels les gestionnaires de déchargement des sous-cadres ne sont pas encore en mesure d'utiliser certaines fonctionnalités et s'efforcent de les résoudre.

L'envoi de pings de fin de session est un cas important pour les gestionnaires de déchargement. Pour ce faire, procédez comme suit:

addEventListener('pagehide', () => {
  const image = new Image();
  img.src = '/end-of-session';
});

Une approche plus robuste compte tenu de ce changement consiste à utiliser navigator.sendBeacon à la place:

addEventListener('pagehide', () => {
  navigator.sendBeacon('/end-of-session');
});

Si vous avez besoin de plus de contrôle sur la requête, vous pouvez utiliser l'option keepalive de l'API Fetch:

addEventListener('pagehide', () => {
  fetch('/end-of-session', {keepalive: true});
});

Conclusion

L'isolation de sites complique la tâche des sites Web non approuvés qui tentent d'accéder à des informations de comptes sur d'autres sites ou de les voler, en isolant chaque site dans son propre processus. Dans ce cadre, CORB tente de maintenir les ressources de données sensibles en dehors du processus de rendu. Nos recommandations ci-dessus vous permettront de profiter pleinement de ces nouvelles fonctionnalités de sécurité.

Merci à Alex Moshchuk, Charlie Reis, Jason Miller, Nasko Oskov, Philip Walton, Shubhi Panicker et Thomas Steiner d'avoir lu une version préliminaire de cet article et de nous avoir fait part de leurs commentaires.