【前端持久化】evercookie

引言:javascript

前端持久化就是要將數據永久的保存在前端,讓數據難以刪除或者刪除後可以從新恢復。存儲的數據能夠理解爲是一種 「殭屍數據」,下面介紹一種前端持久化方法 -- evercookie。php

一.evercookie簡介:html

evercookie是由Samy Kamkar(美國白帽黑客、安全研究員)開發的一組jsApi,它的目的在於持久化cookie,即便用戶清除標準cookie、Flash cookie等以後依然可以獲取設置過的數據,而且從新恢復清除掉的cookie(比較狹隘,本質上是恢復全部維度,一個從新寫的動做)。前端

二.evercookie原理:java

evercookie的原理很簡單,就是將數據寫入瀏覽器各個維度,獲取的時候再從各個維度中讀出來,不管用戶怎樣清洗,只要其中一個維度有數據就能夠獲得數據。比較強大的地方在於:1.存儲的維度很是多,用戶很難清理;2.取數據的時候會將已經清除的數據從新恢復,名副其實的殭屍cookie。web

下面介紹下存儲的維度以及讀取數據的方式和思路:chrome

evercookie 存儲數據的維度:數據庫

1.標準HTTP Cookie:canvas

evercookie會將數據存在 document.cookie 中,獲取的時候直接獲取就能夠了,沒什麼可說的,這部分數據是比較容易被清除的,好比瀏覽器清除cookie、js腳本設置等,分享關於cookie的兩個點:跨域

  • http請求自帶本域以及根域下全部cookie,CSRF的根源就在這裏;
  • js設置cookie默認在當前域以及當前路徑下, cookie通常都會跨路徑使用,必定注意設置path字段;

2.Flash Cookie:

 evercookie提供了一個flash文件,使用的時候會將數據存儲在flash的本地對象中,只有刪除對應的flash存儲文件才能夠清除,把flash文件反編譯了一下,獲得了AS源碼:

shared = sharedobject.getlocal("evercookie");
if (everdata) {
    var newdata = everdata.split("=");
    var str = shared.data.cookie;
    var results = str.split("&");
    var i = 0;
    while (i < results.length) {
        var elem = results[i].split("=");
        if (elem[0] != newdata[0]) {
            everdata = everdata + ("&" + results[i]);
        }
        i++;
    }
    shared.data.cookie = everdata.replace("\\", "\\\\");
    shared.flush();
}
flash.external.ExternalInterface.call("_evercookie_flash_var", shared.data.cookie);

存數據的時候調用swfObject中的接口存入便可,能夠看下js源碼:

this.evercookie_lso = function (name, value) {
    var div = document.getElementById("swfcontainer"),
        flashvars = {},
        params = {},
        attributes = {};
    if (div===null || div === undefined || !div.length) {
        div = document.createElement("div");
        div.setAttribute("id", "swfcontainer");
        document.body.appendChild(div);
    }

    if (value !== undefined) {
        flashvars.everdata = name + "=" + value;
    }
    params.swliveconnect = "true";
    attributes.id        = "myswf";
    attributes.name      = "myswf";
    
    //寫入flash
    swfobject.embedSWF(_ec_baseurl + _ec_asseturi + _ec_swf_file_name, "swfcontainer", "1", "1", "9.0.0", false, flashvars, params, attributes);
};

 而flash加載後會使用 flash.external.ExternalInterface.call("_evercookie_flash_var", shared.data.cookie) 調用window下的javascript方法 _evercookie_flash_var 將數據傳給js,對js來講就是讀取flash數據。

var _global_lso;
function _evercookie_flash_var(cookie) {
    _global_lso = cookie;

    // remove the flash object now
    var swf = document.getElementById("myswf");
    if (swf && swf.parentNode) {
        swf.parentNode.removeChild(swf);
    }
}

window._evercookie_flash_var = _evercookie_flash_var;

 

3.localStorage:localStorage是HTML5的一個新特性,能夠將數據永久存儲在本地,獲取時沒有窗口的限制,同域下便可獲取,能夠調用localStorage的接口來清除,瀏覽器直接清除緩存數據也能清掉;

4.sessionStorage:同localStorage相似,生存週期是當前對話,瀏覽器關閉從新打開後消失;

5.globalStorage:同localStorage相似,一樣是永久存儲在本地,目前只有 Firefox48 以上才支持;

6.openDatabase:HTML5的WebSQL數據庫,能夠理解爲本地存儲 Local Storage 和 Session Storage 的一個增強,用來操縱大量結構化數據,因爲各個瀏覽器實現緣由,WebSQL規範已經被廢棄掉了;

7.IndexedDB:瀏覽器內置的一種數據庫,永久保存數據,IndexDB與WebSQL比較,IndexedDB更像是一個NoSQL數據庫,而WebSQL更像是關係型數據庫,使用SQL查詢數據。

8.圖片緩存數據存儲:

evercookie利用了圖片的緩存進行了存儲,簡單介紹下:

  1. 寫數據的時候根據key構造一個http請求,將值經過document.cookie傳給後臺;
  2. 後臺根據cookie中傳入的值按照每三位生成一個像素點的方式生成一張200個像素點的png圖片(最大爲600個字符),而且設置緩存到前端;
  3. 讀數據的時候一樣根據key構造相同的http請求,獲取緩存的圖片並用canvas解析出對應的像素點,恢復出數據。

這裏面能夠看出兩點,一個 evercookiejs 設置的圖片存儲支持的最大數據爲600個字符,二是此種方式必須使用canvas進行解析,有兼容性要求。這種方式顯然能夠經過瀏覽器清除緩存直接清掉了。

9.ETag存儲:

ETag存儲也要依靠後臺,利用的原理主要是當瀏覽器第一次訪問一個請求的時候若是服務器響應設置ETag標籤,瀏覽器第二次訪問會自動帶上一個IF-NONE-MATCH上來(跟ETag設置的值相同),因此只要把數據值存在ETag上,取數據的時候直接去後臺查連接上的 IF-NONE-MATCH 字段就能夠了, 跟上面png圖片緩存相似。

10.web Cache:看evercookie的思路是對 http cookie 的一種增強,至關於經過後臺對cookie設置個過時時間,evercookie提供的腳本感受有問題。

11.silvelright客戶端存儲:

silvelright也是一種本地存儲方式,能夠將數據直接存在本地,相似於flash能夠跨瀏覽器獲取,須要安裝silverlight插件。evercookie提供了相應的編譯文件能夠經過sliverlight進行存取數據,對sliverlighr不大瞭解,有興趣的同窗能夠研究一下。

12.java應用程序本地存儲:經過使用JNLP調用Java Applet的能力將數據存在了本地文件中,代碼量比較大不細分析了,反編譯了jar包以及class文件,放在文件中有興趣的能夠看下。

13.IE的userData存儲:

userData是IE獨有的一種存儲方式,能夠經過XML、HTML標籤將數據存儲在本地,通常支持IE5以上,官方文檔看存儲數據大小通常在單個域名640k左右,使用方法很簡單

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <style>
        .userData{
            behavior: url(#default#userdata)
        }
    </style>
</head>
<body>
    <div id="userData" class="userData"></div>
</body>
<script>
    var persistDom = document.getElementById("userData");
    function set(name, value){
        persistDom.setAttribute(name, value);
        persistDom.save(name);
    }
     function get(name){
        persistDom.load(name);
        return persistDom.getAttribute(name)
    }
    set("devinn", 1990);
    window.console && console.log(get("devinn"));//1990
</script>
</html>

14.window.name:

window.name是window的一個很特殊的屬性,能夠設置,有兩個特色:

  • window.name設置後刷新頁面不會消失;
  • iframe從一個src跳轉到另外一個src 獲取contentWindow.name 不會發生變化;

evercookie主要是利用了上面的一點,只要頁面不刷新,頁面隨便清理都不會發生變化(奇特的是放在iframe裏面清緩存就能夠清掉 TT)。

window.name常常用於跨域通訊,順便說下window.name跨域通訊原理:

iframe src 從 A.html跳轉到 B.html 的時候 window.name 是不變的, 因此若是一個域的頁面想跨域獲取數據能夠設置一個iframe 先將src指向想要獲取數據的域頁面(此頁面將想要傳遞的數據放在window.name中, ps:此時因爲跨域沒法獲取iframe的contentWindow),以後src指向本身域名下的一個頁面(已變成同域)經過iframe的contentWindow便可獲取;  

跨域獲取注意兩個關鍵點:

  • 必須放在iframe中;
  • 必須使用name屬性(console了一下contentWindow,測試了幾個其餘屬性都不行);

15.<a>標籤歷史訪問狀態存儲:

瀏覽器中的 <a> 標籤有個特性, 同一個瀏覽器被訪問事後狀態會變成 "visited" 狀態,通常只有清理瀏覽器瀏覽記錄纔會消失,evercookie利用了這點進行存儲。

簡單說下思路:

  1. 構造<a>標籤並預設visited樣式(a:visited)做爲訪問校驗值;
  2. 構造http請求,請求的地址爲設置的鍵以及值的各個字符(多個http,個數是值的長度);
  3. 寫數據經過構造iframe對上面的http請求進行一次訪問;
  4. 讀數據用鍵和一個字符構造一個連接賦予<a>標籤的href,獲取<a>標籤的樣式與預設visited樣式進行
  5. 直接將http請求賦給<a>標籤的href,獲取若是樣式爲預設visited的樣式說明這個http請求訪問過,解出字符;

說明:2中設置的值是個encode後的值,最後一步解出的字符拼裝後須要decode後才能獲取到原來的值,evercookie裏面的實現頗有意思,有興趣的能夠看下。

16.HSTS存儲:

HSTS通常用來防止中間人攻擊, 簡單來講,若是一個域名的http響應設置過 Strict-Transport-Security,那麼此域名再次發送http請求時瀏覽器會直接轉成https請求(能夠設置prelist,第一次請求也能夠直接在瀏覽器端轉成https),利用這點能夠作數據存儲:

  1. 申請多個域名(例如32個),構造好服務,設置(端口注意設置成443或者80)、清除、查詢;
  2. 將設置的值轉爲二進制整數,好比 1101, 1的bit發送到對應的第一個域名設置 Strict-Transport-Security,0的清除掉 Strict-Transport-Security: max-age=0;
  3. 獲取時向上面32個域名發送請求進行查詢,服務器返回是不是http,對應的bit位設置爲0,對應的二進制轉成數值就是獲取的結果;

直接看實現:

<?php
//header('Access-Control-Allow-Origin: *');
$is_ssl = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443;

if(isset($_GET['SET'])){
    if($is_ssl){
        header('Strict-Transport-Security: max-age=31536000');
        header('Content-type: image/png');
        echo base64_decode('iVBORw0KGgoAAAANSUhEUgAAAAgAAAAJCAIAAACAMfp5AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYSURBVBhXY/z//z8DNsAEpTHAkJJgYAAAo0sDD8axyJQAAAAASUVORK5CYII=');
    }else{
        $redirect = "https://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
        header("Location: $redirect");
    }
    die();
}

if(isset($_GET['DEL'])){
    if($is_ssl){
        header('Strict-Transport-Security: max-age=0');
    }else{
        $redirect = "https://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
        header("Location: $redirect");
    }
    die();
}

if($is_ssl){
    header('Content-type: image/png');
    // some white pixel
    echo base64_decode('iVBORw0KGgoAAAANSUhEUgAAAAgAAAAJCAIAAACAMfp5AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYSURBVBhXY/z//z8DNsAEpTHAkJJgYAAAo0sDD8axyJQAAAAASUVORK5CYII=');
    die();
}else{
    header('X-PHP-Response-Code: 404', true, 404);
}
?>

HSTS存儲方式缺點比較大,要申請多個域名,發送多個請求,evercookie默認關閉HSTS存儲,chrome和firefox兼容新比較好、IE不支持HSTS設置, 瀏覽器也能夠手動設置關閉HSTS。

 

evercookie讀數據:

evercookie讀數據只說一點就能夠了,它的思想並非從任意維度獲取到數據就直接返回結果,而是要將全部設置的維度所有取出進行最優解查找,能夠防止部分數據被篡改致使的數據異常;也帶來一個問題,由於不少都是異步獲取,好比數據庫、e-tag等,那麼獲取數據就不是當即獲取,會有一部分等待時長。

三.應用:

使用evercookie進行持久化,可讓咱們的數據常駐瀏覽器。利用它不只能夠收集各類瀏覽器數據,更重要的是,即便用戶對瀏覽器cookie進行了大清洗,這些數據仍然能夠起死回生。好比,利用它能夠給瀏覽器創建一個長期有效的身份標識符,利用標識符上報數據對用戶的歷史信息進行分析進而判斷一個操做是善意仍是惡意, 對前端風控體系有很大做用。

四.總結:

evercookie簡單來說就是存數據取數據,並無多少東西,比較閃光的地方在於裏面的存取數據的維度和方法,各類奇淫巧技。同時要注意到裏面的一些方法,好比HSTS會帶來很大開銷,獲取數據是一個異步過程也會有時間開銷,應用的時候儘可能根據業務額場景來調整使用。