在今天讨论前端登录技术时,大家很容易把焦点放在 JWT、Passkey、OIDC 这些“现代”方案上。但如果你把时间线拉回 20–30 年前,会发现整个 Web 的“有状态”世界,几乎完全建立在两个最朴素的东西上:Cookie 和 Session。
这一篇,我们就来聊聊这个“远古时代”的起点、黄金期,以及它为什么在 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。
核心思路极其简单:
- 服务器在响应头里写一句:
Set-Cookie: session_id=abc123; path=/ - 浏览器收到后,把这个键值对存在本地(当时就是个小文本文件)
- 之后每次向同一个域名发请求,浏览器自动在请求头带上:
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 等经典时代):
- 用户第一次登录 → 表单提交用户名密码
- 服务器验证通过 → 生成一个随机、难以猜测的字符串(session id),比如 32 位随机数
- 把这个 session id 存到服务器内存(或文件、数据库)里,同时关联用户真实信息:
{ userId: 123, loginTime: "...", role: "admin" } - 通过
Set-Cookie: PHPSESSID=abc123; path=/把 session id 发给浏览器 - 浏览器后续所有请求都自动带上这个 Cookie
- 服务器收到请求 → 查内存/文件/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 逼到了墙角:
- 移动互联网爆发:原生 App + Hybrid 需要跨域、API 调用,Cookie 跨域携带麻烦
- 前后端分离:SPA(AngularJS 2010、Backbone、Ember)兴起,前端要自己管理状态
- 服务器集群 / 微服务:Session 存在单机内存 → 水平扩展痛苦(要么粘性 session,要么 Session 共享到 Redis)
- API-first 思想:前后端通过 JSON API 通信,Cookie 不再是唯一选择
于是,2012–2015 年,Token 时代(尤其是 JWT)开始崛起。
小结 & 过渡
Cookie + Session 是 Web 从“纯文档”走向“应用”的第一步身份解决方案。它简单、实用、浏览器原生支持,统治了 Web 黄金十年(≈1998–2012)。
但它的核心缺陷也从一开始就注定了:
- 强依赖同源策略(跨域痛苦)
- 服务端有状态(扩展性差)
- XSS/CSRF 等攻击面长期存在