劫持產生的原因和方式
在網頁開發的訪問過程中,http是我們主要的訪問協議。我們知道http是一種無狀態的連接。即沒有驗證通訊雙方的身份,也沒有驗證信息的完整性,所以很容易受到篡改。運營商就是利用了這一點篡改了用戶正常訪問的網頁,插入廣告或者其他一些雜七雜八的東西,達到盈利的目的。
運營商的一般做法有以下手段:
1、對正常網站加入額外的廣告,這包括網頁內浮層或彈出廣告窗口;
2、針對一些廣告聯盟或帶推廣鏈接的網站,加入推廣尾巴;
3、把我們的站點非法解析到其他的站點,比如我們在瀏覽器輸入http://baidu.com,百度綁定的服務器ip地址是111.13.101.208,此時如果運營商的dns服務器將baidu.com的對應的ip地址改為qq的服務器ip 14.17.32.211,我們輸入http://baidu.com就會跳轉到QQ的頁面。
以上的手段,通過原理歸納為兩種
1、HTTP劫持
當我們使用HTTP請求請求一個網站頁面的時候,網絡運營商會在正常的數據流中插入精心設計的網絡數據報文,讓客戶端(通常是瀏覽器)展示“錯誤”的數據,通常是一些彈窗,宣傳性廣告或者直接顯示某網站的內容,大家應該都有遇到過。做法1、2就是通過這種方式
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,運營商的劫持無法達到目的,自然不會去做這樣吃力不討好的事情。