劫持产生的缘由和方式javascript
在网页开发的访问过程当中,http是咱们主要的访问协议。咱们知道http是一种无状态的链接。即没有验证通信双方的身份,也没有验证信息的完整性,因此很容易受到篡改。运营商就是利用了这一点篡改了用户正常访问的网页,插入广告或者其余一些杂七杂八的东西,达到盈利的目的。html
运营商的通常作法有如下手段:前端
1、对正常网站加入额外的广告,这包括网页内浮层或弹出广告窗口;html5
2、针对一些广告联盟或带推广连接的网站,加入推广尾巴;java
3、把咱们的站点非法解析到其余的站点,好比咱们在浏览器输入http://baidu.com,百度绑定的服务器ip地址是111.13.101.208,此时若是运营商的dns服务器将baidu.com的对应的ip地址改成qq的服务器ip 14.17.32.211,咱们输入http://baidu.com就会跳转到QQ的页面。node
以上的手段,经过原理概括为两种web
一、HTTP劫持浏览器
当咱们使用HTTP请求请求一个网站页面的时候,网络运营商会在正常的数据流中插入精心设计的网络数据报文,让客户端(一般是浏览器)展现“错误”的数据,一般是一些弹窗,宣传性广告或者直接显示某网站的内容,你们应该都有遇到过。作法1、2就是经过这种方式安全
二、DNS劫持服务器
咱们经过域名访问网页的时候,都须要经过DNS服务器把域名解析到对应的服务器地址上,而用户上网的DNS服务器都是运营商分配的。因此,在这个节点上,运营商能够随心所欲。作法3就是经过这种方式
对于以上的劫持方式,咱们做为前端的开发人员,经过javascript如何来作到有效的防御呢?
对于DNS劫持,因为发生在域名解析的时候,咱们没法控制,javascript更无能为力。咱们能作的就是拿起手机,投诉网络运营商,或者直接打工信部电话(12300)投诉。
http劫持防范
对于http劫持,运营商在实现上通常有如下几种作法
1、iframe嵌套展现原来正常网页
2、在原html中插入js,再经过js脚本安插广告
3、直接返回一个带广告的HTML
首先咱们来看页面被嵌入了 iframe 的状况。网络运营商为了尽量地减小植入广告对原有网站页面的影响,一般会经过把原有网站页面放置到一个和原页面相同大小的 iframe 里面去,那么就能够经过这个 iframe 来隔离广告代码对原有页面的影响。这种状况比较容易处理。咱们只要判断咱们的页面是否被嵌套在iframe中便可。Window对象中有两个属性self(指向自己的窗口),top(指向顶层的窗口)能够帮咱们来识别判断
咱们能够这样简单判断:
if (window.self != window.top) { var url = location.href; top.location = url; }
可是,有时候咱们在实际业务中,咱们的页面确实须要被嵌套在iframe中推广,上面的判断会致使页面没法嵌套,这时候咱们能够采用配置域名白名单的方式来解决
var avoidIframeNest = { whiteList : [], init: function(whiteList){ if(Object.prototype.toString.call(whiteList) == "[object Array]"){ this.whiteList = whiteList; } this.redirect(); }, redirect: function(){ if(self != top){ var parentUrl = document.referrer; //是否在白名单内 for(var i = 0 ,length = this.whiteList.length ; i < length ; ++ i){ var reg = new RegExp(this.whiteList[i],'i'); if(reg.test(parentUrl)){ return; } } //页面跳转 var url = location.href; top.location = url; } } }
经过配置白名单的方式,比较适合于咱们常常用到的域名,一般咱们会遇到这样的需求,合做方要求嵌套咱们的页面,咱们若是将合做方也加入到咱们白名单,一方面会致使白名单很长,另外一方面咱们须要手动去改代码,这样很不方便。这种状况,咱们能够在嵌套的url上加上域名的参数判断,要求嵌套页面带上域名参数,若是匹配,就认为合法。
var avoidIframeNest = { whiteList : [], init: function(whiteList){ if(Object.prototype.toString.call(whiteList) == "[object Array]"){ this.whiteList = whiteList; } this.redirect(); }, redirect: function(){ if(self != top){ var parentUrl = document.referrer; //是否在白名单内 for(var i = 0 ,length = this.whiteList.length ; i < length ; ++ i){ var reg = new RegExp(this.whiteList[i],'i'); if(reg.test(parentUrl)){ return; } } //判断URL是否带指定参数 var iframeDomain = this.getUrlParam('iframe_domain'); if(iframeDomain && parentUrl.indexOf(iframeDomain) != -1){ return; } //页面跳转 var url = location.href; top.location = url; } }, getUrlParam : function(key) { var regStr = "^.*[\\?|\\&]" + key + "\\=([^\\&]*)", url = location.href; reg = new RegExp(regStr,'i');; var ret = url.match(reg); if (ret != null) { return decodeURIComponent(ret[1]); } else { return ""; } } } avoidIframeNest.init(['baidu.com']);
经过上述的方法,基本能够解决iframe嵌套问题
对于js注入问题,通常都会在页面中插入图片标签,展现广告,诱导用户点击。针对这种方式,咱们能够经过监控页面插入的图片内容来检测。这里,咱们能够利用HTML5的新特性MutationObserver 和window下的DOMNodeInserted事件
Mutation Observer(变更观察器)是监视DOM变更的接口。当DOM对象树发生任何变更时,Mutation Observer会获得通知。具体的介绍能够参考:
能够监听某个 DOM 范围内的结构变化
http://www.cnblogs.com/jscode/p/3600060.html
DOMNodeInserted顾名思义,能够监听某个 DOM 范围内的结构变化,这个特性只有在firefox的低版本和webkit中使用,IE不支持,这里咱们能够做为低版本浏览器的兼容实现。
var validInsertImg = { httpReg : /^http:\/\/(.*\.baidu\.com|.*\.netwin\.com)\//, //验证非法图片 validIllegalityImg : function(src){ var httpReg = this.httpReg; return !httpReg.test(src); }, init : function(){ this.monitor(); }, monitor: function(){ var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; var mutationObserverSupport = !!MutationObserver; //html5监控变化属性 if(!mutationObserverSupport){ this.mutationListen(MutationObserver); }else{ this.insertedListen(); } }, insertedListen : function(){ var that = this; document.addEventListener('DOMNodeInserted', function(e) { var dom = e ? e.srcElement : document.documentElement; if (!dom.outerHTML) { return; } var imgList = (dom.nodeName.toUpperCase() == 'IMG') ? [dom] : dom.getElementsByTagName('img'); if (!imgList || imgList.length == 0) { return; } for (var i = 0; i < imgList.length; i++) { that.removeNode(imgList[i]); } }); }, mutationListen: function(MutationObserver){ var that = this; var observer = new MutationObserver(function(mutations){ mutations.forEach(function(mutation){ var nodes = mutation.addedNodes; for(var i = 0 ; i < nodes.length ; i++){ var node = nodes[i]; that.removeNode(node); } }) }) observer.observe(document, { subtree: true, childList: true }); }, //删除node removeNode : function(node){ if(node.nodeName.toUpperCase() == 'IMG'){ var src = node.src; if(this.validIllegalityImg(src)){ node.parentNode.removeChild(node); console.log('拦截可疑静态脚本:', node.src); } } } } validInsertImg.init(); body = document.getElementsByTagName('body')[0]; var img = document.createElement('img'); img.setAttribute('src','http://m.baidu.com/img/b') body.appendChild(img); var img1 = document.createElement('img'); img1.setAttribute('src','/YTRYTRY/A.PNG') body.appendChild(img1);
对于在返回html内容中插入广告,咱们能够借鉴注入的方式,进入页面就检测的img图片路径是否在白名单内
以上方法,都是针对运营商劫持的经常使用手段进行的一些黑科技操做。只能尽可能的减小劫持给咱们带来的负面影响。针对劫持问题,最好的办法就是全站升级https的方式,验证通信双方的身份以及信息的安全性。
可是https也不能彻底的解决劫持问题,若是https页面被劫持,浏览器会出现空白页面或者提示不安全,没法显示正常的内容。这也会影响到用户的体验。可是仍是推荐使用https,若是大部分的网站都使用了https,运营商的劫持没法达到目的,天然不会去作这样吃力不讨好的事情。