關於互聯網流量劫持分析及可選的解決方案

一、何爲流量劫持

前不久小米等六家互聯網公司發表聯合聲明,呼籲運營商打擊流量劫持。流量劫持最直觀的表現,就是網頁上被插入了一些亂七八糟的廣告/彈窗之類的內容或者網址被無辜跳轉,多了推廣尾巴。好比下面這種:html

inter

頁面的右下角被插入了廣告。android

流量劫持整體來講屬於中間人攻擊(Man-in-the-Middle Attack,MITM)的一種,本質上攻擊者在通訊兩端之間對通訊內容進行嗅探和篡改,以達到插入數據和獲取關鍵信息的目的。目前互聯網上發生的流量劫持基本是兩種手段來實現的:正則表達式

  • 域名劫持:經過劫持掉域名的DNS解析結果,將HTTP請求劫持到特定IP上,使得客戶端和攻擊者的服務器創建TCP鏈接,而非和目標服務器直接鏈接,這樣攻擊者就能夠對內容進行竊取或篡改。在極端的狀況下甚至攻擊者可能僞造目標網站頁面進行釣魚攻擊。通常而言,用戶上網的DNS服務器都是運營商分配的,因此,在這個節點上,運營商能夠隨心所欲。例如,訪問http://jiankang.qq.com/index.html,正常DNS應該返回騰訊的ip,而DNS劫持後,會返回一個運營商的中間服務器ip。訪問該服務器會一致性的返回302,讓用戶瀏覽器跳轉到預處理好的帶廣告的網頁,在該網頁中再經過iframe打開用戶原來訪問的地址。瀏覽器

  • HTTP劫持/直接流量修改:在數據通路上對頁面進行固定的內容插入,好比廣告彈窗等。在這種狀況下,雖然客戶端和服務器是直接創建的鏈接,可是數據內容依然可能遭到野蠻破壞。例如在運營商的路由器節點上,設置協議檢測,一旦發現是HTTP請求,並且是html類型請求,則攔截處理。後續作法每每分爲2種,1種是相似DNS劫持返回302讓用戶瀏覽器跳轉到另外的地址,還有1種是在服務器返回的HTML數據中插入js或dom節點(廣告)。緩存

可以實施流量劫持的根本緣由,是HTTP協議沒有辦法對通訊對方的身份進行校驗以及對數據完整性進行校驗。若是能解決這個問題,則流量劫持將沒法輕易發生。安全

關於流量劫持的更多危害案例,能夠參考:如何看待小米等聯合聲明:呼籲運營商嚴格打擊流量劫持?服務器

二、如何防止劫持:主動防護型

2.1 HTTPS防止劫持

p2367211

HTTPS,是HTTP over SSL的意思,提到HTTPS就不得不先簡單描述一下SSL/TLS協議。SSL協議是Netscape在1995年首次提出的用於解決傳輸層安全問題的網絡協議,其核心是基於公鑰密碼學理論實現了對服務器身份認證、數據的私密性保護以及對數據完整性的校驗等功能。1999年IETF將SSL 3.0標準化,是爲TLS 1.0版本,目前TLS協議的最新版本是1.2版本,TLS 1.3標準正在制定中。爲了方便,下文將SSL/TLS協議都簡稱爲SSL協議。微信

SSL協議在HTTP請求開始以前增長了握手的階段,其粗略流程以下圖所示:網絡

https_1

  • 在SSL握手階段,客戶端瀏覽器會認證服務器的身份,這是經過「證書」來實現的,證書由證書權威(CA)爲某個域名簽發,能夠理解爲網站的身份證件,客戶端須要對這個證件進行認證,須要肯定該證書是否屬於目標網站並確認證書自己是否有效。最後在握手階段,通訊的雙方還會協商出一個用於加密和解密的會話密鑰。app

  • SSL握手階段結束以後,服務器和客戶端使用協商出的會話密鑰對交互的數據進行加密/解密操做,對於HTTP協議來講,就是將HTTP請求和應答通過加密以後再發送到網絡上。

因而可知,由於SSL協議提供了對服務器的身份認證,因此DNS劫持致使鏈接錯誤服務器的狀況將會被發現進而終止鏈接,最終致使DNS挾持攻擊沒法實現。此外SSL協議還提供數據的加密和完整性校驗,這就解決了關鍵信息被嗅探以及數據內容被修改的可能。

可是https也不是如此完美,雖然https解決了諸多安全問題,可是對性能也有着比較大的影響。一是用戶要從http跳轉到https,而且要多幾回TLS的握手,這會消耗必定的時間;二是服務器的壓力也會增長。除此以外若是使用全站的https,全部頁面裏面的嵌入資源都要改爲https,APP的程序也要進行相應的修改,CDN的全部節點也必須都支持https而且導入證書。因此全站https並非一件容易的事情,國外的Google、Facebook、Twitter早已支持全站https,但目前國內大多數公司都沒有采用全站https的方式,微信算是一個,前段時間百度表示已經支持全站的https了,前段時間我試了一下,百度主站在pc端和移動端都已經能夠自動跳轉到https了,可是近期發現移動端又恢復成http了,多是考慮訪問體驗問題。因而可知全站https應該是將來互聯網的趨勢。

2.2 繞過運營商localDNS,使用公共的DNS

讓用戶不使用運營商的local DNS改用114DNS,114DNS是國內最大的中立緩存DNS。推進用戶本身手工去改DNS顯然是不可行的,用戶10個有9個都不知道DNS是啥。那如何去讓用戶不使用運營商的local DNS呢?po主在騰訊的一個工程師的博客裏面看到有這麼一段話:「如何在用戶側構造域名請求:對於PC端的客戶端來講,構造一個標準的DNS請求包並不算什麼難事。但在移動端要向一個指定的LocalDNS上發送標準的DNS請求包,並且要兼容各類iOS和android的版本的話,技術上是可行的,只是兼容的成本會很高。」po主認爲這種方式暫且不談技術上是否可行,最大的問題應該是沒法確保公共DNS的穩定性。一旦公共的DNS遭到攻擊,將會致使全國性的故障,因此必定須要一個冗餘的方案,就是當公共DNS掛了後,能夠去向運營商的local dns去請求。

2.3 拋棄域名訪問方式,直接進行經過IP訪問

這種解決思路,po主瞭解的有兩種方式,第一種是httpdns,這種應該是目前DNS防劫持的主流方式,可是網上相關的介紹很是少,從騰訊員工的博客以及阿里朋友給的內部資料上看,如今騰訊和阿里都在用這種方式。另外一種是網宿的MAA解決方案。二者的原理大同小異,都是爲移動APP而量身定製的。

httpdns的原理大體就是在移動客戶端中加入一個域名解析的模塊,客戶端經過http的方式向企業的流量調度服務器請求ip,此時流量調度服務器會回根據用戶所在位置給用戶一個最優的ip。客戶端在獲取ip後直接用此ip來訪問所需站點資源。

這種方式看着確實很完美,可是對於通常的企業來講想要實現是一件極具挑戰的事情。

      一、客戶端須要進行必定的開發來知足客戶端的httpdns的請求;

      二、針對這個流量調度服務器須要本身開發一套全部節點的IP地址庫以及測速系統,才能夠保證將用戶引導的訪問最快的IDC節點上,而且對於使用了CDN加速的域名來講,這個IP地址庫是不可控的。

      三、如何保證高可用性以及不一樣運營商的用戶訪問到同一個HttpDNS的服務IP,用戶的訪問延遲?騰訊的作法是:HttpDNS經過接入了騰訊公網交換平臺的BGP Anycast網絡,與全國多個主流運營商創建了BGP互聯,保證了這些運營商的用戶可以快速地訪問到HttpDNS服務;另外HttpDNS在多個數據中心進行了部署,任意一個節點發生故障時均能無縫切換到備份節點,保證用戶解析正常。

      因此說httpdns是一個好的解決方案,可是用起來並不容易,而其投入產出比也只有在一些巨型的互聯網公司身上才能體現出來。

       再來講說網宿的MAA的解決方案,po主以爲和httpdns比較相似,前提是這個域名要使用網宿的CDN加速。網宿的MAA方案其實就是幫通常企業來解決上面httpdns的三點難點。


      一、客戶端插入網宿研發的SDK,向其鑑權服務器請求ip;

      二、網宿的鑑權服務器相似於httpdns中的流量調度服務器,由於CDN廠商原本就要負責調度CDN節點的資源,因此這點對他們來講很容易;

      三、如何保證高可用性以及不一樣運營商的用戶訪問到鑑權的服務器,用戶的訪問延遲?這點網宿的資料裏面沒有提到,po主我的猜想問題應該不大,由於對於一個CDN加速的域名來講,用戶原本就是要去網宿的鑑權的服務器的,而對於一個提供CDN服務的廠商來講,鑑權的服務器的高可用確定是要作好的。

      (以上絕對非軟文,po主以爲是個很好的解決思路,表示要向網宿徵收廣告費啊!!!)

       可是網宿的這個解決方案一樣存在問題:

       一、插入SDK的方式不少大企業可能沒法接受

       二、只能用於網宿加速的域名,其餘廠商的CDN節點的ip信息網宿不可控,這樣可能會被一個廠商綁架。

總的來講主動防護型的解決方案整體上看雖然能夠從根本上解決劫持問題,可是成本高,研發週期長,適合超大的互聯網公司,但未必適合全部企業。

三、如何防止劫持:被動監測型

       被動監測型主要是經過採集用戶訪問站點的網絡參數的方式來判斷是否遭到劫持,好比經過採集用戶訪問站點的ip地址來和咱們真實站點以及CDN節點的ip地址進行比對,判斷是否遭到DNS劫持,或者經過採集用戶訪問的URL和真實站點的URL進行比對,判斷是否遭到鏈路劫持。在PC時代對於被動監測型通常就是真機模擬這一種方式,可是在隨着移動互聯網的快速發展,針對於移動APP出現了一種插入SDK來監測的方式,這兩種方式採用的技術手段徹底不一樣,也各有利弊。這種小規模的檢測還行,實際應用中,基本仍是以方案 2 爲主。

例如這裏先對外網作檢測,上報被劫持的狀況:

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;
            }
        }
}

(注:過後發現這個url檢查不夠嚴謹,雖然劫持的狀況都能發現,但也把產品原有的一些正常插入作劫持誤報了。例如<a href="http://jiankang.qq.com" data-id="1">,不過這個是小細節,把正則表達式完善一下就ok了)

好比還能夠針對注入dom節點的狀況,初始化時作檢查,並且後續dom注入也作檢查。能夠檢查dom中是否含有白名單之外的http連接,若是有,就能夠斷定爲http劫持。

四、防劫持的實際案例與小技巧

4.1 重寫 document.write

通常來講,工程師作低成本的防護須要先找到運營商設置的劫持規律。好比這個 case 中查到是運營商採用 document.write 改寫了頁面內容或者 dom 節點,那麼咱們能夠簡單粗暴的將document.write重寫爲空函數:

var oldDocwrite = document.write,newDocwrite = function(str){};
if(oldDocwrite.apply){
    hao360.docWrite = function(str){
        oldDocwrite.apply(document,arguments);
    }
}else{
    hao360.docWrite = oldDocwrite;
}
document.write = newDocwrite;

顯然這種方案有些太不優雅,但卻也頗有效。

第二個方案是豆瓣 http://www.douban.com/js/do.js 尾部用到的白名單過濾方案:

// @TODO 臨時應對劫持 by dexteryy
var _write = _doc.write,
_white_list = {
    'douban.com': 1,
    'douban.fm': 1,
    'google.com': 1,
    'google-analytics.com': 1,
    'googleadservices.com': 1
},
// 統計劫持狀況
_hijack_stat = function(reason, env){
    var img = new Image();
    img.onload = function(){};
    img.src = "http://www.douban.com/j/except_report?kind=ra022&reason="
        + encodeURIComponent(reason)
        + "&environment=" + encodeURIComponent(env);
},
_RE_SCRIPTS = /<script.*?src\=["']?([^"'\s>]+)/ig,
_RE_DOMAIN = /(.+?)\.([^\/]+).+/;
_doc.write = function(str){
    try {
        var s, safes = [], unkowns = [];
        while (s = _RE_SCRIPTS.exec(str)) {
            if (_white_list[(_RE_DOMAIN.exec(s) || [])[2]]) {
                safes.push(s);
            } else {
                unkowns.push(s);
            }
        }
        if (unkowns.length > 0) {
            _hijack_stat([unkowns[0], safes[0] || ""].join("~_~"), location.href);
        }
        try {
          _write.call(this, str);
        } catch (ex) {
          _write(str);
        }
    } catch (ex) {
        _write(str);
        _hijack_stat(ex.name + ":" + ex.message, location.href);
    }
};

上兩個方案可視項目狀況而定,各有利弊。

可是若是運營商換個方案,不採起document.write,那真就要很難鬥了。固然若是無恥的DNS劫持始終跟着流量入口頁的對策升級方案,那我們仍是洗洗睡吧。

4.2 更過度的整站劫持

另外說一句一些地方的小運營商,如四川南充某運營商所管轄的這部分地域,直接將hao.360.cn指向本身的導航頁面了,雖然樣子仍是按期緩存360導航的頁面,但其中內容卻已被改動多半。對於這些的確的用戶反饋,咱們只能表示無奈,且也嘗試付諸於法律手段。

4.3 結語

因此結論就是運營商是用戶的「最後一千米」,咱們只能盡力而爲也就是了。

Refer:

[1] 使用HTTPS防止流量劫持

https://yq.aliyun.com/articles/2666

[2] 關於互聯網流量劫持分析及可選的解決方案

http://www.jianshu.com/p/eff9553c8b64#

[3] 關於啓用 HTTPS 的一些經驗分享(一)

https://imququ.com/post/sth-about-switch-to-https.html

[4] 關於啓用 HTTPS 的一些經驗分享(二)

https://imququ.com/post/sth-about-switch-to-https-2.html

[5] 運營商DNS劫持的那些事兒 做者:irideas

http://bit.ly/1PrpIa6

[6] 【HTTP劫持和DNS劫持】騰訊的實際業務分析

http://www.cnblogs.com/kenkofox/p/4919668.html

[7] 瘋狂的淘寶客:一次搜索引擎流量劫持事件分析

http://bit.ly/1LkZmmd

[8] dig與dns基本理論——解析和緩存

http://www.juvenxu.com/2014/08/04/dig-and-basic-dns-resolving-and-cache/

[9] 詳解https是如何確保安全的?

http://bit.ly/260gPgh

相關文章
相關標籤/搜索