HTML - Mensajería web

-

Tipos de Mensajería Web

Mensajería de Canal

La Mensajería de Canal permite que dos o más contextos de navegación (ventanas, pestañas o iframes) se comuniquen entre sí incluso si están en diferentes orígenes. Utiliza un objeto de canal de mensajes para establecer un enlace entre los contextos y enviar mensajes entre ellos.

La Mensajería de Canal es útil cuando necesitas establecer un enlace directo y bidireccional entre contextos. Por ejemplo, puedes usarla para permitir que una ventana principal controle un iframe o para que dos pestañas compartan datos en tiempo real.

Consejo: Ejemplo de Mensajería de Canal

// En el primer contexto de navegación
const channel = new MessageChannel();
const port1 = channel.port1;
const port2 = channel.port2;

// Enviar port2 al segundo contexto de navegación
window.postMessage('init', '*', [port2]);

// Escuchar mensajes en port1
port1.onmessage = (event) => {
  console.log('Mensaje recibido:', event.data);
};

// En el segundo contexto de navegación
window.onmessage = (event) => {
  if (event.data === 'init') {
    const port2 = event.ports[0];
    port2.onmessage = (event) => {
      console.log('Mensaje recibido:', event.data);
    };
    port2.postMessage('¡Hola desde el segundo contexto!');
  }
};

Mensajería entre Documentos

La Mensajería entre Documentos, también conocida como postMessage, permite que diferentes contextos de navegación (ventanas, pestañas o iframes) se comuniquen incluso si están en diferentes orígenes. Utiliza el método postMessage() para enviar un evento de mensaje al contexto receptor, que luego puede manejarlo usando un listener de eventos.

La Mensajería entre Documentos es útil cuando necesitas mensajes unidireccionales entre contextos. Por ejemplo, cuando una ventana principal necesita que un iframe haga algo o cuando una pestaña necesita enviar datos a otra pestaña.

Consejo: Ejemplo de Mensajería entre Documentos

// En el contexto emisor
const targetWindow = window.parent; // O window.opener, o document.getElementById('myIframe').contentWindow
targetWindow.postMessage('¡Hola desde el emisor!', '*');

// En el contexto receptor
window.addEventListener('message', (event) => {
  if (event.origin === 'http://example.com') {
    console.log('Mensaje recibido:', event.data);
  }
});

Web Workers

Los Web Workers ejecutan scripts en segundo plano sin afectar el rendimiento de la página. Permiten realizar procesamiento pesado en un hilo separado para que la página principal se mantenga receptiva.

Los Web Workers son útiles para tareas que llevan tiempo como cálculos complejos y procesamiento de datos.

Consejo: Ejemplo de Web Workers

// En el script principal
const worker = new Worker('worker.js');
worker.onmessage = (event) => {
  console.log('Mensaje recibido del worker:', event.data);
};
worker.postMessage('¡Hola desde el script principal!');

// En worker.js
self.onmessage = (event) => {
  console.log('Mensaje recibido en el worker:', event.data);
  self.postMessage('¡Hola desde el worker!');
};

Consideraciones de seguridad

Al usar la Mensajería Web, tenga en cuenta los posibles riesgos de seguridad y siga las mejores prácticas para una implementación segura.

Uno de los principales riesgos con la Mensajería Web son los ataques de scripts entre sitios (XSS). Si no valida y desinfecta los datos que recibe a través de mensajes, un atacante podría enviar código dañino que se ejecutaría en su página. Esto podría permitirles robar datos sensibles, realizar acciones en nombre del usuario o tomar el control de la página.

Consejo: Ejemplo de código HTML para riesgo de XSS

<p>Este    es   un   párrafo   con    espacios   extra.</p>

Cuando un navegador renderiza este código, mostrará el texto como:

Este es un párrafo con espacios extra.

Para prevenir ataques XSS, siempre valide y desinfecte cualquier dato recibido a través de mensajes. No asuma que los datos son seguros solo porque provienen de otra ventana o trabajador que usted controla. Trate todos los datos recibidos como entrada no confiable y manéjelos en consecuencia.

Otro riesgo es cuando se usa la Mensajería Web para comunicarse entre diferentes orígenes. Tenga cuidado con los orígenes en los que confía. Si acepta mensajes de cualquier origen sin verificar, un atacante podría enviar mensajes desde un sitio dañino que engañen a su página para que haga algo que no debería. Para reducir este riesgo, siempre verifique la propiedad origin del evento del mensaje y solo acepte mensajes de orígenes confiables.

Al usar la Mensajería de Canales, sepa que cualquier contexto que reciba un puerto de mensaje tiene acceso completo a ese canal. Esto significa que pueden enviar mensajes en el canal e incluso cerrarlo. Así que solo comparta puertos de mensaje con contextos en los que confíe plenamente.

Con los Web Workers, un riesgo principal es que un trabajador podría colgarse o usar demasiados recursos si se queda atrapado en un bucle infinito o intenta hacer demasiado trabajo. Para prevenir esto, use el método Worker.terminate() para detener un trabajador inmediatamente si es necesario. Estructure su código de trabajador para responder a los mensajes rápidamente y evite tareas de larga duración.

Consejo: Usando el método Worker.terminate()

var miTrabajador = new Worker('trabajador.js');

// Terminar el trabajador después de 5 segundos
setTimeout(function() {
    miTrabajador.terminate();
}, 5000);

Este código detiene el trabajador después de un tiempo especificado para prevenir el uso excesivo de recursos.

Aquí hay algunas mejores prácticas para una Mensajería Web segura:

  • Siempre valide y desinfecte los datos de los mensajes recibidos
  • Solo acepte mensajes de orígenes confiables
  • Tenga cuidado al compartir puertos de mensaje
  • Use Worker.terminate() para detener trabajadores que se comporten mal
  • Mantenga su código de trabajador enfocado y receptivo
  • No use la Mensajería Web para compartir datos sensibles a menos que sea necesario
  • Use encriptación para datos sensibles si es necesario
  • Pruebe exhaustivamente su código de Mensajería Web para detectar posibles vulnerabilidades

Soporte de navegadores

La mayoría de los navegadores web modernos admiten la Mensajería Web. Esto incluye Chrome, Firefox, Safari e Internet Explorer 11 y versiones superiores. La API de WebSocket y los Eventos enviados por el servidor, utilizados para la mensajería en tiempo real, también son compatibles con la mayoría de los navegadores.

Los navegadores más antiguos pueden tener soporte limitado o nulo para algunas funciones de Mensajería Web. Por ejemplo:

  • Internet Explorer 8 y versiones anteriores no admiten el método window.postMessage() utilizado en la Mensajería entre documentos.
  • Internet Explorer 10 y versiones anteriores no admiten Web Workers.
  • Internet Explorer no admite la Mensajería de canales.

Si necesitas dar soporte a navegadores más antiguos, es posible que debas usar soluciones alternativas o respaldos. Algunas opciones incluyen:

  • Para la Mensajería entre documentos en versiones antiguas de IE, puedes usar una biblioteca como easyXDM que proporciona un transporte alternativo utilizando iframes y la propiedad location.hash.

Consejo: Ejemplo usando la biblioteca easyXDM

// En el contexto de envío
var socket = new easyXDM.Socket({
  remote: "http://example.com/remote.html",
  onMessage: function(message, origin) {
    console.log("Mensaje recibido:", message);
  }
});
socket.postMessage("Hola desde el remitente!");

// En el contexto de recepción (remote.html)
var socket = new easyXDM.Socket({
  onMessage: function(message, origin) {
    console.log("Mensaje recibido:", message);
  }
});
  • Para Web Workers en IE10 y versiones anteriores, puedes detectar el soporte y proporcionar una alternativa que ejecute el script en el hilo principal en su lugar.

Consejo: Ejemplo de alternativa para Web Workers

if (typeof(Worker) !== "undefined") {
  // Web Workers soportado, usar worker
  var worker = new Worker("worker.js");
} else {
  // Web Workers no soportado, ejecutar script en el hilo principal
  var workerFallbackScript = document.createElement('script');
  workerFallbackScript.src = 'worker.js';
  document.body.appendChild(workerFallbackScript);
}

Para alternativas de Mensajería de canales: Puedes usar otras técnicas de mensajería como la Mensajería entre documentos o una solución basada en servidor cuando la Mensajería de canales no esté disponible.

Es importante probar tu código de Mensajería Web en todos los navegadores que planeas soportar. Usa la detección de características para verificar el soporte y proporciona alternativas apropiadas cuando sea necesario para ofrecer a todos tus usuarios una buena experiencia.

El panorama de los navegadores siempre está mejorando. La mayoría de los usuarios ahora usan navegadores modernos con muchas funciones. Así que, aunque las alternativas para navegadores antiguos aún pueden ser necesarias en ocasiones, se están volviendo menos críticas con el tiempo a medida que el uso de navegadores heredados continúa disminuyendo.

Ejemplos prácticos

Aplicación de chat en tiempo real

Crear una aplicación de chat en tiempo real muestra el poder de la Mensajería Web. Aquí tienes una guía para crear una aplicación de chat básica usando Mensajería de Canal y Web Workers:

  1. Configura la estructura HTML para la interfaz de chat, incluyendo un campo de entrada para mensajes y un contenedor para mostrar el historial del chat.

  2. Crea un nuevo Web Worker para manejar el procesamiento y almacenamiento de mensajes. Este worker recibirá mensajes del script principal, los almacenará y enviará de vuelta el historial de chat actualizado cuando se solicite.

Consejo: Web Worker para Chat (chat_worker.js)

let chatHistory = [];

self.onmessage = (event) => {
  if (event.data.type === 'newMessage') {
    chatHistory.push(event.data.message);
    self.postMessage({ type: 'updateHistory', history: chatHistory });
  } else if (event.data.type === 'getHistory') {
    self.postMessage({ type: 'updateHistory', history: chatHistory });
  }
};
  1. En el script principal, crea un nuevo canal de Mensajería de Canal y envía uno de los puertos al worker.

Consejo: Script Principal (script.js)

const chatWorker = new Worker('chat_worker.js');
const channel = new MessageChannel();
chatWorker.postMessage({ type: 'init', port: channel.port1 }, [channel.port1]);
  1. Configura los event listeners para la entrada de mensajes y la comunicación del canal. Cuando se ingresa un nuevo mensaje, envíalo al worker a través del canal. Escucha las actualizaciones del historial del worker y muéstralas en la interfaz de chat.

Consejo: Event Listeners y Mensajería

const messageInput = document.getElementById('messageInput');
const chatContainer = document.getElementById('chatContainer');

channel.port2.onmessage = (event) => {
  if (event.data.type === 'updateHistory') {
    const messagesHTML = event.data.history.map(message => `<p>${message}</p>`).join('');
    chatContainer.innerHTML = messagesHTML;
  }
};

messageInput.addEventListener('keypress', (event) => {
  if (event.key === 'Enter') {
    const message = messageInput.value;
    channel.port2.postMessage({ type: 'newMessage', message });
    messageInput.value = '';
  }
});
  1. Cuando la página carga, solicita el historial inicial de chat al worker.

Consejo: Solicitar historial inicial de chat

chatWorker.postMessage({ type: 'getHistory' });

Con estos pasos en su lugar, tienes una aplicación de chat en tiempo real básica que usa Mensajería Web para comunicarse entre el script principal y el Web Worker.

Comunicación entre dominios

La Mensajería Web también puede permitir la comunicación entre diferentes dominios usando Mensajería entre Documentos:

  1. En la ventana padre, crea un iframe con el atributo src establecido en la URL de la página con la que quieres comunicarte.

Consejo: Ventana Padre (parent.html)

<iframe id="myIframe" src="https://example.com/iframe.html"></iframe>
  1. En el script del iframe, escucha los eventos de mensaje y manéjalos según el formato de datos esperado.

Consejo: Script del Iframe (iframe.js)

window.addEventListener('message', function(event) {
  if (event.origin === 'https://example.com' && event.data.type === 'userData') {
      console.log('Datos de usuario recibidos:', event.data.value);
   }
});
  1. En el script de la ventana padre, obtén una referencia a la ventana del iframe y envía mensajes usando el método postMessage().

Consejo: Script de la Ventana Padre (parent.js)

const iframeWindow= document.getElementById('myIframe').contentWindow;
const userData= { name:'John Doe', age :30 };

document.getElementById("myIframe").addEventListener("load", () =>{
   iframeWindow.postMessage({type :'userData ', value :userData },'https://example.com ');
});

Con esta configuración, la ventana padre puede enviar mensajes que contienen datos de usuario al iframe, y el iframe puede escuchar estos mensajes y procesarlos en consecuencia. Esto permite una comunicación segura entre dos contextos aunque estén alojados en diferentes dominios.

Al usar Mensajería entre Documentos, valida el origen de los mensajes recibidos asegurándote de que provengan de fuentes confiables para prevenir ataques de cross-site scripting (XSS) y otras vulnerabilidades de seguridad.