前言:任何一门技术产生的根本原因都是为了解决问题,在学习新技术的时候,也很有必要去了解一下产生这个技术的原因。
1. Cookie的由来
Cookie
是为了解决早期Web应用无法维持状态而产生的。因为 HTTP 请求是无状态的,服务器无法知道两个请求是否来自于同一个浏览器,即服务器不知道用户上一次做了什么,每次请求都是完全相互独立。
早期互联网只是用于简单的浏览信息,并没有交互(客户端与服务器可以互动,如用户登录,购买商品,各种论坛)。但是随着互联网飞速发展,交互式Web慢慢兴起,而HTTP无状态的特点却严重阻碍了其发展!
当时,最简单的办法就是在请求的页面中插入一个token
,然后在下次请求时将这个 token 返回至服务器。这需要在页面的 form 表单
中插入一个包含 token 的隐藏域,或者将 token 放在 URL 的 query 字符串中来传递。
将 token 放在 URL 的 query 字符串中就不必说了,来了解下隐藏域吧。在HTML表单
中写入如下代码:
这样把用户上一次操作记录放在form表单的input中,这样请求时将表单提交,就知道上一次用户的操作了。
隐藏域是用来
收集或发送信息的不可见元素
,对于用户来说,隐藏域是不可见的。当表单被提交时,隐藏域就会将信息用你设置时定义的名称和值发送到服务器上。
上面的两种方法都是要手动操作的,而且很容易出错,所以当时的开发者肯定会寻求更优的解决方案。网景公司当时的一名员工Lou Montulli(卢-蒙特利),在1994年将“cookies”的概念应用于网络通信,用来解决用户网上购物的购物车历史记录,而当时最强大的浏览器正是网景浏览器,在网景浏览器的支持下其他浏览器也渐渐开始支持Cookie,到目前所有浏览器都支持Cookie了。
2. Cookie 到底是什么?
cookie
翻译过来是“饼干,甜品”的意思,cookie
在网络应用中到处存在,当我们浏览之前访问过的网站,网页中可能会显示:你好,大帅哥,这就会让我们感觉很亲切,像吃了一块很甜的饼干一样。
上面提到过 Http 是无状态的协议,当浏览器和服务器的数据交换完毕,就会断开连接。当再次请求时服务端不知道这次请求的和上次发起请求的是同一个人。怎么解决呢? 那就让服务器在接收到新的用户请求时,给它指定并返回给浏览器一个唯一的标识,这个标识存在浏览器,当再次请求的时候需要将这个标识也带上,有了这个标识,服务端就能知道是哪个用户发起的请求了。
这个标识就是 cookie
,关于它是如何生成和如何在浏览器和服务器传输的先不谈。先来看下它是啥?
其实cookie是一个很小的纯文本文件,服务器发给浏览器并存储在用户机器上的。它储存一些服务器需要的信息,每次发起请求,会发送相应的cookie。
Cookie主要用于以下三个方面:
- 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
- 个性化设置(如用户自定义设置、主题等)
- 浏览器行为跟踪(如跟踪分析用户行为等)
3. Cookie的设置
上面我们已经知道了cookie 是当用户发起请求时,由服务器生成,返回给浏览器,并存储在用户机器上的文本文件。那具体是怎么样的一个过程呢?可以看下下面这张图:
用户在输入用户名和密码之后,浏览器将用户名和密码发送给服务器,服务器进行验证,验证通过之后将用户信息加密后封装成Cookie放在请求头中返回给浏览器,如下所示:
HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: user_cookie=Rg3vHJZnehYLjVg7qi3bZjzg; Expires=Tue, 15 Aug 2019 21:47:38 GMT; Path=/; Domain=.169it.com; HttpOnly
浏览器收到服务器返回数据,发现请求头中有一个:Set-Cookie
,然后它就把这个Cookie
保存起来,下次浏览器再请求服务器的时候,会把Cookie也放在请求头(这一步只会传递 cookie 的值,忽略设置选项)中传给服务器:
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: user_cookie=Rg3vHJZnehYLjVg7qi3bZjzg
服务器收到请求后从请求头中拿到cookie,然后解析并到用户信息,说明此用户已登录。
上面两步无论是服务端返回 cookie 给浏览器,还是浏览器将cookie 传给服务器,都是将 cookie 放在请求头中的。
4. Cookie 中都包含什么
上面的例子中可以看到Set-Cookie
对应的字符串是:
Set-Cookie: user_cookie=Rg3vHJZnehYLjVg7qi3bZjzg; Expires=Tue, 15 Aug 2019 21:47:38 GMT; Path=/; Domain=.169it.com; HttpOnly
它是有一定的格式的:
Set-Cookie: value[; expires=date][; domain=domain][; path=path][; secure]
消息头的第一部分:value 部分,通常是一个name=value
格式的字符串,后面的则是一些设置选项(设置选项只用于浏览器端,请求时不会传给服务器),用来控制 cookie 在什么情况下应该被发送至服务器。
从上面的Set-Cookie
中,我们看到后面的设置项有 Expires
、Path
、Domain
、HttpOnly
等设置,其实还有一些其他的设置项,一起来了解一下:
Expires (过期时间)
指定 cookie 的过期时间,过期之后就会被浏览器删除;如果没有设置Expires
,cookie 的生命周期仅限于当前会话中,关闭浏览器意味着这次会话的结;如果设置了一个过去的时间点,那么这个 cookie 会被立即删掉;Domain (对应的域名)
默认domain
会被设置为创建该 cookie 的页面所在的域名,所以当给相同域名发送请求时该 cookie 会被发送至服务器;Path (指定的路径)
当请求的资源 URL 中包含(从头开始逐字符比较)Path字符串 时,才会发送Cookie 消息头。默认值是发送 Set-Cookie 消息头所对应的 URL 中的 path 部分;secure
只是一个标记而没有值,只有当一个请求通过 SSL 或 HTTPS 创建时,包含 secure 选项的 cookie 才能被发送至服务器。默认情况下,在 HTTPS 链接上传输的 cookie 都会被自动添加上 secure 选项。HTTP-Only
微软的 IE6 SP1 在 cookie 中引入的新选项,也是一个标记。用于告之浏览器该 cookie 绝不能通过JavaScript 的 document.cookie
属性访问(下面会有介绍),阻止通过 JavaScript 发起的跨站脚本攻击 (XSS) 窃取 cookie 的行为。
下图是通过谷歌浏览器查看百度设置的 Cookie:
5. Cookie的维护
在一个 cookie 中可以指定任意数量的选项,并且这些选项可以是任意顺序,例如:
Set-Cookie:name=Nicholas; domain=nczonline.net; path=/blog
这个 cookie 有四个标识符:cookie
的 name
,domain
,path
,secure
标记。要想改变这个 cookie 的值,需要发送另一个具有相同 cookie name
,domain
,path
的 Set-Cookie
消息头。例如:
Set-Cookie: name=Greg; domain=nczonline.net; path=/blog
但是,修改 cookie
选项的任意一项都将创建一个完全不同的新 cookie
,比如下面的path
值改变了:
Set-Cookie: name=Nicholas; domain=nczonline.net; path=/
这会创建一个新的cookie
,当访问www.nczonline.net/blog
下的一个页面,以下的消息头将被包含进来:
Cookie: name=Greg; name=Nicholas
如果cookie
被浏览器自动删除,通常存在以下几种原因:
-
会话 cooke (Session cookie)
在会话结束时(浏览器关闭)会被删除 -
持久化 cookie(Persistent cookie)
在到达失效日期时会被删除 - 如果浏览器中的
cookie 数量达到限制
,那么 cookie 会被删除以为新建的 cookie 创建空间
6. 其他
6.1 Cookie 限制条件
cookie 存在许多限制条件,数量的限制还比较宽松(可以通过subcookies增加存储量)。但是,发向服务器的所有 cookie 的最大数量(空间)仍旧维持原始规范中所指出的:4KB。所有超出该限制的 cookie 都会被截掉并且不会发送至服务器。
6.2 JavaScript 中的 Cookie
在 JavaScript 中通过 document.cookie
属性,你可以创建、维护和删除 cookie。创建 cookie 时该属性等同于 Set-Cookie
消息头,而在读取 cookie 时则等同于 Cookie
消息头。在创建一个 cookie 时,你需要使用和 Set-Cookie 期望格式相同的字符串:
document.cookie="name=Nicholas;domain=nczonline.net;path=/";
设置
document.cookie
属性的值并不会删除存储在页面中的所有 cookie。它只简单的创建或修改字符串中指定的 cookie,和其他的 cookie 并没有什么明确的不同之处。
要使用 JavaScript
提取 cookie
的值,只需要从 document.cookie
中读取即可。返回的字符串与 Cookie 消息头中的字符串格式相同,所以多个 cookie 会被分号和字符串分割。例如:
name1=Greg; name2=Nicholas
通过访问 document.cookie
返回的 cookie 遵循发向服务器的 cookie 一样的访问规则。要通过 JavaScript
访问 cookie
,该页面和 cookie 必须在相同的域中,有相同的 path,有相同的安全级别。
6.3 第三方 Cookie
通常cookie的域和浏览器地址的域匹配,这被称为第一方cookie。那么第三方cookie就是cookie的域和地址栏中的域不匹配,这种cookie通常被用在第三方广告网站。为了跟踪用户的浏览记录,并且根据收集的用户的浏览习惯,给用户推送相关的广告。
如上图(a):用户访问服务器1的一个页面index.html,这个页面和第三方广告网站合作,这个页面还有一张www.advertisement.com
域名下的一张广告图ad1.jpg
,当请求这张ad1.jpg
图片的时候,www.advertisement.com
这个服务器会给用户设置cookie:
Set-Cookie: user="wang";like="a"; domain="advertisement.com"
记录用户的浏览记录,分配一个user来表示用户的身份。
图(b):用户访问服务器2的一个index.html页面,这个页面也和同一家广告商合作,这个页面也包含一张www.advertisement.com
域名下的一张广告图ad2.jpg
,当请求这张ad2.jpg
图片的时候,浏览器就会向www.advertisement.com
发送cookie:
Cookie: user="wang"; like="a";
www.advertisement.com
收到浏览器发送的cookie识别了用户的身份,同时又把这个页面用户的浏览数据设置cookie:
Set-Cookie: buy="b"; domain="advertisement.com"
图(c):很巧,用户访问服务器3的一个index.html页面,这个页面也和那一家广告商合作,这个页面也包含一张www.advertisement.com
域名下的一张广告图ad3.jpg
,当请求这张·ad3.jpg·图片的时候,浏览器就会向www.advertisement.com
发送cookie:
Cookie: user="wang"; like="a"; buy="b"
这样广告公司就可以根据用户的浏览习惯,给用户推送合适的广告。
6.5 安全问题
多数网站使用cookie作为用户会话的唯一标识,因为其他的方法具有限制和漏洞。如果一个网站使用cookies作为会话标识符,攻击者可以通过窃取一套用户的cookies来冒充用户的请求。从服务器的角度,它是没法分辨用户和攻击者的,因为用户和攻击者拥有相同的身份验证。 下面介绍几种cookie盗用和会话劫持的例子:
网络窃听
网络上的流量可以被网络上任何计算机拦截,特别是未加密的开放式WIFI。这种流量包含在普通的未加密的HTTP清求上发送Cookie。在未加密的情况下,攻击者可以读取网络上的其他用户的信息,包含HTTP Cookie的全部内容,以便进行中间的攻击。比如:拦截cookie来冒充用户身份执行恶意任务(银行转账等)。
解决办法:服务器可以设置secure属性的cookie,这样就只能通过https的方式来发送cookies了。
DNS缓存中毒
如果攻击者可以使DNS缓存中毒,那么攻击者就可以访问用户的Cookie了,例如:攻击者使用DNS中毒来创建一个虚拟的NDS服务h123456.www.demo.com
指向攻击者服务器的ip地址。然后攻击者可以从服务器h123456.www.demo.com/img_01.png
发布图片。用户访问这个图片,由于 www.demo.co
和h123456.www.demo.com
是同一个子域,所以浏览器会把用户的与www.demo.com
相关的cookie都会发送到h123456.www.demo.com
这个服务器上,这样攻击者就会拿到用户的cookie搞事情。
一般情况下是不会发生这种情况,通常是网络供应商错误。
跨站点脚本XSS
使用跨站点脚本技术可以窃取cookie。当网站允许使用javascript操作cookie的时候,就会发生攻击者发布恶意代码攻击用户的会话,同时可以拿到用户的cookie信息。
例子:
领取红包
当用户点击这个链接的时候,浏览器就会执行onclick
里面的代码,结果这个网站用户的cookie信息就会被发送到abc.com
攻击者的服务器。攻击者同样可以拿cookie搞事情。
解决办法:可以通过cookie的HttpOnly
属性,设置了HttpOnly
属性,javascript
代码将不能操作cookie。
跨站请求伪造CSRF
例如,SanShao
可能正在浏览其他用户XiaoMing
发布消息的聊天论坛。假设XiaoMing
制作了一个引用ShanShao
银行网站的HTML图像元素,例如,
如果SanShao
的银行将其认证信息保存在cookie
中,并且cookie
尚未过期,(当然是没有其他验证身份的东西),那么SanShao
的浏览器尝试加载该图片将使用他的cookie
提交提款表单,从而在未经SanShao
批准的情况下授权交易。
解决办法:增加其他信息的校验(手机验证码,或者其他盾牌)。
参考:
https://www.w3cschool.cn/pegosu/pqwghozt.html
https://blog.csdn.net/u014044812/article/details/95899291