从远古时代开始:Cookie + Session 的诞生与黄金十年(1994–2012)

8 阅读6分钟

在今天讨论前端登录技术时,大家很容易把焦点放在 JWT、Passkey、OIDC 这些“现代”方案上。但如果你把时间线拉回 20–30 年前,会发现整个 Web 的“有状态”世界,几乎完全建立在两个最朴素的东西上:CookieSession

这一篇,我们就来聊聊这个“远古时代”的起点、黄金期,以及它为什么在 2010 年代初开始显露疲态。

1. HTTP 为什么是无状态的?问题从这里开始

1990–1991 年,Tim Berners-Lee 发明了 World Wide Web,核心协议就是 HTTP/0.9 → HTTP/1.0。

HTTP 的设计哲学非常极简:

  • 无连接(请求完就断开)
  • 无状态(每次请求之间互不记得对方)

这在当时非常合理:服务器资源宝贵,浏览器也弱鸡,保持长连接或记住上一次请求几乎是奢望。

但很快,商业需求来了。

1994 年,Netscape 正在帮 MCI 做一个在线购物系统。问题就暴露了:

用户把商品 A 加到购物车 → 跳转到商品 B 页面 → 购物车怎么还记得有 A?

服务器根本不知道这个请求是同一个用户发出的。

2. Cookie 的诞生:1994 年,Lou Montulli 的灵光一闪

1994 年 6 月,Netscape 工程师 Lou Montulli(时年 23 岁)想到了一个办法。

他借用了 UNIX 系统里早已存在的概念——“magic cookie”(一种用于进程间传递小数据的 token),发明了浏览器端的 HTTP Cookie

核心思路极其简单:

  1. 服务器在响应头里写一句:Set-Cookie: session_id=abc123; path=/
  2. 浏览器收到后,把这个键值对存在本地(当时就是个小文本文件)
  3. 之后每次向同一个域名发请求,浏览器自动在请求头带上:Cookie: session_id=abc123

1994 年 10 月 13 日,Mosaic Netscape 0.9 beta 第一个支持了 Cookie。

第一个真实场景?Netscape 官网用它判断“你是不是第一次访问本站”。

同年,Montulli 和 John Giannandrea 一起写了最初的(非正式)Cookie 规范。

1995 年,Internet Explorer 2.0 跟进支持。

1997–2000 年,IETF 先后出了 RFC 2109 和 RFC 2965,试图把 Cookie 变成正式标准(但浏览器实现其实一直以 Netscape 的原始版本为主)。

3. Session 是怎么玩的?服务端内存里的“身份证”

Cookie 只是一个“载体”,真正保存用户状态的,通常是服务器端的 Session

典型流程(PHP / Java / ASP 等经典时代):

  1. 用户第一次登录 → 表单提交用户名密码
  2. 服务器验证通过 → 生成一个随机、难以猜测的字符串(session id),比如 32 位随机数
  3. 把这个 session id 存到服务器内存(或文件、数据库)里,同时关联用户真实信息:{ userId: 123, loginTime: "...", role: "admin" }
  4. 通过 Set-Cookie: PHPSESSID=abc123; path=/ 把 session id 发给浏览器
  5. 浏览器后续所有请求都自动带上这个 Cookie
  6. 服务器收到请求 → 查内存/文件/Redis → 找到对应 session → 知道“你是谁”

这套机制在 2000–2010 年几乎是所有有登录系统的网站的标配。

代表技术栈:

  • 前端:纯 HTML + <form> + jQuery 1.x(2006 年后流行)
  • 后端:PHP + Apache + MySQL、Java + Tomcat、ASP.NET + IIS
  • Session 存储:默认内存 → 后期用文件、Memcached、数据库、Redis 做集群共享

4. 黄金十年:为什么 Cookie + Session 统治了那么久?

  • 实现简单:后端几行代码就搞定
  • 浏览器原生支持:不用写任何前端逻辑
  • 跨页面自动携带:用户体验好
  • 安全性在当时“够用”:session id 够长够随机,基本猜不出来

2000–2010 年,几乎所有主流网站(淘宝早期、Facebook 早期、WordPress、BBS、天涯、猫扑……)都用这套。

5. 但它从来都不是完美的:三大经典攻击

这个时代的安全问题,其实早就埋好了雷。

① XSS(Cross-Site Scripting)

  • 攻击者通过输入框、评论区等地方注入 <script>alert(document.cookie)</script>
  • 一旦页面把用户输入不转义输出 → 脚本执行 → 偷走 document.cookie → 拿到 session id
  • 2000 年代中期 XSS 泛滥,2005–2010 年是高发期

缓解手段(后来才普及):2002 年微软在 IE6 SP1 引入 HttpOnly 属性

Set-Cookie: sessionid=abc123; HttpOnly

加了 HttpOnly 后,JavaScript 无法读取这个 Cookie,XSS 就偷不走 session(但仍能发起请求)。

可惜很多老系统一直没加。

② CSRF(Cross-Site Request Forgery)

  • 用户已登录银行网站
  • 访问恶意页面:<img src="https://bank.com/transfer?to=attacker&amount=10000">
  • 浏览器自动带上 Cookie → 银行服务器以为是用户自己操作 → 转账成功

最早 2000 年左右就有人发现,2001 年 Peter Watkins 正式命名 CSRF,2006–2008 年才大规模被重视。

缓解手段(当时主流):表单里加随机 CSRF Token(Synchronizer Token Pattern)

③ Session Fixation

  • 攻击者先访问网站,拿到一个 session id
  • 诱导受害者用带 ?PHPSESSID=xxxx 的链接登录
  • 受害者登录后,同一个 session id 就绑定了他的身份
  • 攻击者用同样的 session id 冒充受害者

缓解手段:登录成功后立即更换 session id(session_regenerate_id() 等)

6. 远古时代的前端有多“闲”?

说出来你可能不信:

  • 基本不写登录相关的 JavaScript
  • 表单提交 → 页面刷新 → 显示“欢迎 xxx”或错误提示
  • 最多用一点 jQuery 做表单校验、loading 遮罩
  • 没有 axios 拦截器、没有 token 刷新、没有 pinia/vuex 持久化

前端的全部职责:渲染 + 提交表单 + 展示错误

7. 黄金时代的终结信号(2010–2012 左右)

2010 年后,几个大趋势把 Cookie + Session 逼到了墙角:

  1. 移动互联网爆发:原生 App + Hybrid 需要跨域、API 调用,Cookie 跨域携带麻烦
  2. 前后端分离:SPA(AngularJS 2010、Backbone、Ember)兴起,前端要自己管理状态
  3. 服务器集群 / 微服务:Session 存在单机内存 → 水平扩展痛苦(要么粘性 session,要么 Session 共享到 Redis)
  4. API-first 思想:前后端通过 JSON API 通信,Cookie 不再是唯一选择

于是,2012–2015 年,Token 时代(尤其是 JWT)开始崛起。

小结 & 过渡

Cookie + Session 是 Web 从“纯文档”走向“应用”的第一步身份解决方案。它简单、实用、浏览器原生支持,统治了 Web 黄金十年(≈1998–2012)。

但它的核心缺陷也从一开始就注定了:

  • 强依赖同源策略(跨域痛苦)
  • 服务端有状态(扩展性差)
  • XSS/CSRF 等攻击面长期存在