Window: método postMessage()
Baseline Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
El método window.postMessage()
permite la comunicación segura entre objetos Window
de diferentes orígenes; por ejemplo, entre una página y una ventana emergente que ha abierto, o entre una página y un iframe incrustado dentro de ella.
Normalmente, a los scripts en diferentes páginas se les permite acceder entre sí si y solo si las páginas de las que se originan comparten el mismo origen (también conocido como "política del mismo origen"). window.postMessage()
proporciona un mecanismo controlado para eludir esta restricción de forma segura (si se usa correctamente).
Furthermore, an accessing script must have obtained the window object of the accessed document beforehand. This can occur through methods such as window.open()
for popups or iframe.contentWindow
for iframes.
Además, un script de acceso debe haber obtenido el objeto de ventana del documento al que se ha accedido de antemano. Esto puede ocurrir a través de métodos como window.open()
para ventanas emergentes o iframe.contentWindow
para iframes.
En términos generales, una ventana puede obtener una referencia a otra (por ejemplo, a través de targetWindow = window.opener
) y luego enviar un MessageEvent
a través de targetWindow.postMessage()
. La ventana receptora queda entonces libre para manejar este evento según sea necesario. Los argumentos pasados a window.postMessage()
(es decir, el "mensaje") se exponen a la ventana receptora a través del objeto de evento.
Sintaxis
postMessage(message)
postMessage(message, targetOrigin)
postMessage(message, targetOrigin, transfer)
postMessage(message, options)
Parámetros
message
-
Datos que se enviarán a la otra ventana. Los datos se serializan utilizando el algoritmo de clonación estructurada. Esto significa que puedes pasar una amplia variedad de objetos de datos de forma segura a la ventana de destino sin tener que serializarlos tú mismo.
targetOrigin
Opcional-
Especifica el origen que debe tener la ventana del destinatario para recibir el evento. Para que se envíe el evento, el origen debe coincidir exactamente (incluido el esquema, el nombre de host y el puerto). Si se omite, se toma como valor predeterminado el origen que llama al método. Este mecanismo proporciona control sobre el lugar al que se envían los mensajes; por ejemplo, si se utilizó
postMessage()
para transmitir una contraseña, sería absolutamente fundamental que este argumento fuera una URI cuyo origen sea el mismo que el del receptor previsto del mensaje que contiene la contraseña, para evitar la intercepción de la contraseña por parte de un tercero malintencionado. También se puede proporcionar*
, lo que significa que el mensaje se puede enviar a un receptor con cualquier origen.Nota: Siempre proporcione un
targetOrigin
específico, no*
, si sabe dónde debe ubicarse el documento de la otra ventana. Si no proporciona un target específico, podría revelar datos a un sitio malicioso. transfer
Opcional-
Un arreglo opcional de objetos transferibles para transferir la propiedad. La propiedad de estos objetos se otorga al lado de destino y ya no se pueden utilizar en el lado de envío. Estos objetos transferibles deben adjuntarse al mensaje; de lo contrario, se moverían pero no serían accesibles en el extremo receptor.
options
Opcional-
Un objeto opcional que contiene las siguientes propiedades:
transfer
Opcional-
Tiene el mismo significado que el parámetro
transfer
. targetOrigin
Opcional-
Tiene el mismo significado que el parámetro
targetOrigin
.
Valor de retorno
Ninguno (undefined
).
El evento enviado
Un objeto window
puede escuchar mensajes enviados ejecutando el siguiente JavaScript:
window.addEventListener(
"message",
(event) => {
if (event.origin !== "http://example.org:8080") return;
// …
},
false,
);
Las propiedades del mensaje enviado son:
data
-
El objeto pasado desde la otra ventana.
origin
-
El origen de la ventana que envió el mensaje en el momento en que se llamó a
postMessage
. Esta cadena es la concatenación del protocolo y "://", el nombre de host si existe, y ":" seguido de un número de puerto si hay un puerto presente y difiere del puerto predeterminado para el protocolo dado. Ejemplos de orígenes típicos sonhttps://example.org
(implicando el puerto443
),http://example.net
(implicando el puerto80
), yhttp://example.com:8080
. Ten en cuenta que este origen no está garantizado para ser el origen actual o futuro de esa ventana, que podría haber sido navegada a una ubicación diferente desde que se llamó apostMessage
. source
-
Una referencia al objeto
window
que envió el mensaje; puedes usar esto para establecer una comunicación bidireccional entre dos ventanas con diferentes orígenes.
Preocupaciones de seguridad
Si no esperas recibir mensajes de otros sitios, no agregues ningún detector de eventos para eventos de mensaje
. Esta es una forma completamente infalible de evitar problemas de seguridad.
Si esperas recibir mensajes de otros sitios, siempre verifica la identidad del remitente usando las propiedades origin
y posiblemente source
. Cualquier ventana (incluyendo, por ejemplo, http://evil.example.com
) puede enviar un mensaje a cualquier otra ventana, y no tienes garantías de que un remitente desconocido no enviará mensajes maliciosos. Habiendo verificado la identidad, sin embargo, aún deberías verificar siempre la sintaxis del mensaje recibido. De lo contrario, un agujero de seguridad en el sitio en el que confiabas para enviar solo mensajes confiables podría entonces abrir un agujero de scripting entre sitios en tu sitio.
Siempre especifica un origen de destino exacto, no *
, cuando uses postMessage
para enviar datos a otras ventanas. Un sitio malicioso puede cambiar la ubicación de la ventana sin tu conocimiento y, por lo tanto, interceptar los datos enviados utilizando postMessage
.
Mensajería segura de memoría compartida
Si postMessage()
lanza una excepción cuando se usa con objetos SharedArrayBuffer
, es posible que necesites asegurarte de que tu sitio esté correctamente aislado entre sitios. La memoria compartida está protegida detrás de dos encabezados HTTP:
Cross-Origin-Opener-Policy
consame-origin
como valor (protege tu origen de atacantes)Cross-Origin-Embedder-Policy
conrequire-corp
ocredentialless
como valor (protege a las víctimas de tu origen)
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Para comprobar si el aislamiento entre sitios ha sido exitoso, puedes probar contra la propiedad Window.crossOriginIsolated
disponible para los contextos de ventana y worker:
const myWorker = new Worker("worker.js");
if (crossOriginIsolated) {
const buffer = new SharedArrayBuffer(16);
myWorker.postMessage(buffer);
} else {
const buffer = new ArrayBuffer(16);
myWorker.postMessage(buffer);
}
Ejemplos
/*
* En los scripts de la ventana A, con A en http://example.com:8080:
*/
const popup = window.open(/* detalles de la ventana emergente */);
// Cuando la ventana emergente se haya cargado completamente, si no está bloqueada por un bloqueador de ventanas emergentes:
// Esto no hace nada, suponiendo que la ventana no haya cambiado su ubicación.
popup.postMessage(
"El usuario es 'bob' y la contraseña es 'secret'",
"https://secure.example.net",
);
// Esto colocará exitosamente un mensaje en la cola para ser enviado a la ventana emergente,
// suponiendo que la ventana no haya cambiado su ubicación.
popup.postMessage("¡Hola!", "http://example.com");
window.addEventListener(
"message",
(event) => {
// ¿Confiamos en el remitente de este mensaje? (podría ser
// diferente de lo que originalmente abrimos, por ejemplo).
if (event.origin !== "http://example.com") return;
// event.source es popup
// event.data es "¡hola a ti mismo! la respuesta secreta es: ¡rheeeeet!"
},
false,
);
/*
* En los scripts de la ventana emergente, ejecutándose en http://example.com:
*/
// Llamado en algún momento después de que se llama a postMessage
window.addEventListener("message", (event) => {
// ¿Confiamos en el remitente de este mensaje?
if (event.origin !== "http://example.com:8080") return;
// event.source es window.opener
// event.data es "¡Hola!"
// Suponiendo que has verificado el origen del mensaje recibido (lo cual
// debes hacer en cualquier caso), un modismo conveniente para responder a un
// mensaje es llamar a postMessage en event.source y proporcionar
// event.origin como el targetOrigin.
event.source.postMessage(
"¡hola a ti mismo! la respuesta secreta " + "es: ¡rheeeeet!",
event.origin,
);
});
Notas
Cualquier script en un documento en una ventana puede solicitar que se envíe un mensaje a un documento en otra ventana cuyo objeto de ventana haya obtenido, llamando a .postMessage()
en ese objeto de ventana. En consecuencia, cualquier detector de eventos utilizado para recibir mensajes debe comprobar primero la identidad del remitente del mensaje, utilizando las propiedades origin
y posiblemente source
. Esto no se puede exagerar: No comprobar las propiedades origin
y posiblemente source
permite ataques de secuencias de comandos entre sitios.
Como con cualquier script despachado asincrónicamente (tiempos de espera, eventos generados por el usuario), no es posible para el llamador de postMessage
detectar cuando un controlador de eventos que detecta eventos enviados por postMessage
lanza una excepción.
Después de que se llama a postMessage()
, el MessageEvent
se despachará solo después de que todos los contextos de ejecución pendientes hayan terminado. Por ejemplo, si postMessage()
se invoca en un controlador de eventos, ese controlador de eventos se ejecutará hasta completarse, al igual que cualquier controlador restante para ese mismo evento, antes de que se despache el MessageEvent
.
El valor de la propiedad origin
del evento despachado no se ve afectado por el valor actual de document.domain
en la ventana llamadora.
Para nombres de host IDN solamente, el valor de la propiedad origin
no es consistentemente Unicode o punycode; para una mayor compatibilidad, verifica tanto los valores IDN como punycode cuando uses esta propiedad si esperas mensajes de sitios IDN. Este valor eventualmente será consistentemente IDN, pero por ahora debes manejar ambas formas, IDN y punycode.
El valor de la propiedad origin
cuando la ventana que envía contiene una URL javascript:
o data:
es el origen del script que cargó la URL.
Uso de window.postMessage en extensiones No estándar
window.postMessage
está disponible para JavaScript que se ejecuta en código de chrome (por ejemplo, en extensiones y código privilegiado), pero la propiedad source
del evento despachado siempre es null
como una restricción de seguridad. (Las otras propiedades tienen sus valores esperados).
No es posible para scripts de contenido o contextos web especificar un targetOrigin
para comunicarse directamente con una extensión (ya sea el script de fondo o un script de contenido). Los scripts web o de contenido pueden usar window.postMessage
con un targetOrigin
de "*"
para transmitir a cada oyente, pero esto no se recomienda, ya que una extensión no puede estar segura del origen de tales mensajes, y otros oyentes (incluidos aquellos que no controlas) pueden detectar.
Los scripts de contenido deben usar runtime.sendMessage
para comunicarse con el script de fondo. Los scripts de contexto web pueden usar eventos personalizados para comunicarse con los scripts de contenido (con nombres de eventos generados aleatoriamente, si es necesario, para evitar el espionaje desde la página invitada).
Por último, publicar un mensaje a una página en una URL file:
actualmente requiere que el argumento targetOrigin
sea "*"
. file://
no puede ser usado como una restricción de seguridad; esta restricción puede ser modificada en el futuro.
Especificaciones
Specification |
---|
HTML Standard # dom-window-postmessage-options-dev |
Compatibilidad con navegadores
BCD tables only load in the browser
Véase también
Document.domain
CustomEvent
BroadcastChannel
- Para comunicación del mismo origen.