frontend / html
iframes et sandboxing sécurisé
Explication
Ce que vous allez apprendre
- Comprendre les risques par défaut d'une
<iframe>sans restriction - Appliquer le principe du moindre privilège avec l'attribut
sandbox - Identifier la combinaison dangereuse
allow-scripts+allow-same-origin - Communiquer en sécurité entre une page et son iframe via
postMessage - Vérifier systématiquement
e.originavant de faire confiance à un message reçu
Dans quel contexte ?
Une revue de sécurité pointe une <iframe src="https://widget-partenaire.com/embed"> intégrée sur paiement.html, sans aucun attribut sandbox. Le widget tiers dispose alors des mêmes droits que s'il tournait dans un onglet à part entière : exécution de scripts, ouverture de popups, soumission de formulaires. En ajoutant sandbox="allow-scripts allow-forms", seuls les privilèges strictement nécessaires au fonctionnement du widget sont accordés, tout le reste restant verrouillé par défaut.
Étape 1 : faire entrer du contenu étranger chez soi
Une <iframe> intègre une autre page web à l'intérieur de la vôtre — une vidéo YouTube, un widget de paiement, un contenu partenaire. C'est extrêmement pratique.
Étape 2 : le problème de confiance que ça pose
Mais cela pose une vraie question : par défaut, ce contenu embarqué dispose de presque tous les mêmes droits que s'il était chargé dans un onglet séparé. Un peu comme laisser un inconnu entrer chez soi avec les clés de toutes les pièces.
Étape 3 : la solution, tout fermer d'abord
Voici comment on répond à ce risque : l'attribut sandbox applique le principe du "moindre privilège". Un sandbox="" vide verrouille absolument tout — scripts, formulaires, popups, cookies.
Étape 4 : rouvrir seulement ce qui est nécessaire
Une fois tout fermé, on rouvre explicitement, un par un, uniquement les privilèges réellement nécessaires (allow-scripts, allow-forms...). On part donc de "rien n'est permis" plutôt que l'inverse.
Valeur sandbox | Autorise |
|---|---|
allow-scripts | Exécution de JavaScript |
allow-forms | Soumission de formulaires |
allow-popups | window.open |
allow-same-origin | Accès à son propre localStorage/cookies |
Piège fréquent
Combiner allow-scripts et allow-same-origin sur la même iframe recrée une bonne partie des conditions d'une iframe non sandboxée : le contenu embarqué peut alors exécuter du JavaScript ET accéder à ses propres cookies. N'activez cette combinaison que si le contenu embarqué est réellement digne de confiance.
Étape 5 : un piège de combinaison à connaître
Il reste une combinaison dangereuse à éviter sans réflexion : allow-scripts ET allow-same-origin ensemble. Le contenu embarqué peut alors exécuter du JavaScript ET accéder à ses propres cookies, ce qui recrée une bonne partie des conditions d'une iframe non sandboxée.
Étape 6 : communiquer sans se faire confiance aveuglément
Il reste un dernier point à comprendre : une page et son iframe d'origines différentes ne peuvent jamais accéder directement au contenu l'une de l'autre. Le seul canal légitime est postMessage, une sorte de boîte aux lettres sécurisée.
Mais attention, n'importe quelle page peut techniquement envoyer un message : il est donc indispensable de toujours vérifier e.origin avant de faire confiance au contenu reçu.
Bonne pratique
Précisez toujours une origine exacte ("https://partenaire.com") en second argument de postMessage, jamais "*" en production. Du côté réception, vérifiez systématiquement e.origin avant de traiter e.data : sans cette vérification, n'importe quel site pourrait injecter de fausses données dans votre page.
Et ensuite ?
Une fois la sécurité des iframes comprise, la dernière leçon du cours montre comment <dialog> et la Popover API remplacent des années de code JavaScript pour les modales.
Commandes & code
iframes et sandboxing sécurisé
Intégrer du contenu tiers sans lui laisser un accès complet à la page hôte.
<!-- iframe basique : par défaut, le contenu embarqué a QUASIMENT tous les droits -->
<iframe src="https://partenaire.com/widget" title="Widget partenaire"></iframe>
<!-- sandbox="" (vide) : verrouille TOUT, puis on rouvre au cas par cas -->
<iframe
src="https://contenu-non-fiable.com/embed"
title="Contenu tiers restreint"
sandbox="allow-scripts allow-forms"
referrerpolicy="no-referrer"
loading="lazy"
></iframe>
<!-- sandbox="" seul : bloque scripts, formulaires, popups, plugins, et traite
l'origine comme unique (pas d'accès localStorage/cookies du domaine réel) -->Valeur sandbox | Autorise |
|---|---|
allow-scripts | exécution JavaScript |
allow-same-origin | accès à son propre localStorage/cookies (attention : combiné à allow-scripts, annule une partie de la protection) |
allow-forms | soumission de formulaires |
allow-popups | window.open |
allow-top-navigation | rediriger la page PARENTE (dangereux, à éviter) |
<!-- Cas concret : embarquer une vidéo tierce sans risque -->
<iframe
src="https://www.youtube-nocookie.com/embed/xyz"
title="Vidéo de démonstration"
sandbox="allow-scripts allow-same-origin allow-presentation"
allow="fullscreen; encrypted-media"
loading="lazy"
></iframe>// Communication sécurisée parent <-> iframe : postMessage, jamais d'accès direct
// au contentWindow d'une origine différente (bloqué par le navigateur de toute façon)
// --- Côté page parente ---
const iframe = document.querySelector("iframe");
iframe.addEventListener("load", () => {
iframe.contentWindow.postMessage(
{ type: "init", theme: "sombre" },
"https://partenaire.com" // origine cible EXACTE : jamais "*" en prod
);
});
window.addEventListener("message", (e) => {
// Vérifier l'origine : n'importe quelle page peut envoyer un postMessage
if (e.origin !== "https://partenaire.com") return;
if (e.data.type === "hauteur-contenu") {
iframe.style.height = `${e.data.valeur}px`;
}
});// --- Côté iframe (sur le domaine partenaire.com) ---
window.addEventListener("message", (e) => {
if (e.origin !== "https://technologik.dev") return;
if (e.data.type === "init") {
document.body.dataset.theme = e.data.theme;
}
// Répondre en indiquant sa propre hauteur au parent
window.parent.postMessage(
{ type: "hauteur-contenu", valeur: document.body.scrollHeight },
"https://technologik.dev"
);
});<!-- Content-Security-Policy (en-tête HTTP, pas balise) : contrôle QUI peut être embarqué -->
<!-- Header envoyé par le SERVEUR, indicatif ici en commentaire :
Content-Security-Policy: frame-src https://partenaire.com https://www.youtube-nocookie.com -->
<!-- Empêcher SA PROPRE page d'être embarquée ailleurs (anti-clickjacking) -->
<!-- Header serveur : Content-Security-Policy: frame-ancestors 'self' -->Résumé
sandbox=""verrouille tout par défaut ; chaque privilège (allow-scripts,allow-forms...) se rouvre explicitement.allow-scripts+allow-same-originensemble annulent une partie de l'isolation : à combiner avec prudence.postMessageest le seul canal de communication légitime entre origines différentes ; toujours vérifiere.origin.frame-ancestors(CSP) protège sa propre page contre le clickjacking par iframe.
Exercices pratiques
Mission : verrouiller le widget partenaire de paiement.html
Objectif : Corriger une iframe tierce non restreinte sur une page de paiement, en appliquant le principe du moindre privilège.
Contexte
Une revue de sécurité pointe une <iframe src="https://widget-partenaire.com/embed"> intégrée sur paiement.html, sans aucun attribut sandbox. Le widget dispose ainsi des mêmes droits que s'il tournait dans un onglet à part entière. Ce widget a besoin d'exécuter du JavaScript et de soumettre un formulaire de carte bancaire, mais n'a besoin ni de popups ni d'accès à ses propres cookies.
Ta mission : restreindre cette iframe au strict nécessaire.