当前位置: 代码迷 >> 综合 >> window.postMessage()跨站请求的新方式
  详细解决方案

window.postMessage()跨站请求的新方式

热度:91   发布时间:2024-01-15 16:32:28.0

翻译链接:https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage


window.postMessage方法安全的开启了跨站请求的交互。正常情况下,有且仅有页面所在的位置有相同的协议,端口号和主机地址的时候,不同页面上的脚本才可以相互引用。window.postMessage在提供了一种可控的机制以一种安全的方式绕过这种限制。window.postMessage在被调用的时候,任意追加的必须被完整执行的脚本会在目标窗口上触发一个MessageEvent事件。MessageEvent事件包含了信息的类型,提供给window.postMessage作为第一个参数的数据属性,一个对应起始主文档调用window.postMessage窗口调用window.postMessage时的起始地址属性,以及一个来自调用window.postMessage窗口的资源属性。


语法:

otherWindow.postMessage(message,targetOrigin,[transfer]);

otherWindow:一个其它窗口的引用;这个引用可取得的,例如:一个iframe元素的contentWindow属性,一个window.open返回的对象,或者在window.iframes上被命名或者数字化的索引。

message:发送到另一个窗口的信息。这个数据可以使用结构化克隆算法序列化。这意味着你可以不用自己进行序列化就可以传输大容量的数据对象到不同的窗口上。

targetOrigin:指定otherWindow必须触发事件的目标窗口,要么使用"*“,要么使用一个URI。如果当被安排的触发事件的otherWindow文档的协议,主机地址或者端口号不能匹配提供的targetOrigin,这个事件将不会触发;当且仅当三个全部匹配的时候,事件才会触发。这个机制在信息被发送的时候提供了控制;例如,当postMessage用来传输一个密码的时候,来阻止恶意的第三方拦截密码。如果你知道otherWindow应该定位到哪里的话,一定要提供一个明确的targetOrigin,而不是*。不提供明确的目标会向任意恶意的站点泄漏你发送的数据。

transfer : [可选的],是一个同信息一起传递的Transfersble对象序列。这些信息的所有权将传输到不同的站点上并在发送端被弃用。


触发的事件

otherWindow可以通过执行下面的脚本了来监听发送的信息:

window.addEventListener("message", receiveMessage, false);function receiveMessage(event)
{if (event.origin !== "http://example.org:8080")return;// ...
}
触发信息的属性如下:

data:从另外一个窗口传递过来的对象

origin:调用psotMessage发送信息窗口的目标窗口。这个字符串使用"://"作为协议和存在的主机地址的连接符,并且“:”后面跟着可以显示并与给定协议默认端口号不同的端口号。例如这类地址可以是 https://example.org(端口号是443), http://example.net(端口号是80), http://example.com:8080 。注意:这个地址不能保证存在与这个窗口的当前或者未来,当postMessage被调用的时候,它也可能定位到一个不同的位置上。

source:发送信息窗口对象的一个引用;你可以建立跨站的跨窗口的交互。


安全提醒

如果你不希望从其它站点接收信息,不要添加任何的信息事件监听器。这是一个相当简单可靠的安全措施了。如果你希望从其它站点接收信息,要一直使用URI和资源属性来验证发送者的身份。任意窗口都可以给其它窗口发送信息,并且你无法保证未知的发送者是否将要发送恶意信息。然而,即便已经验证了身份,你仍然需要验证信息的合法性。换句话说,一个可信站点上安全通道只有发送可信信息的时候才可以打开你的站点上的一个跨站脚本通道。当你向其它窗口使用postMessage发送数据的时候,一直要指定目标地址,不是“*”。一个恶意站点可以使用你不知道的方式改变窗口的地址,因此它可以拦截使用postMessage发送的信息。


样例代码:

/** In window A's scripts, with A being on <http://example.com:8080>:*/var popup = window.open(...popup details...);// When the popup has fully loaded, if not blocked by a popup blocker:// This does nothing, assuming the window hasn't changed its location.
popup.postMessage("The user is 'bob' and the password is 'secret'","https://secure.example.net");// This will successfully queue a message to be sent to the popup, assuming
// the window hasn't changed its location.
popup.postMessage("hello there!", "http://example.org");function receiveMessage(event)
{// Do we trust the sender of this message?  (might be// different from what we originally opened, for example).if (event.origin !== "http://example.org")return;// event.source is popup// event.data is "hi there yourself!  the secret response is: rheeeeet!"
}
window.addEventListener("message", receiveMessage, false);
/** In the popup's scripts, running on <http://example.org>:*/// Called sometime after postMessage is called
function receiveMessage(event)
{// Do we trust the sender of this message?if (event.origin !== "http://example.com:8080")return;// event.source is window.opener// event.data is "hello there!"// Assuming you've verified the origin of the received message (which// you must do in any case), a convenient idiom for replying to a// message is to call postMessage on event.source and provide// event.origin as the targetOrigin.event.source.postMessage("hi there yourself!  the secret response " +"is: rheeeeet!",event.origin);
}window.addEventListener("message", receiveMessage, false);

注意

在其它任意窗口上任意时刻,任意窗口都可能进入这个方法,无论窗口上文档定位的位置是什么,来发送信息。因此,任意用来接收信息监听器都必须使用源地址和资源属性来验证发送信息者的信息。无论怎么强调都不过分的原则:验证源地址和资源属性失败可能会导致跨站脚本攻击。由于可能携带任意的异步执行脚本,当postMessage监听器句柄抛出一个异常的时候,调用postMessage的人是很难检测到的。分发事件的源地址属性值是不会影响到当前调用窗口的document.domain的值的。

仅对IDN主机名称,源地址的属性不是连续的Unicode或者域名编码;如果你需要从IDN站点上获取信息,需要对这个属性做兼容性检查,包括IDN和域名编码。这个值应该与IDN一直,但是现在你必须同时提交IDN和域名编码。当发送的窗口含有脚本或者数据的时候,URL就是源地址加载的URL.





  相关解决方案