关键词:
早期互联网只是用于简单的页面浏览,并没有交互,服务器也无法知道不同的请求是否来自同一个浏览器,不知道某用户上一次做了什么。每次请求都是相互完全独立的,这也是 HTTP 协议无状态特征的表现。这种缺陷显然无法满足交互式 Web 发展的需求,Cookie 作为一种解决这一问题的方案,被当时最强大的网景浏览器公司提出。
一、Cookie叙述
Cookie 可以理解成浏览器的身份证。不同站点会根据实际情况,发放一个唯一的身份证或不发放。当再次访问相同站点时,按照约定要带上这个身份证来享受部分特权。如果身份证丢了,那就要重新登记办理。
Cookie 信息是由客户端浏览器自身维护的。不同的浏览器有不同的客户端本地存储方式,Chrome 和 Firefox 使用 SQLite 存储,IE 使用的是文本格式。Cookie 里面重要的 key&value 都是被浏览器加密存放的,只有通过给定的 API 方式才能获取存入的原始数据。默认情况下,Cookie 信息会随着浏览器进程的结束而从内存中销毁,如果由于某些需求,服务器端设置了 Cookie 的存活时间,那么这个 Cookie 就会以某种形式被存储在磁盘上,在有效存活期内不会被清理,可以被重复使用并更新其生命周期。
1.1 观察Cookie在HTTP数据包中的交互
这里以 http://www.website.com/bbs/
站点为例,说明 Cookie 在 HTTP 协议包里是如何传输的。
第一次请求 bbs 首页 /bbs/ 时,在请求数据包的 header 部分是没有 Cookie 信息的。这时,因为某些功能需要,站点会要求在浏览器本地存储 Cookies,如下面第一次交互请求登陆页面,本地浏览器会存储三个变量: phpbb3_lhc4d_u
, phpbb3_lhc4d_k
, phpbb3_lhc4d_sid
。在这里要重点关注 phpbb3_lhc4d_sid
这个变量,它存储的是服务器端 SessionID 的值(也可以称呼它为会话标识符)。
GET /bbs/ HTTP/1.1
Host: www.website.com.cn
Upgrade-Insecure-Requests: 1
User-Agent: Mosilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Referer: http://www.website.com.cn/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: close
HTTP/1.1 200 OK
Date: Mon, 21 Oct 2019 03:04:44 GMT
Server: Apache
X-Powered-By: PHP/5.5.38-1~dotdeb+7.1
Set-Cookie: phpbb3_lhc4d_u=1; expires=Tue, 20-Oct-2020 03:04:44 GMT; path=/; domain=www.website.com.cn; HttpOnly
Set-Cookie: phpbb3_lhc4d_k=; expires=Tue, 20-Oct-2020 03:04:44 GMT; path=/; domain=www.website.com.cn; HttpOnly
Set-Cookie: phpbb3_lhc4d_sid=8fedbe0e849ab04df7a698b54d011b16; expires=Tue, 20-Oct-2020 03:04:44 GMT; path=/; domain=www.website.com.cn; HttpOnly
Cache-Control: private, no-cache="set-cookie"
Expires: Mon, 21 Oct 2019 03:04:44 GMT
Referer-Policy: same-origin
X-Frame-Options: sameorigin
Vary: Accept-Encoding
Content-Length: 9110
Connection: close
Content-Type: text/html; charset=UTF-8
刷新页面,再次请求同一个页面,可以发现浏览器会自动把之前存储在本地的对应站点 Cookies 全部提交过去。这里提一下,每一个 Cookie 都是有大小限制的,大约在 4k 左右。
GET /bbs/ HTTP/1.1
Host: www.website.com.cn
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mosilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Referer: http://www.website.com.cn/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: phpbb3_lhc4d_u=1; phpbb3_ihc4d_k=; phpbb3_lhc4d_sid=8fedbe0e849ab04df7a698b54d011b16
Connection: close
HTTP/1.1 200 OK
Date: Mon, 21 Oct 2019 03:05:16 GMT
Server: Apache
X-Powered-By: PHP/5.5.38-1~dotdeb+7.1
Cache-Control: private, no-cache="set-cookie"
Expires: Mon, 21 Oct 2019 03:05:16 GMT
Referer-Policy: same-origin
X-Frame-Options: sameorigin
Vary: Accept-Encoding
Content-Length: 8435
Connection: close
Content-Type: text/html; charset=UTF-8
输入正确的账号信息进入 bbs 内部后,会发现服务器更新了存储在本地 Cookie 中的 phpbb3_lhc4d_sid
信息,它是你能留在 bbs 内部板块的通行证,只要访问的时候带上这个 Cookies 就可以畅通无阻。
POST /bbs/ucp/php?mode=login HTTP/1.1
Host: www.website.com.cn
Content-Length: 94
Cache-Control: max-age=0
Origin: http://www.website.com.cn
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mosilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Referer: http://www.website.com.cn/bbs/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: phpbb3_lhc4d_u=1; phpbb3_ihc4d_k=; phpbb3_lhc4d_sid=8fedbe0e849ab04df7a698b54d011b16
Connection: close
username=***********&password=**************************&login=%E7%99%BB%E5%BD%95&redirect=.%2Findex.php%3F
HTTP/1.1 302 Found
Date: Mon, 21 Oct 2019 03:05:48 GMT
Server: Apache
X-Powered-By: PHP/5.5.38-1~dotdeb+7.1
Set-Cookie: phpbb3_lhc4d_u=77; expires=Tue, 20-Oct-2020 03:05:48 GMT; path=/; domain=www.website.com.cn; HttpOnly
Set-Cookie: phpbb3_lhc4d_k=; expires=Tue, 20-Oct-2020 03:05:48 GMT; path=/; domain=www.website.com.cn; HttpOnly
Set-Cookie: phpbb3_lhc4d_sid=bdabd760e3a87aa6b0dfb517c8c7d90a; expires=Tue, 20-Oct-2020 03:05:48 GMT; path=/; domain=www.website.com.cn; HttpOnly
Location: http://www.website.com.cn/bbs/index.php?&sid=bdabd760e3a87aa6b0dfb517c8c7d90a
Cache-Control: max-age=86400
Expires: Tue, 22 Oct 2019 03:05:48 GMT
Referer-Policy: same-origin
X-Frame-Options: sameorigin
Vary: Accept-Encoding
Content-Length: 0
Connection: close
Content-Type: text/html
GET /bbs/index.php HTTP/1.1
Host: www.website.com.cn
Upgrade-Insecure-Requests: 1
User-Agent: Mosilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Referer: http://www.website.com.cn/bbs/index.php?&sid=bdabd760e3a87aa6b0dfb517c8c7d90a
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: phpbb3_lhc4d_u=1; phpbb3_ihc4d_k=; phpbb3_lhc4d_sid=bdabd760e3a87aa6b0dfb517c8c7d90a
Connection: close
HTTP/1.1 200 OK
Date: Mon, 21 Oct 2019 03:06:16 GMT
Server: Apache
X-Powered-By: PHP/5.5.38-1~dotdeb+7.1
Cache-Control: private, no-cache="set-cookie"
Expires: Mon, 21 Oct 2019 03:06:16 GMT
Referer-Policy: same-origin
X-Frame-Options: sameorigin
Vary: Accept-Encoding
Content-Length: 37722
Connection: close
Content-Type: text/html; charset=UTF-8
分析上述 HTTP 数据包的交互:
- Cookie 是使用 HTTP 的头部来传递和交换信息的;
- Set-Cookie 是服务器端给浏览器下发指令的关键字,可以带上一些控制属性;
- Cookie 是浏览器端发送给服务器端消息字段,它只能是 name=value 格式,无法带上其他属性。
二、Cookie有哪些属性
在 RFC6265 中规定,通常 Set-Cookie 响应头部包含头部名称为「Set-Cookie:尾随的 Cookie」。尾随的 Cookie 除了 name, value 这两个必备属性外,还有几个其他的控制属性可选。相关描述摘录如下:
属性 | 描述 |
---|---|
必须属性 | 变量名称:值 |
可选属性 | 描述摘录 |
expires 过期属性 |
如果这个属性的值不能被转换为日期,客户端会忽略该属性。 当同一个 Cookie 两次请求的 expires 值不相同时,新的可能会替换旧的。 |
max-age 最大缓存 时间属性 |
相对过期时间,以秒为单位。 如果该属性的值不是数字,客户端将不做处理。(max-age 优先级大于 expires) |
domain 域属性 |
如果没有设置 Cookie 的 domain 值,该属性的默认值就是创建 Cookie 的网页所在的服务器域名。 |
path 路径属性 |
Cookie 的适用范围为路径设置所限制。 如果服务器忽略路径属性,用户客户端会将请求 uri 路径元素的名录当做缺省值。 |
secure 安全属性 |
它指定了在网络上如何传输 Cookie 值。 默认情况下,Cookie 是不安全的,也就是说,它们是通过一个普通的、不安全的 HTTP 链接传输的。 但是如果将 Cookie 标记为安全的,那么它将只在浏览器和服务器通过 HTTPS 或其他安全协议链接时才被传输。 这个属性只能保证 Cookie 是保密的。 |
HttpOnly 属性 |
HttpOnly 属性限制 Cookie 的 HTTP 请求的适用范围,只能通过 HTTP 访问,不能通过 document.cookie 获取设定为 HttpOnly 的键值,防止 XSS 读取 Cookie。 Cookie 可以同时拥有 HttpOnly 以及安全属性。 |
三、Cookie的应用场景
3.1 HTTP会话状态保持
单纯的Cookie会话管理——
相对安全的Cookie会话机制——
在功能实现上,两者都没有问题,但在实际使用中会发现 Cookie 本身大小数量的限制和信息本地保存会带来一定的安全问题(如可能会暴露给攻击者,或被攻击者重放)。一般使用中不推荐把敏感信息存放于 Cookie 中,而是采用存储会话标识符(SessionID)到 Cookie 里的方式,服务器端可以通过获取这个会话标识符(SessionID)关联当前状态信息。当然了,使用会话标识符并非没有风险,相对于前一种方式已是个好的改善。
打个比方,假设浏览器对应自然人,Cookie 对应身份证,而 Web 服务器端对应的是户籍管理处,那么,Web 服务器(户籍管理处)需要负责给浏览器(自然人)发放 Cookie(身份证)。后期的会话鉴别就是通过保存在客户端浏览器的 Cookie(身份证姓名和号码)里的「会话标识符」实现的,所有的敏感信息都存储在服务器端(户籍管理处),而非交给第三方的浏览器来保管。
3.2 基于Cookie的SSO单点登录
同域和非同域下SSO单点登录
在同域下实现 Cookies 单点登录相对而言简单一些。将 Cookie 的 domain 属性配置好父域名,如 .website.com.cn
,那么这个 Cookie 就可以被类似 a.website.com.cn
,b.website.com.cn
,sso.website.com.cn
,website.com.cn
共享使用。有一点需要特别注意,浏览器请求子域会带上父域的 Cookie,反之则不会。按照约定,我们把需要共享的 Cookie 信息写入到 website.com.cn
这个域名下即可。
以上讨论的是同域情况下的 SSO 单点登录过程。那么跨域情况下又是怎么个原理呢?继续来看流程图,相比上述同域情况会多了跳来跳去的动作。
3.3 跟踪分析用户行为
「大数据」颠覆了某些旧的束缚,有能力去分析全部数据,可得出准确的大方向,而不再仅凭随机采样数据做分析。在互联网行业,「大数据」表现在采集和分析网络用户行为数据,并推送需求上。说到网络用户行为数据的采集,就不能不谈谈 Cookie 在此所扮演的另类角色。
客户端访问个性化设置
网站的各项配置参数可以存储在浏览器本地 Cookies 中,当客户端浏览器再次访问此站点时,直接通过读取 Cookies 信息即可完成相关的个性化配置。当然,局限性也很明显,Cookies 到期失效或换一台电脑,效果就没有了。这是针对临时用户而言的。有会员注册功能的站点,一般将配置保存在 Web 服务器本地数据库,这是持久存在的。
搜索引擎&定向广告推送
在搜索引擎或购物站点搜索产品信息后,再浏览其他网页时,经常在其广告区域显示曾经搜索过的产品或相关信息。哪天小伙伴借你电脑临时一用,打开游览器,你的隐私可能就暴露了,很是讨厌,而这是 Cookie 的功劳。
网络广告公司信息推广
还有一种可能,你打开的某站点很有可能还内嵌了一种叫做网页臭虫的图片。该图片透明且只有一个像素大小,我们称呼它为 Pixel Code,其作用是向所有访问过此页面的客户端写入定制的 Cookie (通过不同站点收集用户爱好习惯)。当你访问与网络广告推广有合作的购物站点时,这些 Cookie 信息就会被读取并被有针对性地推荐广告。整个流程和上面的搜索引擎&定向广告类似,在此不再作图赘述了。
四、Cookie的安全问题探讨
作为一个和 HTTP 协议打交道多年的安全从业者,还是忍不住想讨论下这个问题——
我们知道 Cookie 是保存在用户本地的,由各自的浏览器自行维护。拿 Chrome 浏览器来说,用户可以直接在浏览器内输入 chrome://settings/siteData
来访问它的 Cookies 存储信息,这种方式是通过浏览器 API 获取,可以完整地看到原始 value 信息。如果用户想通过其他手段查看,还可以通过 chrome://version/
发现数据存储的位置,仔细查看后,不难发现,%LOCALAPPDATA%\\Google\\Chrome\\UserData\\Default\\Cookies
就是它的 Cookies 存储文件。这是一个 SQLite 轻型数据库,通过工具打开查询后可发现,domain 是 baidu 的都可以被搜索出来,如下图。这里可以看到,name=value 的 value 部分被浏览器进行了一次加密存储处理。
聊完 Chrome 的本地存储,再说说 Cookie 本身的特点。Cookie 通常可能记录了用户账号 ID、密码、会话标识符 SessionID,这些信息可能是加密,也可能是未加密的。加密存储只能说明其在安全上做了些功课,但被攻击者截获(抓包、XSS)后,是否加密并无太大意义。攻击者无需看懂那串密语,只需要把这些信息照葫芦画瓢丢给服务器,就可以实现越权操作了。而这些安全性问题,可以阅读相关词条(见Ref1)了解学习。
(易树国 | 天存信息)
Ref
- ‘cookie 储存在用户本地终端上的数据’ - 百科词条
- ‘HTTP State Management Mechanism’ - RFC6265
一块小饼干(cookie)的故事-上篇(代码片段)
cookie如果非要用汉语理解的话应该是一段小型文本文件,由网景的创始人之一的卢蒙特利在93年发明。上篇是熟悉一下注册的大致流程,下篇熟悉登录流程以及真正的Cookie实现基本的注册功能我们打开网站,浏览网站,最常见的... 查看详情
$ch5105cookies$线性$dp+$贪心(代码片段)
...一道题:) Sol第一反应就是f[i][j]表示前i个小朋友分j块饼干的最小怨气值但是一个孩子所产生的怨气值并不固定,它与其他孩子获得饼干的情况有关 这里可以用到一个贪心,就是贪婪度大的孩子应该获得尽量多的饼干所以先... 查看详情
贪心算法(代码片段)
455.分发饼干假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子i,都有一个胃口值 gi,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干j,都有一个尺寸sj&n... 查看详情
leetcode0455.分发饼干(代码片段)
【LetMeFly】455.分发饼干力扣题目链接:https://leetcode.cn/problems/assign-cookies/假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子i,都有一个胃口值 g[i],... 查看详情
cookie从入门到进阶:一文彻底弄懂其原理以及应用(代码片段)
...ff1f;FortunecookieCookie,它的名字源自一种叫Fortunecookie的饼干,这种饼干里面有一张写着精辟句子的小纸条。在浏览器中,Cookie是服务器让浏览器帮忙携带信息的手段,就像饼干里的纸条,浏览器会储存它,... 查看详情
cookie从入门到进阶:一文彻底弄懂其原理以及应用(代码片段)
...ff1f;FortunecookieCookie,它的名字源自一种叫Fortunecookie的饼干,这种饼干里面有一张写着精辟句子的小纸条。在浏览器中,Cookie是服务器让浏览器帮忙携带信息的手段,就像饼干里的纸条,浏览器会储存它,... 查看详情
django组件-cookie与session(代码片段)
... 1.什么叫Cookie Cookie翻译成中文是小甜点,小饼干的意思。在HTTP中它表示服务器送给客户端浏览器的小甜点。其实Cookie是key-value结构,类似于一个python中的字典。随着服务器端的响应发送给客户端浏览器。然后客户端... 查看详情
cookies(代码片段)
...以前的DP专题里也说过,同样没打代码圣诞老人共用有个饼干,准备全部分给N个孩子。每个孩子有一个贪婪度,第i个孩子的贪婪度为g[i]。如果有a[i]个孩子拿到的饼干数比第i个孩子多,那么第i个孩子会产生g[i]*a[i]的怨气。给定N... 查看详情
贪心分饼干/分水杯(代码片段)
给孩子们分一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子i,都有一个胃口值g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干j,都有一个尺寸s[j]。如果s[j]>=g[i],我们可以将这个饼干j分配给孩... 查看详情
ch5105cookies贪心+dp(代码片段)
通过邻项交换法可知,怨气值大的孩子分得的饼干数也应该多(否则交换之后得到的解更优)。观察目标函数的性质,可知目标函数本身是由孩子饼干数的相对大小得到,因此此题中关注的是相对大小。状态设计:(dp[i][j])表示... 查看详情
前端存储(代码片段)
...低之分,只有对特定任务是否合适。cookie简介cookie:"小饼干,小甜品"参与http通信。因为在http中可以看到cookie,所以易受到攻击。面向路径。只作用于当前路径(页面)。每个cookie<4K运行机制使用总结设置cookie的值functionsetCoo... 查看详情
455.分发饼干(代码片段)
题目假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子i,都有一个胃口值gi,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干j,都有一个尺寸sj。如果sj>=gi... 查看详情
从一个小demo开始,体验“api经济”的大魅力
写在前面“API经济”这个词是越来越火了,但是"API经济"具体指的是什么,相信很多人还没有个明确的认识。不过今天我可不打算长篇大论的去讲解一些概念,我们就以“电话号码归属地查询”的小demo为例,来看看到底... 查看详情
455.分发饼干贪心算法(代码片段)
455.分发饼干一、题目二、参考代码一、题目假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子i,都有一个胃口值g[i],这是能让孩子们满足胃口的饼干的... 查看详情
455.分发饼干贪心算法(代码片段)
455.分发饼干一、题目二、参考代码一、题目假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子i,都有一个胃口值g[i],这是能让孩子们满足胃口的饼干的... 查看详情
noip模拟:饼干(简单规律推导)
...>0,小矩阵不能超过网格的边界。然后把右上一半都放上饼干。下图是当x=1或2的时候:每个格子不能放2个饼干。问最少能空几个格子不放饼干。输入格式从文件cookies.in中读入数据。第一行一个整数n。输出格式输出到文件cookies.ou... 查看详情
贪心-分发饼干(代码片段)
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有... 查看详情
leetcode455.分发饼干(代码片段)
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有... 查看详情