cookie到底是怎样存储数据的
cookie
随着Web应用程序的出现,也产生了对于能够直接在客户端上存储用户信息能力的要求。想法很合乎逻辑,属于某个特定用户的信息应该存在该用户的机器上。无论是登录信息、偏好设定或其他数据,Web应用提供者发现他们在找各种方式将数据存在客户端上。
HTTP Cookie,通常直接叫做cookie,最初是在客户端用于存储会话信息的。该标准要求服务器对任意HTTP请求发送Set-Cookie
HTTP头作为响应的一部分,其中包含会话信息。例如,这种服务器响应的头可能如下:
HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name=value
Other-header: other-header-value
这个HTTP响应设置以name
为名称、以value
为值的一个cookie,名称和值在传送时都必须是URL编码的。浏览器会存储这样的会话信息,并在这之后,通过为每个请求添加Cookie HTTP头将信息发送回服务器,如下所示:
GET /index.html HTTP/1.1
Cookie: name=value
Other-header: other-header-value
发送回服务器的额外信息可以用于唯一验证客户来自于发送的哪个请求。
cookie在性质上是绑定在特定的域名下的。当设定了一个cookie后,再给创建它的域名发送请求时, 都会包含这个cookie。这个限制确保了储存在cookie中的信息只能让批准的接受者访问,而无法被其他域访问。
浏览器中对于cookie的尺寸也有限制。大多数浏览器都有大约4096B(加减1)的长度限制。为了最佳的浏览器兼容性,最好将整个cookie长度限制在4095B(含4095)以内。尺寸限制影响到一个域下所有的cookie,而并非每个cookie单独限制。
如果你尝试创建超过最大尺寸限制的cookie,那么该cookie会被悄无声息地丢掉。注意,虽然一个字符通常占用一字节,但是多字节情况则有不同。
cookie的成分
cookie由浏览器保存的以下几块信息构成。
名称:一个唯一确定cookie的名称。cookie名称是不区分大小写的,所以myCookie和MyCookie被认为是同一个cookie。然而,实践中最好将cookie名称看作是区分大小写的,因为某些服务离线应用与客户端存储器会这样处理cookie。cookie的名称必须是经过URL编码的。
值:储存在cookie中的字符串值。值必须被URL编码。域:cookie对于哪个域是有效的。所有向该域发送的请求中都会包含这个cookie信息。这个值可以包含子域(subdomain,如www.wrox.com),也可以不包含它(如.wrox.com,则对于wrox.com的所有子域都有效)。如果没有明确设定,那么这个域会被认作来自设置cookie的那个域。
路径:对于指定域中的那个路径,应该向服务器发送cookie。例如,你可以指定cookie只有从
http://www.wrox.com/books/
中才能访问,那么http://www.wrox.com
的页面就不会发送cookie信息,即使请求都是来自同一个域的。失效时间:表示cookie何时应该被删除的时间戳(也就是,何时应该停止向服务器发送这个cookie)。默认情况下,浏览器会话结束时即将所有cookie删除;不过也可以自己设置删除时间。这个值是个GMT格式的日期(Wdy,DD-Mon-YYYY HH:MM:SSGMT),用于指定应该删除cookie的准确时间。因此,cookie可在浏览器关闭后依然保存在用户的机器上。如果你设置的失效日期是个以前的时间,则cookie会被立刻删除。
安全标志:指定后,cookie只有在使用SSL连接的时候才发送到服务器。例如,cookie信息只能发送给
https://www.wrox.com
,而http://www.wrox.com
的请求则不能发送 cookie。
node中的HTTP模块会将所有的报文字段解析到 req.headers
上,那么Cookie就是 req.headers.cookie
。根据规范中的定义,Cookie值的格式是 key=value; key2=value2
形式的,如果我们需要Cookie,解析它也十分容易,如下所示:
let parseCookie = (cookie) => {let cookies = {};if (!cookie) {return cookies;}let list = cookie.split(';');for (let i = 0; i < list.length; i++) {let pair = list[i].split('=');cookies[pair[0].trim()] = pair[1];}return cookies;
};function handle (req, res) {req.cookies = parseCookie(req.headers.cookie);// 处理请求
}
响应的Cookie值在 Set-Cookie
字段中。它的格式与请求中的格式不太相同,规范中对它的定义如下所示:
Set-Cookie: name=value; Path=/; Expires=Sun, 23-Apr-23 09:01:35 GMT; Domain=.domain.com;
其中 name=value
是必须包含的部分,其余部分皆是可选参数。这些可选参数将会影响浏览器在后续将Cookie发送给服务器端的行为。以下为主要的几个选项。
path
表示这个Cookie影响到的路径,当前访问的路径不满足该匹配时,浏览器则不发送这个Cookie。Expires
和Max-Age
是用来告知浏览器这个Cookie何时过期的,如果不设置该选项,在关闭浏览器时会丢失掉这个Cookie。如果设置了过期时间,浏览器将会把Cookie内容写入到磁盘中并保存,下次打开浏览器依旧有效。Expires
的值是一个UTC格式的时间字符串,告知浏览器此Cookie何时将过期,Max-Age
则告知浏览器此Cookie多久后过期。前者一般而言不存在问题,但是如果服务器端的时间和客户端的时间不能匹配,这种时间设置就会存在偏差。为此,Max-Age
告知浏览器这条Cookie多久之后过期,而不是一个具体的时间点。HttpOnly
告知浏览器不允许通过脚本document.cookie
去更改这个Cookie值,事实上,设置HttpOnly
之后,这个值在document.cookie
中不可见。但是在HTTP请求的过程中,依然会发送这个Cookie到服务器端。Secure
。当Secure
值为true
时,在HTTP中是无效的,在HTTPS中才有效,表示创建的Cookie只能在HTTPS连接中被浏览器传递到服务器端进行会话验证,如果是HTTP连接则不会传递该信息,所以很难被窃听到。
知道Cookie在报文头中的具体格式后,下面我们将Cookie序列化成符合规范的字符串,相关代码如下:
let serialize = function (name, val, opt) {let pairs = [`${name}=${val}`];opt = opt || {};if (opt.maxAge) pairs.push('Max-Age = ' + opt.maxAge);if (opt.domain) pairs.push('Domain=' + opt.domain);if (opt.path) pairs.push('Path=' + opt.path);if (opt.expires) pairs.push('Expires=' + opt.expires.toUTCString());if (opt.httpOnly) pairs.push('HttpOnly');if (opt.secure) pairs.push('Secure');return pairs.join('; ');
};
客户端收到这个带 Set-Cookie
的响应后,在之后的请求时会在Cookie字段中带上这个值。值得注意的是, Set-Cookie
是较少的,在报头中可能存在多个字段。为此 res.setHeader
的第二个参数可以是一个数组,如下所示:
res.setHeader('Set-Cookie', [serialize('foo', 'bar'), serialize('baz', 'val')])
Cookie的性能影响
5. 减小Cookie的大小
6. 为静态组件使用不同的域名
7. 减少DNS查询
Cookie可以在前后端进行修改,因此数据就极容易被篹改和伪造。为了解决Cookie敏感数据的问题,Session应运而生。Session的数据只保留在服务器端,客户端无法修改,这样数据的安全性得到一定的保障,数据也无须在协议中每次都被传递。
虽然在服务器端存储数据十分方便,但是如何将每个客户和服务器中的数据一一对应起来,这里有常见的基于Cookie来实现用户和数据的映射。
虽然将所有数据都放在Cookie中不可取,但是将口令放在Cookie中还是可以的。因为口令一旦被篹改,就丢失了映射关系,也无法修改服务器端存在的数据了。并且Session的有效期通常较短,普遍的设置是20分钟,如果在20分钟内客户端和服务器端没有交互产生,服务器端就将数据删除。由于数据过期时间较短,且在服务器端存储数据,因此安全性相对较高。那么口令是如何产生的呢?
一旦服务器端启用了Session,它将约定一个键值作为Session的口令,这个值可以随意约定。每个请求到来时,检查Cookie中的口令与服务器端的数据,如果过期,就重新生成,如下所示:
const http = require('http');
const crypto = require('crypto')
const queryString = require('querystring')let parseCookie = (cookie) => {return queryString.parse(cookie, '; ') || {}
};// 服务器存储的cookie
let sessions = {};
let key = 'session_id';
const EXPIRES = 20 * 60 * 1000;
const SECRET = '0123456789'let generate = function () {let session = {};session.id = String((new Date()).getTime() + Math.random())session.cookie = {expire: (new Date()).getTime() + EXPIRES};sessions[session.id] = session;return session;
};// 将值通过私钥签名,由.分割原值和签名
let sign = function (val, secret) {return val + '.' + crypto.createHmac('sha256', secret).update(val).digest('base64').replace(/\=+$/, '');
};// 取出口令部分进行签名,对比用户提交的值
let unsign = function (val, secret) {var str = val.slice(0, val.lastIndexOf('.'));return sign(str, secret) == val ? str : false;
};let server = http.createServer((req, res) => {req.cookies = parseCookie(req.headers.cookie)let id = req.cookies[key];if (!id) {// 一般会根据用户的ID来生成带签名的cookie这里只是简单的处理req.session = generate();res.setHeader('Set-Cookie', `${key}=${sign(req.session.id, SECRET)}`);res.end('login')} else {id = unsign(id, SECRET)let session = sessions[id];if (session) {if (session.cookie.expire > (new Date()).getTime()) {// 更新超时时间session.cookie.expire = (new Date()).getTime() + EXPIRES;req.session = session;} else {// 超时了,删除旧的数据,并重新生成delete sessions[id];req.session = generate();}} else {// 如果session过期或口令不对,重新生成sessionreq.session = generate();}res.setHeader('Set-Cookie', `${key}=${sign(req.session.id, SECRET)}`);res.end('home')}
})server.listen(8080, () => {console.log('port in 8080')
})
这里提到的Session的安全,就主要指如何让这个口令更加安全。有一种做法是将这个口令通过私钥加密进行签名,使得伪造的成本较高。客户端尽管可以伪造口令值,但是由于不知道私钥值,签名信息很难伪造。如此,我们只要在响应时将口令和签名进行对比,如果签名非法,我们将服务器端的数据立即过期即可。
cookie到底是怎样存储数据的相关推荐
- es客户端工具_超越 Cookie:当今的客户端数据存储
作者:Adam Giese 翻译:疯狂的技术宅 原文:https://blog.logrocket.com/beyond-cookies-todays-options-for-client-side- ...
- js存储数据cookie,localhost,sessionstorage
存储数据 1.cookie.设置 属性不会被覆盖 特点:内存小只有4kb,可以设置过期时间.而且有些域名会限制 网站里面查找cookie:1.检查 2.Application 3.cookies ...
- 前端 在浏览器端存储数据
在前端中存储数据大致有三种模式: 1:cookie:但是cookie的空间只有4k,所以很少使用: 2:sessionStorage: 是在本地存储数据,其空间有最多可以达到5M,但是生命周期是浏览器 ...
- mysql 把表的值用来计算_mysql一张表到底能存多少数据?
前言 程序员平时和mysql打交道一定不少,可以说每天都有接触到,但是mysql一张表到底能存多少数据呢?计算根据是什么呢?接下来咱们逐一探讨 知识准备 数据页 在操作系统中,我们知道为了跟磁盘交互, ...
- .net core2.0下使用Identity改用dapper存储数据
前言. 已经好多天没写博客了,鉴于空闲无聊之时又兴起想写写博客,也当是给自己做个笔记.过了这么些天,我的文笔还是依然那么烂就请多多谅解了.今天主要是分享一下在使用.net core2.0下的实际遇到的 ...
- 一个mysql可以存多少数据类型_mysql一张表到底能存多少数据?
前言 程序员平时和mysql打交道一定不少,可以说每天都有接触到,但是mysql一张表到底能存多少数据呢?计算根据是什么呢?接下来咱们逐一探讨 知识准备 数据页 在操作系统中,我们知道为了跟磁盘交互, ...
- Go语言编程笔记16:存储数据
Go语言编程笔记16:存储数据 图源:wallpapercave.com 几乎任何程序都绕不开读写数据,只不过具体的数据存储介质和方式有所不同.本篇文章将从多种数据存储方式进行探讨各种存储方式如何实现 ...
- 除了缓存,浏览器还有哪些存储数据的方式?
一.简介 浏览器提供3种用于数据存储的 JavaScript APIs:cookie .Web Storage API.IndexedDB. 二.cookie cookie 是最早期 用于存储 键/值 ...
- 浏览器对象存储数据详解
浏览器对象存储数据详解 前言 随着需求的发展,浏览器的功能正变的越来越强大,在本地存储数据可以极大的方便人们进行各种操作,如localStroage/sessionStroage等,下面我就记录在项目 ...
最新文章
- amp 保留指定位c语言,C语言位运算符学习
- iOS 中的网络请求 (同步请求、异步请求、GET请求、POST请求)
- 通过Easy-UI 树插件实现ArcGIS多个服务图层的图层控制
- 解决JupyterLab中tqdm_notebook进度条不显示问题
- PHP集锦点,php 函数集锦
- 使用vs2019和pyinstaller将py文件打包成一个exe文件(含图标),pyinstaller安装失败解决方案
- 高亮插件Highlighting的使用
- 2021 自编译最新稳定版 裴讯 Phicomm K2 Openwrt 固件
- python简单的计算方法_用python实现简单的有限元方法(二)
- php嵌入wowza,Wowza服务器上的PHP脚本无法通过HTTP身份验证?
- Cobalt Strike远控木马分析
- 雪夜拾到一部破旧的手机
- 《断舍离(心灵篇)》-[日]山下英子
- python sklearn逻辑回归 sgd和lr_LR逻辑回归模型的原理、公式推导、Python实现和应用...
- OpenHarmony,一路前行,为了奇迹
- 用Python分析《令人心动的offer2》的13万条弹幕,网友们都在吐槽什么?
- python中turtle画笑脸_怎么用python实现画笑脸
- 云计算之路-阿里云上:一夜之间竞价服务器全没了
- SuperMap地图发布
- nginx安装配置 linux