La méthode window.postMessage
permet une communication inter-domaine en toute sécurité. Normalement, les scripts de différentes pages sont autorisés à accéder les uns aux autres si et seulement si les pages depuis lesquelles ils sont exécutés ont des URL de même origine, c'est-à-dire avec le même protocole (généralement http
ou https
), le même numéro de port (80
étant le port par défaut pour http
), et le même nom d'hôte (à condition que document.domain soit initialisé à la même valeur par les deux pages). window.postMessage
fournit un mécanisme contrôlé pour contourner cette restriction d'une manière sécurisée si bien utilisée.
La méthode window.postMessage
, quand elle est appelée, provoque l'envoi d'un MessageEvent
à la fenêtre ciblée une fois que tout script en attente a terminé son exécution (par exemple, les gestionnaires d'évènements restants si window.postMessage
est appelée depuis un gestionnaire d'évènement, des timeouts en attente enregistrées auparavant, etc.) Le MessageEvent
est de type message
, a une propriété data
qui est initialisée à la valeur du premier argument passé à window.postMessage
, une propriété origin
correspondant à l'origine du document principal de la fenêtre appelant window.postMessage
au moment où window.postMessage
a été appelée, et une propriété source
qui est la fenêtre depuis laquelle window.postMessage
est appelée. (les autres propriétés standard d'évènement sont présentes avec leurs valeurs attendues).
Syntaxe
otherWindow.postMessage(message, targetOrigin, [transfer]);
otherWindow
- Une référence à une autre fenêtre ; une telle référence peut être obtenue, par exemple, via la propriété
contentWindow
d'un élémentiframe
, l'objet retourné par window.open, ou par index nommé ou numérique de window.frames. message
- La donnée à envoyer à l'autre fenêtre. Elle est sérialisée en utilisant l'algorithme de clônage structuré. Cela signifie que vous pouvez passer sereinement une large variété d'objets de données à la fenêtre de destination sans avoir à les sérialiser vous-mêmes. [1]
targetOrigin
- Indique quelle doit être l'origine de
otherWindow
pour l'évènement à envoyer, soit comme la chaîne littérale"*"
(signifiant pas de préférence) soit comme une URI. Si, au moment où l'évènement est inscrit pour être envoyé, le protocole, le nom d'hôte ou le port du document deotherWindow
ne correspond pas à ceux contenus danstargetOrigin
, l'évènement ne sera pas envoyé ; il ne le sera que si les trois correspondent. Ce mécanisme permet de contrôler où les messages sont envoyés ; par exemple, sipostMessage
était utilisé pour transmettre un mot de passe, il serait absolument vital que cet argument soit une URI dont l'origine est la même que le récepteur prévu du message contenant le mot de passe, afin de prévenir l'interception du mot de passe par une tierce-partie malicieuse. Fournissez toujours unetargetOrigin
spécifique, jamais*
, si vous savez où le document de l'autre fenêtre est censé se trouver. Ne pas fournir une cible spécifique expose les données que vous envoyez à tout site malicieux à l'écoute. transfer
Facultatif- Séquence d'objets
Transferable
qui sera transmise avec le message. La possession de ces objets est cédée à la destination et ils ne sont plus utilisables du côté de l'expéditeur.
L'évènement envoyé
otherWindow
peut surveiller les messages envoyés en exécutant le JavaScript suivant :
window.addEventListener("message", receiveMessage, false); function receiveMessage(event) { if (event.origin !== "https://example.org:8080") return; // ... }
Les propriétés du message envoyé sont :
data
- L'objet passé depuis l'autre fenêtre.
origin
- L'origine de la fenêtre qui a envoyé le message au moment où
postMessage
a été appelée. Des exemples typiques d'origines sonthttps://example.org
(sous-entendu port443
),https://example.net
(sous-entendu port80
), ethttps://example.com:8080
. Notez qu'il n'est pas garanti que cette origine soit l'origine actuelle ou future de cette fenêtre, qui peut avoir été naviguée vers une adresse différente depuis l'appel àpostMessage
. source
- Une référence à l'objet
window
qui a envoyé le message ; vous pouvez utiliser ceci pour établir une communication dans les deux sens entre deux fenêtres ayant différentes origines.
Précautions de sécurité
Si vous ne prévoyez pas de recevoir de messages depuis d'autres sites, n'ajoutez pas de gestionnaire d'évènement pour les évènements message
. C'est un moyen sûr d'éviter les problèmes de sécurité.
Si vous prévoyez de recevoir des messages depuis d'autres sites, vérifiez toujours l'identité de l'expéditeur à l'aide des propriétés origin
et si possible source
. Toute fenêtre (y compris, par exemple, https://evil.example.com
) peut envoyer un message à toute autre fenêtre, et vous n'avez aucune garantie qu'un expéditeur inconnu ne va pas envoyer de message malicieux. Cependant, même si vous vérifiez l'identité, vous devriez toujours vérifier la syntaxe du message reçu. Dans le cas contraire, une faille de sécurité dans le site auquel vous faites confiance peut ouvrir une vulnérabilité XSS dans votre propre site.
Spécifiez toujours explicitement une origine de destination, jamais *
, quand vous utilisez postMessage
pour envoyer des données à d'autres fenêtres. Un site malicieux peut changer l'adresse de la fenêtre à votre insu, et ainsi intercepter les données envoyées à l'aide de postMessage
.
Exemple
/* * Dans les scripts de la fenêtre A, avec A sur <https://example.com:8080>: */ var popup = window.open(...popup details...); // Quand la popup est chargée, si pas bloquée par un bloqueur de popups: // Ceci ne fait rien, en supposant que la fenêtre n'a pas changé d'adresse. popup.postMessage("The user is 'bob' and the password is 'secret'", "https://secure.example.net"); // Ceci va planifier l'envoi d'un message à la popup, en supposant que // la fenêtre n'a pas changé d'adresse. popup.postMessage("hello there!", "https://example.org"); function receiveMessage(event) { // Faisons-nous confiance à l'expéditeur de ce message ? (il pourrait être // différent de la fenêtre que nous avons ouverte au départ, par exemple). if (event.origin !== "https://example.org") return; // event.source est la popup // event.data est "hi there yourself! the secret response is: rheeeeet!" } window.addEventListener("message", receiveMessage, false);
/* * Dans les scripts de la popup, sur <https://example.org>: */ // Appelée quelques instants après que postMessage a été appelée function receiveMessage(event) { // Faisons-nous confiance à l'expéditeur de ce message ? if (event.origin !== "https://example.com:8080") return; // event.source est window.opener // event.data est "hello there!" // Supposant que vous avez vérifié l'origine du message reçu // (ce que vous devriez faire en toutes circonstances), // un moyen pratique de répondre à un message est d'appeler postMessage // sur event.source et fournir event.origin comme targetOrigin. event.source.postMessage("hi there yourself! the secret response " + "is: rheeeeet!", event.origin); } window.addEventListener("message", receiveMessage, false);
Notes
Toute fenêtre peut accéder à cette méthode sur toute autre fenêtre, à tout moment, peu importe l'adresse du document dans la fenêtre, pour y envoyer un message. Par conséquent, tout gestionnaire d'évènement utilisé pour recevoir des messages doit d'abord vérifier l'identité de l'expéditeur du message, en utilisant les propriétés origin
et si possible source
. Cela ne peut pas être minimisé : ne pas vérifier les propriétés origin
et si possible source
permet des attaques inter-site.
De même qu'avec les scripts exécutés de manière asynchrone (timeouts, evènements générés par l'utilisateur), il n'est pas possible pour l'appelant à postMessage
de détecter quand un gestionnaire d'évènement écoutant des évènements envoyés par postMessage
lance une exception.
La valeur de la propriété origin
de l'évènement envoyé n'est pas affectée par la valeur actuelle de document.domain
dans la fenêtre appelante.
Pour les noms d'hôte IDN uniquement, la valeur de la propriété origin
n'est pas systématiquement Unicode ou punycode ; pour la plus grande compatibilité, testez à la fois les valeurs IDN et punycode quand vous utilisez cette propriété si vous attendez des messages de sites IDN. Cette valeur sera systématiquement IDN à l'avenir, mais pour l'instant vous devriez gérer à la fois les formes IDN et punycode.
La valeur de la propriété origin
quand la fenêtre expéditrice contient une URL javascript:
ou data:
est l'origin du script qui a chargé cette URL.
Utiliser window.postMessage dans les extensions
window.postMessage
est disponible depuis le JavaScript exécuté en code chrome (par exemple dans les extensions et le code privilégié), mais la propriété source
de l'évènement envoyé est toujours null
par mesure de sécurité. (Les autres propriétés ont leurs valeurs usuelles.) L'argument targetOrigin
pour un message envoyé à une fenêtre située à une URL chrome:
est actuellement mal interprété, si bien que la seule valeur qui conduit à l'envoi d'un message est "*"
. Comme cette valeur n'est pas sûre quand la fenêtre ciblée peut être naviguée n'importe où par un site malicieux, il est recommandé de ne pas utiliser postMessage
pour communiquer avec des pages chrome:
pour l'instant ; utilisez une méthode différente (comme une query string quand la fenêtre est ouverte) pour communiquer avec des fenêtres chrome. Enfin, poster un message à une page à une URL file:
requiert actuellement que l'argument targetOrigin
soit "*"
. file://
ne peut pas être utilisé comme restriction de sécurité ; cette restriction pourrait être modifiée à l'avenir.
Spécifications
Spécification | Status | Commentaire |
---|---|---|
WHATWG HTML Living Standard La définition de 'window.postMessage' dans cette spécification. |
Standard évolutif | Pas de changement depuis HTML5 Web Messaging |
HTML5 Web Messaging La définition de 'window.postMessage' dans cette spécification. |
Recommendation | Définition initiale. |
Compatibilité des navigateurs
Fonctionnalité | Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari (WebKit) |
---|---|---|---|---|---|
Support de base | 1.0 | 6.0 (6.0) [1] [4] | 8.0 [2] [3] | 9.5 | 4.0 |
argument transfer |
? | 20.0 (20.0) | Pas de support | ? | ? |
Fonctionnalité | Android | Firefox Mobile (Gecko) | IE Phone | Opera Mobile | Safari Mobile |
---|---|---|---|---|---|
Support de base | (Oui) | 6.0 (6.0) [1] [4] | (Oui) | (Oui) | (Oui) |
argument transfer |
? | 20.0 (20.0) | Pas de support | ? | ? |
[1] Pour les versions antérieures à Gecko 6.0 (Firefox 6.0 / Thunderbird 6.0 / SeaMonkey 2.3), le paramètre message
doit être une chaîne. À partir de Gecko 6.0 (Firefox 6.0 / Thunderbird 6.0 / SeaMonkey 2.3), le paramètre message
est sérialisé à l'aide de l'agorithme de clônage structuré. Cela signifie que vous pouvez passer sereinement une large variété d'objets de données à la fenêtre de destination sans avoir à les sérialiser vous-mêmes.
[2] IE8 et IE9 le supportent seulement pour <frame>
et <iframe>
.
[3] IE10 a des limitations importantes : voyez cet article pour les détails.
[4] Gecko 8.0 a introduit le support de l'envoi d'objets File
et FileList
entre les fenêtres. Ceci est permis seulement si la fenêtre du destinataire est contenue dans la fenêtre de l'expéditeur, pour des raisons de sécurité.