Command Palette

Search for a command to run...

Comunicación con postMessage: Mensajes Entre Contextos en JavaScript

Aprende a comunicar entre el hilo principal y Web Workers usando mensajes bidireccionales con postMessage, incluyendo serialización y transferencia de objetos.

Lectura: 14 min
Nivel: Intermedio

TL;DR - Resumen rápido

  • postMessage permite comunicación segura entre diferentes contextos de ejecución
  • Los datos se serializan automáticamente usando el algoritmo de clonación estructurada
  • Puedes transferir objetos como ArrayBuffer para evitar copias costosas
  • Siempre valida el origen de los mensajes recibidos para evitar ataques
  • El sistema es asíncrono y no bloquea el hilo de ejecución

Introducción a postMessage

El método postMessage es la forma principal de comunicación entre diferentes contextos de ejecución en JavaScript. Introducida en HTML5 (2008) como parte de la especificación Web Messaging, permite enviar mensajes de forma segura entre el hilo principal y Web Workers, entre iframes, entre ventanas del navegador, e incluso entre diferentes dominios cuando se configura correctamente.

La comunicación con postMessage es asíncrona y no bloqueante, lo que significa que el emisor no espera a que el receptor procese el mensaje. Esto es fundamental para mantener la fluidez de la interfaz de usuario, especialmente cuando trabajas con Web Workers que ejecutan tareas en segundo plano.

  • <strong>Comunicación con Web Workers</strong>: Enviar y recibir datos entre el hilo principal y workers
  • <strong>Comunicación entre iframes</strong>: Mensajes entre documentos en diferentes iframes
  • <strong>Comunicación entre ventanas</strong>: Mensajes entre ventanas del navegador (window.open)
  • <strong>Comunicación cross-origin</strong>: Mensajes entre diferentes dominios con validación de origen
  • <strong>Comunicación con Service Workers</strong>: Mensajes entre la página y el Service Worker

postMessage Básico

El método postMessage tiene dos parámetros principales: el mensaje a enviar y opcionalmente un array de objetos a transferir. El mensaje puede ser cualquier dato que sea clonable según el algoritmo de clonación estructurada de JavaScript.

Enviar un Mensaje

Para enviar un mensaje, simplemente llamas a postMessage en el objeto del contexto destino. Este puede ser un Web Worker, un iframe, o una ventana del navegador.

postmessage-basico.js
Loading code...

Este ejemplo muestra cómo enviar un mensaje simple a un Web Worker. El mensaje puede ser un string, un número, un objeto, o un array. JavaScript serializa automáticamente el mensaje usando el algoritmo de clonación estructurada.

Recibir un Mensaje

Para recibir mensajes, debes escuchar el evento message en el contexto receptor. El evento contiene el mensaje en la propiedad data, y también información sobre el origen del mensaje.

recibir-mensaje.js
Loading code...

El evento message proporciona información importante sobre el mensaje recibido. La propiedad data contiene el mensaje, origin indica de dónde vino el mensaje, y source es una referencia al objeto que envió el mensaje, lo que permite responder directamente.

Mejor práctica

Siempre valida el origen de los mensajes recibidos usando event.origin. Esto previene ataques donde sitios maliciosos intentan enviar mensajes a tu aplicación. Nunca confíes ciegamente en los mensajes que recibes.

Origen y Destino

La propiedad origin es fundamental para la seguridad en la comunicación con postMessage. Representa el origen del mensaje en formato "protocol://host:port" y te permite validar que el mensaje viene de una fuente confiable.

Validar el Origen

Validar el origen es crucial para prevenir ataques de seguridad. Solo debes procesar mensajes que vienen de orígenes que conoces y confías.

validar-origen.js
Loading code...

Este ejemplo muestra cómo validar el origen antes de procesar un mensaje. La función isValidOrigin comprueba si el origen está en una lista de orígenes permitidos, lo que es una práctica de seguridad esencial.

Responder a un Mensaje

La propiedad source del evento message es una referencia al objeto que envió el mensaje. Puedes usar esta referencia para responder directamente al emisor sin necesidad de mantener una referencia separada.

responder-mensaje.js
Loading code...

Usar event.source para responder es más elegante y menos propenso a errores que mantener referencias separadas. También funciona correctamente en situaciones donde tienes múltiples emisores de mensajes.

Advertencia de seguridad

Nunca uses event.source.postMessage sin validar primero event.origin. Un atacante podría inyectar un iframe malicioso que intenta comunicarse con tu aplicación. Siempre valida el origen antes de responder.

Serialización de Datos

Cuando envías un mensaje con postMessage, JavaScript serializa automáticamente los datos usando el algoritmo de clonación estructurada. Este algoritmo copia el objeto completo, incluyendo propiedades anidadas, pero tiene ciertas limitaciones sobre qué tipos de datos puede clonar.

Tipos de Datos Clonables

El algoritmo de clonación estructurada puede clonar la mayoría de los tipos de datos primitivos y objetos comunes de JavaScript. Sin embargo, hay ciertos tipos que no pueden clonarse directamente.

tipos-clonables.js
Loading code...

Este ejemplo muestra los tipos de datos que pueden clonarse y cómo manejar tipos que no son clonables. Para funciones, clases, y otros objetos no clonables, debes usar técnicas alternativas como transferencia de objetos o serialización manual.

Limitaciones de la Clonación

Hay ciertas limitaciones importantes que debes conocer al trabajar con la clonación estructurada. Estas limitaciones pueden causar errores si intentas enviar objetos que no son clonables.

limitaciones-clonacion.js
Loading code...

Las funciones, los objetos DOM, los objetos con referencias circulares, y ciertos objetos especializados no pueden clonarse. Si intentas enviar estos objetos, recibirás un error de tipo DataCloneError.

Error DataCloneError

Cuando intentas clonar un objeto no clonable, JavaScript lanza un DataCloneError. Este error indica que el objeto contiene propiedades que no pueden clonarse. Para evitar este error, usa la transferencia de objetos para ArrayBuffer, o serializa manualmente objetos complejos.

Transferencia de Objetos

La transferencia de objetos es una alternativa a la clonación que permite mover objetos en lugar de copiarlos. Esto es especialmente útil para objetos grandes como ArrayBuffer, MessagePort, y otros objetos transferibles, ya que evita la sobrecarga de copiar grandes cantidades de datos.

  • <strong>ArrayBuffer</strong>: Buffers de datos binarios, ideales para imágenes, audio y datos numéricos
  • <strong>MessagePort</strong>: Canales de comunicación dedicados entre contextos
  • <strong>ImageBitmap</strong>: Imágenes bitmap para procesamiento de gráficos
  • <strong>OffscreenCanvas</strong>: Canvas que se puede renderizar en un worker

Transferir ArrayBuffer

Los ArrayBuffer son objetos transferibles que puedes mover entre contextos sin copiar sus datos. Esto es mucho más eficiente que clonar, especialmente para buffers grandes.

transferir-arraybuffer.js
Loading code...

Al transferir un ArrayBuffer, el objeto se mueve completamente al contexto destino. El contexto original pierde el acceso al buffer, lo que se refleja en que el buffer queda "detached" (desconectado). Esto es importante porque no puedes usar el buffer después de transferirlo.

Transferir MessagePort

Los MessagePort son objetos transferibles que crean canales de comunicación dedicados entre contextos. Al transferir un MessagePort, estableces una conexión directa que puede usarse para comunicación bidireccional.

transferir-messageport.js
Loading code...

Los MessagePort son útiles para crear canales de comunicación dedicados entre contextos. A diferencia de la comunicación global con postMessage, los MessagePort proporcionan un canal privado y directo entre dos contextos específicos.

Rendimiento de transferencia

La transferencia de objetos es significativamente más rápida que la clonación para objetos grandes. Mientras que la clonación copia todos los datos, la transferencia simplemente mueve la referencia del objeto, lo que es una operación O(1) en lugar de O(n) donde n es el tamaño del objeto.

Errores Comunes en Transferencia

Hay errores comunes que debes evitar al transferir objetos. El error más frecuente es intentar transferir un objeto que ya fue transferido, o intentar usar un objeto después de transferirlo.

error-transferencia.js
Loading code...

Este ejemplo muestra errores comunes al transferir objetos. El primer error es intentar transferir el mismo objeto dos veces, lo que lanza un error. El segundo error es intentar usar un objeto después de transferirlo, lo que también causa un error.

Seguridad con postMessage

La seguridad es fundamental cuando trabajas con postMessage, especialmente en aplicaciones que permiten comunicación cross-origin. Hay varios vectores de ataque que debes conocer y proteger contra ellos.

Validación de Origen

La validación del origen es la primera línea de defensa contra ataques. Debes validar event.origin antes de procesar cualquier mensaje recibido.

validacion-origen.js
Loading code...

Este ejemplo muestra una implementación robusta de validación de origen. La función isValidOrigin comprueba si el origen está en una lista blanca de orígenes permitidos, y el código solo procesa mensajes de orígenes válidos.

Sanitización de Mensajes

Además de validar el origen, debes sanitizar los mensajes recibidos para evitar inyección de código y otros ataques. Nunca ejecutes código recibido en un mensaje sin validación.

sanitizacion-mensajes.js
Loading code...

Este ejemplo muestra cómo sanitizar mensajes recibidos. La función sanitizeMessage valida que el mensaje tenga la estructura esperada y que los campos sean del tipo correcto. Esto previene inyección de código y otros ataques.

Advertencia de seguridad crítica

Nunca uses eval(), Function(), o cualquier otra función que ejecute código dinámico con datos recibidos de postMessage. Esto es una vulnerabilidad de seguridad grave que puede permitir a atacantes ejecutar código arbitrario en tu aplicación.

Comunicación Cross-Origin

La comunicación cross-origin permite que diferentes dominios se comuniquen de forma segura. Esto es útil para widgets, iframes de terceros, y otras situaciones donde necesitas comunicación entre dominios diferentes.

cross-origin.js
Loading code...

Este ejemplo muestra cómo configurar comunicación cross-origin entre dos dominios diferentes. El iframe envía un mensaje al padre, y el padre valida el origen antes de responder. Este patrón es común en widgets y aplicaciones integradas.

Resumen: postMessage

Conceptos principales:

  • postMessage permite comunicación asíncrona entre diferentes contextos de ejecución
  • Los datos se serializan usando el algoritmo de clonación estructurada
  • La transferencia de objetos mueve objetos en lugar de copiarlos
  • event.origin indica el origen del mensaje y debe validarse siempre
  • event.source es una referencia al emisor para responder directamente

Mejores prácticas:

  • Siempre valida event.origin antes de procesar mensajes recibidos
  • Usa transferencia de objetos para datos grandes como ArrayBuffer
  • Sanitiza los mensajes recibidos para evitar inyección de código
  • Nunca ejecutes código dinámico con datos de postMessage
  • Usa MessagePort para canales de comunicación dedicados entre contextos