現象
運營商、黑客、瀏覽器廠商、手機廠商,通過某些方式篡改了使用者正常訪問的網頁,插入廣告或者其他一些雜七雜八的東西。在一些偏遠地區、雜牌運營商尤為常見。
網路劫持的原理
-
DNS 劫持 一般而言,使用者上網的DNS伺服器都是運營商分配的,所以在這個節點上,運營商可以為所欲為。 例如,訪問http://jiankang.qq.com/index.html,正常DNS應該返回騰訊的ip,而DNS劫持後,會返回一個運營商的中間伺服器ip。訪問該伺服器會一致性的返回302,讓使用者瀏覽器跳轉到預處理好的帶廣告的網頁,在該網頁中再通過iframe開啟使用者原來訪問的地址。
-
HTTP劫持 在運營商的路由器節點上,設定協議檢測,一旦發現是HTTP請求,而且是html型別請求,則攔截處理。 常見有兩種:
- 類似DNS劫持返回302讓使用者瀏覽器跳轉到另外的地址。(釣魚網站就是這麼幹)
- 在伺服器返回的HTML資料中插入js或dom節點(廣告)。(比較常見)
譬如下圖:
被劫持怎麼辦?
- 對於使用者來說,最最直接的就是向運營商投訴。
- 在html 上加上
<meta http-equiv="Cache-Control" content="no-siteapp">
<meta http-equiv="Cache-Control" content="no-transform " />
百度官方給的禁止轉碼宣告。 - 最有用的方式,使用HTTPS ,不讓資料那麼明顯的裸奔。 https 加了SSL協議,會對資料進行加密。
- 在開發的網頁中加入程式碼過濾,大概思路就是用JavaScript程式碼檢查所有的外鏈是否屬於白名單。
各種劫持的手段都有:
1. 直接返回一個帶廣告的HTML
2. 在原html中插入js,再通過js指令碼安插廣告;
3. iframe展示原來正常網頁。
js實際對抗
- 在window 監聽 DOMNodeInserted 事件,上報插入的dom、分析插入的dom 資訊。(通常匹配所有的url,逐個比較是否白名單域名,如果不是,則判定為劫持,上報的同時,移除dom.parentNode.removeChild(dom)); 剛插入的dom。小心誤傷。比較穩的操作是做監測統計,再決策預防。
ul.addEventListener('DOMNodeInserted',function (e) {
console.log(e.srcElement)
console.log(ul.childElementCount)
})
ul.addEventListener('DOMNodeRemoved',function (e) {
console.log(e.srcElement)
console.log(ul.childElementCount)
})
複製程式碼
如下:
function checkDivHijack(e) {
var dom = e ? e.srcElement : document.documentElement;
if (!dom.outerHTML) {
return; //e不是一個dom,只是插入一段文字
}
var imgList = (dom.nodeName.toUpperCase() == 'IMG') ? [dom] : dom.getElementsByTagName('img');
if (!imgList || imgList.length == 0) {
return;
}
var httpReg = /^http:\/\/(.*\.qq\.com|.*\.gtimg\.cn|.*\.qlogo\.cn|.*\.qpic\.cn)\//;
var base64Reg = /^data:image/;
var src;
var hijack = false;
for (var i = 0; i < imgList.length; i++) {
src = imgList[i].src;
if (!httpReg.test(src) && !base64Reg.test(src)) {
hijack = true;
break;
}
}
}
複製程式碼
但這樣也有漏洞,如果運營商通過div+style設定背景的方式顯示廣告圖,上述程式碼就無法檢查出來。
- 如果是iframe 插入的情況,比較self 和top是否相同來處理
function checkIframeHijack() {
var flag = 'iframe_hijack_redirected';
if (getURLParam(flag)) {
sendHijackReport('jiankang.hijack.iframe_ad', 'iframe hijack: ' + location.href);
} else {
if (self != top) {
var url = location.href;
var parts = url.split('#');
if (location.search) {
parts[0] += '&' + flag + '=1';
} else {
parts[0] += '?' + flag + '=1';
}
try {
top.location = parts.join('#');
} catch (e) {
}
}
}
}
複製程式碼
eg:
window.addEventListener('DOMNodeInserted', checkDivHijack);
function checkDivHijack(e) {
var html = e ? (e.srcElement.outerHTML || e.srcElement.wholeText) : $('html').html();
var reg = /http:\/\/([^\/]+)\//g;
var urlList = html.match(reg);
if (!urlList || urlList.length == 0) {
return;
}
reg = /^http:\/\/(.*\.qq\.com|.*\.gtimg\.cn|.*\.qlogo\.cn|.*\.qpic\.cn|.*\.wanggou\.com)\/$/;
var hijack = false;
for (var i = 0; i < urlList.length; i++) {
if (!reg.test(urlList[i])) {
hijack = true;
break;
}
}
}
複製程式碼
最終,根本解決辦法是使用HTTPS.