赞
踩
相信大部分同学都接到过"点击页面某个地方从新窗口打开一个页面"的需求, 一般这个需求都有一下两种处理方式来完成:
1. a标签加上target="_blank"属性
2. window.open(url)
但是以上两种处理方式可能会遇到已下问题:
1. 用户账户或个人信息被盗取
2. 打开新页面后, 原窗口页面出现卡顿情况
经过一顿猛操作去验证, 发现新窗口打开新页面可能出现安全问题和性能问题, 原因是Window对象的opener属性
(关于window opener属性解释摘自菜鸟教程)
废话不多说, 我们通过代码来检验
<html><head> <meta charset="utf-8"> <title>原始页面title>head><body><h1 style="color: red;" id="alert-tips">h1><a href="./target.html" target="_blank">1.target _blanka><br><a id="btn-diff" href="javascript:void(0);">2.window.opena><br><a id="btn-open" href="javascript:void(0);">3.window.open并且设置opener为nulla><br><a id="btn-iframe" href="javascript:void(0);">4.新建iframe打开a><br><a href="./target.html" target="_blank" rel="noopener">5.target _blank noopenera><p id="random-number">0.6890402854817066p><script> window.onload = function () { var randomItem = document.getElementById('random-number'), btnDiff = document.getElementById('btn-diff'), btnIframe = document.getElementById('btn-iframe'), btnOpen = document.getElementById('btn-open'); console.log('DOM READY'); btnDiff.addEventListener('click', function () { window.open('./target.html'); }); btnIframe.addEventListener('click', function () { iframeOpen('./target.html'); }); btnOpen.addEventListener('click', function () { var child = window.open('./target.html'); child.opener = null; }) var setRandom = function () { randomItem.innerHTML = Math.random(); setTimeout(setRandom, 10); } checkHash(); setRandom(); } window.onhashchange = checkHash; function checkHash() { if (location.hash.indexOf('#hack') >= 0) { document.getElementById('alert-tips').innerHTML = '你被HACK了啊!这个页面的地址已经变了!'; } } /** * Opens the provided url by injecting a hidden iframe that calls * window.open(), then removes the iframe from the DOM. * * @param {string} url The url to open * @param {string} [strWindowName] * @param {string} [strWindowFeatures] * @returns {Window} */ function iframeOpen(url, strWindowName, strWindowFeatures) { var iframe, iframeDoc, script, openArgs, newWin; iframe = document.createElement('iframe'); iframe.style.display = 'none'; document.body.appendChild(iframe); iframeDoc = iframe.contentDocument || iframe.contentWindow.document; openArgs = '"' + url + '"'; if (strWindowName) { openArgs += ', "' + strWindowName + '"'; } if (strWindowFeatures) { openArgs += ', "' + strWindowFeatures + '"'; } script = iframeDoc.createElement('script'); script.type = 'text/javascript'; script.text = 'window.parent = null; window.top = null;' + 'window.frameElement = null; var child = window.open(' + openArgs + ');' + 'child.opener = null'; iframeDoc.body.appendChild(script); newWin = iframe.contentWindow.child; document.body.removeChild(iframe); return newWin; }script>body>html>
(如上为origin.html)
<html><head> <meta charset="utf-8"> <title>目标页面title>head><body><p id="content">p><script> window.onload = function(arguments) { hackOrigin(); superFunJankTime(); }; function hackOrigin() { var content = document.getElementById('content'); if (window.opener) { // console.log('window.opener', window.opener) // console.log('opener', opener) opener.location = './origin.html#hack'; content.innerHTML = 'HACK成功,再看看上个TAB?'; } else { content.innerHTML = 'HACK失败。。。。'; } } function superFunJankTime() { var start = Date.now(); while (Date.now() - start < 1000); setTimeout(superFunJankTime, 1000); }script>body>html>
(如上为target.html)
经过动手尝试后, 发现原来第1,2个跳转链接是可以通过window.opener.location=newUrl来重写原页面的url的, 即使与原窗口的页面不同域; 利用这种方式原始页面窗口的链接就被悄悄换成钓鱼页面的地址. 一般人也不会关注到url悄悄变了, 因此就会造成安全性问题.
那么, 透过上面的代码, 不难发现, 这个安全性问题可以通过3,4,5可以解决安全性问题; 但是, 如果透过3,4去发生跳转的话, 我们会发现一开始的页面上的一串鬼畜随机数, 偶尔有点小卡顿; 这个随机数是通过一个定时器生成的, 每隔一段时间就有一个持续循环. 这个循环在阻塞新页面本身的js线程的同时, 也阻止了opener(即打开了新页面的原始页面)里的js线程. 若这种类似的操作很多的话, 原始页面的可能会卡死;
问题来了, 为啥新窗口会影响原来窗口的线程呢? chrome不是每个标签页一个单独的进程吗? 进程内包含若干线程吗?
chrome确实有不同的标签页面使用不同的进程和线程, 但是有个例外, 通过a标签的target="_blank"属性, 或者window.open(url)在新窗口中打开页面, 会与原窗口共用进程和线程. 这是因为opener中含有DOM信息, 两个进程中同时hold住这个DOM信息, 在多个进程下很难控制, 于是放到一个进程里面
当然, 上述的问题也有对应的解决方案: 通过a标签上添加noopener属性, 可将新打开窗口的opener置为空. 这个方案可解决除IE以外的安全问题和所有现代浏览器的性能问题
总结一下:
1. 使a标签noopener属性, 将新打开的窗口opener置为空(推荐)
特点: 可解决除IE外的安全问题和所有现代浏览器的性能问题
2. window.open并设置opener为空
特点: 可解决所有除safari下的安全问题, 无法解决性能问题
var newWindow = window.open();newWindow.opener = null;newWindow.location = 'url';newWindow.target = '_blank';
3. 新建iframe中打开新窗口, 然后关掉iframe
特点: 可解决safari下的安全问题, 无法解决性能问题
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。