浅谈用户行为分析之用户身份识别:cookie 知多少?

对于数据统计分析或者数据挖掘而言,用户是个非常重要的维度,也是统计分析能落地的基础。一般而言,咱们追踪或者识别一个用户的首选方案是 userID,大多数公司的产品都会要求用户注册、登录操作,都存在一个类似 UMC 的数据库,管理和标示所有的用户。但这有个前提条件,就是你所在的公司业务必须以闭环为主(比如 qq、微信、淘宝等)。如果产品没有形成闭环,用户就不会主动去注册、登录,那上面通过 userID 数据库来管理、追踪用户行为的方案就不行了。比如BBS站点或者广告联盟都会非常想要一种技术方式可以在网络上精确定位到每一个个体,这样可以通过收集这些个体的数据,通过分析后更加精准的去推送广告(精准化营销)或其他有针对性的一些活动。当用户访问一个网站时,网站生成一个含有唯一标示符(UUID)的信息,并通过这个信息将用户所有行为(浏览了哪些页面?搜索了哪些关键字?对什么感兴趣?点了哪些按钮?用了哪些功能?看了哪些商品?把哪些放入了购物车等等)关联起来。那这种情况下有没有可能有其它的技术方案去管理追踪这种游客态用户呢?

答案或许很多同学会回答用 cookie。是的,对于游客态用户而言,常用的身份识别方案就是使用 cookie,技术实现难度小,成本相对很低廉。那是不是使用 cookie 就万事大吉了呢?准确性、稳定性、可辨识性怎么样?下面咱们就来深入探讨下 cookie 追踪用户的利弊及其发展与移动互联网时代下用户身份识别面临的新问题。

1、追踪/标示用户的方法

先上一张图,可以看到大部分流行的方法还是基于Cookie,只是这些 Cookie 会稍有不同,本文会按照整张图的脉络来一一介绍各种 cookie 及其利与弊。

2、HTTP Cookie

2.1 由来

为什么会有 HTTPCookie 呢?因为HTTP协议是无状态的,即服务器不知道用户上一次做了什么,这严重阻碍了交互式Web应用程序的实现。在典型的网上购物场景中,用户浏览了几个页面,买了一盒饼干和两瓶饮料。最后结帐时,由于HTTP的无状态性,不通过额外的手段,服务器并不知道用户到底买了什么。 所以Cookie就是用来绕开HTTP的无状态性的“额外手段”之一。服务器可以设置或读取Cookies中包含信息,借此维护用户跟服务器会话中的状态。

2.2 实现方式

Cookie是由服务器端生成(webserver或者cgi),response 给User-Agent(一般是浏览器),浏览器会将Cookie的key/value保存到某个目录下的文本文件内,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)。

2.3 缺陷

  • Cookie会被附加在每个HTTP请求中,所以无形中增加了流量。
  • 由于在HTTP请求中的Cookie是明文传递的,所以安全性成问题。(除非用HTTPS)
  • Cookie的大小限制在4KB左右。对于复杂的存储需求来说是不够用的。
  • 浏览器安全策略不允许种植 cookieID、用户清除 cookieID,不同的浏览器也会生成不同的 cookieID,大量的爬虫也可能带上随机 cookieID
  • 识别不准确:浏览器安全策略不允许种植 cookieID、用户清除 cookieID,不同的浏览器生成不同的 cookieID,大量的爬虫带上随机 cookieID

3、Flash Cookie

3.1 由来

在客户端Cookie里保存数据是不稳 定的,因为用户可能随时会清除掉浏览器的Cookie,在这种情况下,一般的解决方案是重新向服务器端发送一个请求,以获得一个新的HTTP Cookie数据,并将其保存,就一般的交互需求而言,这是没有问题的。但是,倘若我的需求是:要求恢复到原来的Cookie里保存数据持久的追踪用户的行为呢?这种情况,倘若服务器端没有做特殊的处理的话,显然是很难实现的,这里就该 Flash Cookie 登场了。

FlashCookie是由FlashPlayer控制的客户端共享存储技术,它具备以下特点:

  • 类似 HTTPCookie,FlashCookie利用SharedObject类实现本地存储信息,SharedObject类用于在用户计算机上读取和存 储有限的数据量,共享对象提供永久贮存在用户计算机上的对象之间的全局实时数据共享;
  • 本地共享对象是作为一些单独的文件来存储的,它们的文件扩展名 为.SOL。默认时,它们的尺寸为不超过100kB,并且不会过期——这一点与传统的HTTP Cookie不同(4KB);
  • 本地共享对象并不是基于浏览器的,所以普通的用户不容易删除它们。如果要删掉它们的话,首先要知道这些文件所在的具体位 置。这使得本地共享对象能够长时间的保留在本地系统上。

3.2 实现方式

要实现Flash Cookie永远存储的功能,显然,首先要实现Flash Cookie与Http Cookie的互通,所以,在技术上使用JavaScript与ActionScript的来进行沟通显然是最好的选择,因为在这两种语言之间,除了语法 上相近,从沟通上也有着完美的实现。下面我们来看看实现流程(如图所示):

这里给一个实现的 demo,点窗口→动作,我们就可以写actionscript3的代码了,然后文件→发布成 .swf 文件:

//导入ExternalInterface类
import flash.external.ExternalInterface;

flash.system.Security.allowDomain("http://localhost");
flash.system.Security.allowDomain("http://127.0.0.1");
//允许任何域都可以访问
flash.system.Security.allowDomain("*");

function setFC(userName:String, sex:String) {
    var FlashCookie:SharedObject = SharedObject.getLocal("testFlashCookie");
    FlashCookie.data.cookie["userName"] = userName;
    FlashCookie.data.cookie["sex"] = sex;
    FlashCookie.flush();
}
function getFC():String {
    var FlashCookie:SharedObject = SharedObject.getLocal("testFlashCookie");
    return FlashCookie.data.cookie["userName"];
}
function setFCUserObj(obj:Object) {
    var FlashCookie:SharedObject = SharedObject.getLocal("testFlashCookie");
    if (FlashCookie.data.cookie == undefined) {
        //var obj:Object = {};
        //obj[key] = value;
        FlashCookie.data.cookie = obj;
    } else {
        for (var key:String in obj) {
            FlashCookie.data.cookie[key] = obj[key];
        }

    }
    //FlashCookie.data.userName = obj.userName;
    //FlashCookie.data.sex = obj.sex;
    FlashCookie.flush();
}
//允许js)调用flash中的getFC(),setFC(),setFCUserObj
ExternalInterface.addCallback("getFC", getFC);
ExternalInterface.addCallback("setFC", setFC);
ExternalInterface.addCallback("setFCUserObj", setFCUserObj);

再用 flask 搭一个简单的页面:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-CN">
<head>
    <title>testFC</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <style type="text/css" media="screen">
        html, body {
            height: 100%;
            background-color: #ffffff;
        }

        body {
            margin: 0;
            padding: 0;
            overflow: hidden;
        }

        #flashContent {
            width: 100%;
            height: 100%;
        }
    </style>
    <script type="application/javascript">
        function setCookie(c_name, value, expiredays) {
            var exdate = new Date()
            exdate.setDate(exdate.getDate() + expiredays)
            document.cookie = c_name + "=" + escape(value) +
                    ((expiredays == null) ? "" : ";expires=" + exdate.toGMTString())
        }
        function getCookie(c_name) {
            if (document.cookie.length > 0) {
                c_start = document.cookie.indexOf(c_name + "=")
                if (c_start != -1) {
                    c_start = c_start + c_name.length + 1
                    c_end = document.cookie.indexOf(";", c_start)
                    if (c_end == -1) c_end = document.cookie.length
                    return unescape(document.cookie.substring(c_start, c_end))
                }
            }
            return ""
        }

    </script>

    <script type="text/javascript">
        //搭建js与flash互通的环境
        function thisMovie() {
            if (navigator.appName.indexOf("Microsoft") != -1) {
                return window["testFC"];
            } else {
                return document["testFC"];
            }
        }

        function setFCUseObj() {
            c_name = getCookie("userName")
            c_sex = getCookie("sex")
            if (c_name == "") {
                alert("当前 jCookie: " + c_name + "\n" + "当前 flash cookie: " + thisMovie().getFC())
                var ajaxRequest = new XMLHttpRequest();
                ajaxRequest.open("GET", "http://127.0.0.1:5000/add", false);
                ajaxRequest.send(null);
                c_name = getCookie("userName") + Math.random();
                c_sex = getCookie("sex") + Math.random();
            }
            {#            expiredays = 1#}
            {#            setCookie(key, value, expiredays)#}
            var obj = new Object();
            obj.userName = c_name;
            obj.sex = c_sex;
            thisMovie().setFCUserObj(obj);
        }
        function getFC() {
            alert(thisMovie().getFC());
        }
        function setFC() {
            thisMovie().setFC("June_flashCookie", "male");
        }

    </script>
</head>

<body>
<input type="button" onclick="setFC()" value="setFC"/>
<input type="button" onclick="getFC()" value="getFC"/>
<input type="button" onclick="setFCUseObj()" value="setFCUseObj"/>

<div id="flashContent">
    <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
            codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,19,0" width="1"
            height="1" id="testFC" title="testFC">
        <param name="allowScriptAccess" value="always"/>
        <param name="movie" value="testFC.swf">
        <param name="quality" value="high">
        <param name="wmode" value="transparent"/>
        <embed src="static/testFC.swf" name="testFC" quality="high" allowScriptAccess="always" swLiveConnect="true"
               pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" width="1"
               height="1"></embed>
    </object>
</div>
</body>
</html>

最后再配个简单的 cgi:

from flask import Flask, request, Response, make_response, render_template
import time

app = Flask(__name__)


@app.route('/')
def hello_world():
    return 'hello world'


@app.route('/add')
def login():
    res = Response('add cookies')
    res.set_cookie(key='userName', value='lisi_jsCookie', expires=time.time() + 10 * 60)
    res.set_cookie(key='sex', value='unKnown', expires=time.time() + 10 * 60)
    return res


@app.route('/testFC')
def cookietest():
    return render_template("testFC.html")

if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True)

3.3 缺陷

优势是显而易见的,缺陷在于:

  • 部署相对 HTTP Cookie 复杂了,存在一定的开发维护成本。
  • 平台兼容性不够好,目前只支持特定的平台与特定的浏览器,比如 苹果的全系列产品都不支持 flash,以至于2012年开始 adobe 已经彻底放弃了移动端 flash 的更新,这样 Android 平台未来也不会存在 flash 这一技术了,因此至少在移动端 Flash Cookie 不能作为长久之计。
  • 可以跨浏览器使用,只要浏览器调用的是同一个Flash组件,但彼此兼容性不够(如Internet Explorer和Mozilla Firefox之间信息便可共享,但Google Chrome和Mozilla Firefox便不行)。
  • 虽然可以跨浏览器,但隐身模式无效。可以被很容易地清除。

4、EverCookie

4.1 由来

前面的两种方法都存在一定的缺陷,在复杂多变的用户场景里,数据可能和真实的误差很大,那有没有办法进一步提高 Cookie 追踪用户身份的准确度呢?也有,那就是最后聊到的 EverCookie,其实它也不是什么新的 cookie 技术,只是利用客户端各种存储区域,尽可能的存储多的 cookie 副本,以防某处 cookie 被删除可以恢复,相当于 cookie 也有了类似 hadoop 多副本灾备机制,同时生成 cookieID 的算法参考了更多的客户端标识和软硬件特征,让 cookieID 具有更高的稳定性、唯一性、可辨识性,不随算法本身随机性的影响。

4.2 实现方式

Evercookie不仅仅是难删除,而是会积极“反抗”删除。方法就是在用户电脑里,利用不同的存储机制不断地复制自己,或者在副本丢失或到期作废时让自己重新复活。具体来说,Evercookie在创建cookie时会使用如下存储机制:

  • 标准HTTP cookie
  • Local Shared Objects (Flash cookie)
  • Silverlight Isolated Storage
  • 以自动生成、强制缓存的PNG像素图片的RGB值形式保存cookie,使用HTML5 Canvas标签读取像素图片(cookie)
  • 在浏览器历史记录中存储cookie
  • 在HTTP ETag中存储cookie
  • 在浏览器缓存中存储cookie
  • window.name缓存
  • Internet Explorer userData
  • HTML5 Session Storage
  • HTML5 Local Storage
  • HTML5 Global Storage
  • HTML5 Database Storage(SQLite)

开发人员计划增加如下功能:

  • HTTP Authentication缓存
  • 使用Java基于NIC信息产生唯一键

4.3 缺陷

上面的 EverCookie 看起来气场十足,很完美,其实只是理想太美好,显示依旧很残酷——一样的存在诸多缺陷,只是它把一些缺陷不足尽可能降低了而已。

以 canvas指纹 为例:

取代cookie的网站追踪技术:”帆布指纹识别”初探

虽然解决了 cookie 的稳定性——无法删除,但是唯一性、可辨识性并没有解决——重复率太高、ID容易变化:

canvas指纹验证测试报告

这里有个在线指纹测试的例子:

http://jude.me/fingerprint/

5、移动互联网时代下的新挑战

从文初的图上可以看到,在 PC 时代,追踪用户身份技术方案多,也挺靠谱的,但是随着移动互联网大潮的到来,用户逐渐转向了 M 和 APP,形成三大平台三足鼎立的局面,这三大平台的软硬件技术方案各异,比如苹果系列的产品不支持 flash、不允许随便种植 cookie,而 Android 虽然开放,但是开放的尺度太大了,导致了很严重的软硬件碎片化的问题,这给技术方案的通用兼容性带来了严重的问题。应用又分为 NativeAPP 和 webAPP,前者可以很好的和系统结合,拿到系统的硬件信息特征,比如 MAC、IMEI,而 webAPP 大都受限于浏览器隐私策略保护和前端技术限制,没法拿到系统的硬件信息,这就直接导致无法生成一个基于硬件的唯一的、稳定的、准确的“用户ID”,而且想要三端用户身份都打通就成了一个难事,比如:公司三端的用户重合度是 100%,每端 UV 都是一亿,那么三端的总 UV 应该是一亿,但是以现有业界的 cookieID 技术方案来统计 UV,会得出三端总 UV 是三亿的错误结论,而这目前业界也还没有很完善、通用的解决方案。

总结下在移动互联网时代,用户身份识别与追踪的新挑战有两点:

  • 三端用户身份无法打通、统一
  • 追踪识别的成本越来越高,方案越来越复杂化

这或许是商业行为与用户隐私的一场持久博弈,而在这场博弈的背后技术又将会扮演什么角色呢?

6、Refer:

[1] Javascript-Flash-Cookies

https://github.com/nfriedly/Javascript-Flash-Cookies

[2] flash cookie的制作和使用例子详解 一

http://ylq365.iteye.com/blog/1873382

[3] 用户数据跟踪之Flash Cookies

http://www.biaodianfu.com/flash-cookies.html

[4] 使用Flash Cookie技术在客户端永久保存HTTP Cookie

http://www.cnblogs.com/dcba1112/archive/2011/05/05/2037715.html

[5] 不用Cookie的“Cookie”技术:etag

http://blog.jobbole.com/46266/

[6] 网站数据收集

https://support.google.com/partners/answer/6083646?hl=zh-Hans

[7] php 如何对客户端 pc 生成唯一标识?

https://www.v2ex.com/t/133910

[8] 防恶意点击代码系统思路与实现

http://wenku.baidu.com/view/6c0b0749be1e650e52ea9917

[9] Evercookie(永远删不掉的cookie)

http://www.ituring.com.cn/article/35102

[9] 如何设置一个永远无法删除的Cookie

http://www.biaodianfu.com/zombie-cookie.html

[9] 关于浏览器身份追踪技术的研究与整理

http://blog.zsxsoft.com/post/11

[10] evercookie

https://github.com/samyk/evercookie

https://github.com/decli/flask-fingerprint

[11] 取代cookie的网站追踪技术:”帆布指纹识别”初探

http://security.tencent.com/index.php/blog/msg/59

[12] canvas指纹验证测试报告

http://blog.csdn.net/huangm_fat/article/details/38522939

[13] 在线指纹测试例子:

http://jude.me/fingerprint/

[14] 自由之设备,独立之人格:从设备识别到跨屏营销

http://bit.ly/1USZl1J

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏个人分享

一次极限项目管理,设计,开发,联调与测试

     什么是All In? 是你不知道全力做这件事情会得到什么。但你只想把它做好的感觉。

1111
来自专栏腾讯移动品质中心TMQ的专栏

边开发边测试--故事从闹钟讲起

你开发过alarm相关的应用吗? 你测试过alarm相关的应用吗? 如果答案是肯定的,建议看官停下来拍拍砖。 手机管家新年运营功能有一个招牌:红包闹钟。 功能发...

2639
来自专栏Java架构师学习

多研究些架构,少谈些框架——一名阿里架构师的笔记

微服务架构和SOA区别 微服务现在辣么火,业界流行的对比的却都是所谓的Monolithic单体应用,而大量的系统在十几年前都是已经是分布式系统了,那么微服务作为...

3678
来自专栏大数据文摘

一个可以把web表单变成会话形式的开源框架

4855
来自专栏java思维导图

电商中常见的高并发怎么处理?

在过去的工作中,我曾经面对过5w每秒的高并发秒杀功能,在这个过程中,整个Web系统遇到了很多的问题和挑战。如果Web系统不做针对性的优化,会轻而易举地陷入到异常...

3372
来自专栏人工智能头条

6月Python开源项目Top 10:如何快速找到抖音上的漂亮小姐姐……

1543
来自专栏纯洁的微笑

港股上市!小米开源项目盘点

1133
来自专栏码匠的流水账

2017年终总结

又到了写年终总结的时候了。每当这个时候思绪总是翻江倒海,因为太久没有反思和总结的缘故,一年才总结一次,确实是有点久,欠的账的太多,梳理起来有点费劲。这里依旧还是...

2301
来自专栏PHP在线

网站大规模并发处理方案:电商秒杀与抢购

一、大规模并发带来的挑战 在过去的工作中,我曾经面对过5w每秒的高并发秒杀功能,在这个过程中,整个Web系统遇到了很多的问题和挑战。如果Web系统不做针对性的优...

4517
来自专栏Android机动车

Android模块化开发方案

随着业务的不断发展壮大,移动端所承担的功能也越来越重,特别是代码几易其主之后开始变得杂乱无章,牵一发而动全局的事情时常发生。为了应对团队壮大之后的开发模式,我们...

1442

扫码关注云+社区