一、概念
1. window.name
- 在窗口还没被关闭之前,窗口载入的所有页面都共享同一个 window.name。
这个窗口可理解为 chrome 的一个 tab 标签页面,在该页面中跳转打开另一个页面后,这些页面都共享同一个 window.name。
- 每个页面都能读写 window.name。
这个可能就是安全问题的来源。
- 可以存储不超过 2M 的数据。
大小没测试过,MDN 上没说明,但有些文章有提到。
可参考:
https://developer.mozilla.org…
https://www.jianshu.com/p/43f…
2. postMessage
- 可以安全地实现跨源通信。
- 提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全。
语法:otherWindow.postMessage(message, targetOrigin, [transfer]);
事件:window.addEventListener("message", receiveMessage, false);
message 的属性有:data, origin, source
详情可参考:https://developer.mozilla.org…
二、安全问题比较
简称:
攻击者页面:A
被攻击者页面:B
1. window.name
- 可通过在 A 中嵌入 B,实现在 A 获取 B 的 window.name 的数据
- 如果 B 中存在 eval 执行 window.name 的代码,则存在 xss 安全问题,因为在 1 的基础上,A 可修改 window.name 的数据,从而导致 B 出现 xss 问题
2. demo 验证:
验证观点 1
A:http://localhost:3000/window.name.html
B:http://localhost:3001/window.name.html
效果如下图:
代码如下:
// A 页面:<head>
<meta charset="utf-8">
<title>window.name</title>
</head>
<body>
<script>
function iframeOnload() {var iframe = document.getElementById('iframe');
iframe.onload = function () {console.log('攻击者页面 log:被攻击者页面的数据已拿到:', iframe.contentWindow.name);
}
iframe.src = 'http://localhost:3000/xx.html'
}
</script>
<iframe id="iframe" src="http://localhost:3001/window.name.html" onload="iframeOnload()"></iframe>
</body>
// B 页面:<head>
<meta charset="utf-8">
<title>window.name</title>
</head>
<body>
<script>
window.name = '这是重要信息!!!'
console.log('被攻击者页面 log:', window.name)
</script>
</body>
验证观点 2
A:http://localhost:3000/window.name.html
B:http://localhost:3001/window.name.html
效果如下图:
先打开 A 页面,1 秒钟后,跳转到 B 页面,并执行 alert 代码
代码如下:
// A 页面
<head>
<meta charset="utf-8">
<title>window.name</title>
</head>
<body>
<script>
window.name = 'alert(/xss/)'
console.log(window.name)
setTimeout(() => {location.href = 'http://localhost:3001/window.name.html'}, 1000)
</script>
</body>
// B 页面
<head>
<meta charset="utf-8">
<title>window.name</title>
</head>
<body>
<script>
console.log('被攻击者页面 log:', window.name)
eval(window.name)
</script>
</body>
3. postMessage
不存在 window.name 的第一个问题,因为 postMessage 要指定源 targetOrigin,只要不是该源的,都收不到消息。同时,在接收消息的时候,也可以指定来源 origin,如果来源不对,则可不读取消息。
解决跨域问题,现在基本用 postMessage。
但 postMessage 也存在不足,比如只能指定特定的源,相对正常的 xhr 请求,增加代码量。
所以如果能开 cors,则基本是完美的跨域解决方案。
MDN 上安全问题的建议:
如果您不希望从其他网站接收 message,请不要为 message 事件添加任何事件侦听器。这是一个完全万无一失的方式来避免安全问题。
如果您确实希望从其他网站接收 message,请始终使用 origin 和 source 属性验证发件人的身份。任何窗口(包括例如 http://evil.example.com)都可以向任何其他窗口发送消息,并且您不能保证未知发件人不会发送恶意消息。但是,验证身份后,您仍然应该始终验证接收到的消息的语法。否则,您信任只发送受信任邮件的网站中的安全漏洞可能会在您的网站中打开跨网站脚本漏洞。
当您使用 postMessage 将数据发送到其他窗口时,始终指定精确的目标 origin,而不是 *。恶意网站可以在您不知情的情况下更改窗口的位置,因此它可以拦截使用 postMessage 发送的数据。