说说关于 Cookie 和 Session 方方面面 - 文章教程

说说关于 Cookie 和 Session 方方面面

发布于 2020-11-28 字数 8848 浏览 1033 评论 0

Cookie 和 Session 对于 Web 开发来说,最熟悉不过了,但是有些小技巧和功能你可能还不知道,这篇文章说说关于 Cookie 和 Session 方方面面。

http 请求报文格式

请求行:方法 路径 http 版本,e.g:GET /images/logo.png HTTP/1.1

请求头部:Accept-Language 等 空行 可选消息主体 注:请求行和其他头部必须以 ; 结尾,空白行只有。其中’Host’域区分实现区分虚拟主机功能,在 http/1.0 中可选,http/1.1 中强制。

http 响应报文格式

说说关于 Cookie 和 Session 方方面面

状态行:http 版本 状态码 状态:e.g:HTTP/1.1 200 OK 响应消息头部:如 Content-Type:text/html 空行 可选消息主体 注:状态行和其他头部必须以 ; 结尾,空白行只有

http 方法

GET:请求指定的资源
HEAD:请求和GET方法一样的响应头文件,但没有主体
POST:请求接受请求中的实体作为一个新的当前URI定义的网络资源的下级。如可能是提交表单,公告板信息,邮件列表,评论,或者提交的表单
PUT:要求封闭的整体被存储在指定URI下,已存在则替换资源,不存在则创建
DELETE:删除指定资源
TRACE:回显收到的请求以确保中间媒介没有修改或增加内容
OPTIONS:返回服务器支持的HTTP方法,通过请求'*'而不是指定的资源
CONNECT:转换链接到一个透明的TCP/IP隧道来实现HTTPS加密传输等,以实现通过未加密的HTTP代理的安全
PATCH:修改部分资源

http 请求报文头部

Accept:    e.g:text/plain 接受的内容类型
Acccept-Charset:    e.g:    utf-8 接受的字符集类型
Accept-Encoding:    e.g:    gzip,deflate 接受的编码
Accept-Language:    e.g:    en-US 接受的语言
Accept-Datetime:    e.g:    Thu, 31 May 2007 20:35:00 GMT 接受的时间类型
Cache-Control:    e.g:    no-cache 指定缓存类型
Connection:        e.g:    Keep-alive|UpGrade 链接类型
Upgrade:    e.g:    HTTP/2.0, SHTTP/1.3, TRC/6.9要求服务器升级到其他协议
User-Agent:    e.g:    Mozilla/5.0 (X11;Linux x86_64;rv:12.0) Gecko/20100101 Firefox/21.0 用户代理,即浏览器信息
Cookie: 被服务端set-cookie指令设定的cookie
Host:    域名和端口,端口可省,1.1中强制要求Host存在

http 响应报文头部

Accept-Patch:    e.g:    text/example;charset=utf-8 支持的补丁文档格式
Age:        e.g:    12     在缓存代理中存在的秒数
Allow:e.g:         GET, HEAD 支持的方法
Cache-Control:    e.g:max-age=3600 客户端可以缓存这个页面的秒数
ETag:        e.g:    "737060cd8c284d8af7ad3082f209582d" 指定资源的标识符,通常为消息摘要
Expires:        e.g:    Thu, 01 Dec 1994 16:00:00 GMT 过期时间
Retry-After 资源暂时不可得时的重试秒数
Server         服务器名称
Set-Cookie     设置cookie

Cookie 技术

Cookie是由客户端保存的小型文本文件,其内容为一系列的键值对。 Cookie是由HTTP服务器设置的,保存在浏览器中, 在用户访问其他页面时,会在HTTP请求中附上该服务器之前设置的Cookie。

Cookie 的实现标准定义在 RFC2109: HTTP State Management Mechanism中。 那么Cookie是怎样工作的呢?下面给出整个Cookie的传递流程: 浏览器向某个URL发起HTTP请求(可以是任何请求,比如GET一个页面、POST一个登录表单等) 对应的服务器收到该HTTP请求,并计算应当返回给浏览器的HTTP响应。 HTTP响应包括请求头和请求体两部分,可以参见:读 HTTP 协议。

在响应头加入Set-Cookie字段,它的值是要设置的Cookie。 在RFC2109 6.3 Implementation Limits中提到: UserAgent(浏览器就是一种用户代理)至少应支持300项Cookie, 每项至少应支持到4096字节,每个域名至少支持20项Cookie。 浏览器收到来自服务器的HTTP响应。 浏览器在响应头中发现Set-Cookie字段,就会将该字段的值保存在内存或者硬盘中。

Set-Cookie字段的值可以是很多项Cookie,每一项都可以指定过期时间Expires。 默认的过期时间是用户关闭浏览器时。 浏览器下次给该服务器发送HTTP请求时, 会将服务器设置的Cookie附加在HTTP请求的头字段Cookie中。 浏览器可以存储多个域名下的Cookie,但只发送当前请求的域名曾经指定的Cookie, 这个域名也可以在Set-Cookie字段中指定)。

服务器收到这个HTTP请求,发现请求头中有Cookie字段, 便知道之前就和这个用户打过交道了。 过期的Cookie会被浏览器删除。 总之,服务器通过Set-Cookie响应头字段来指示浏览器保存Cookie, 浏览器通过Cookie请求头字段来告诉服务器之前的状态。 Cookie中包含若干个键值对,每个键值对可以设置过期时间。

cookie 类别

session cookie

会话cookie,用户浏览网站时存在临时内存里,用户关闭浏览器时就会删除之,会话cookie没有过期时间,浏览器,以此区别会话cookie

persistent cookie

长期cookie, 在指定时间或者指定的时间长度后过期,在用户浏览cookie所属的网站或者含有那个网站链接(比如广告)时,cookie就会被发送给服务器。长期cookie也叫跟踪cookie,可以根据大量含有链接的网站了解用户习惯。也可以作为已登录的凭证

secure cookie

安全cookie, 只能被加密传输,如 https

httponly cookie

只能通过 http/https 传输使用的cookie,不能被不含http api的应用如javascript获取,避免跨站脚本攻击,无法避免跨站追踪或者跨站伪造请求。此cookie被大多数现代浏览器支持

third-party cookie

第三方cookie, 不属于访问的网站的服务器的cookie,比如广告链接的cookie

Supercookie

含有顶级域名的cookie,如.com或公用后缀如.co.uk.普通cookie有一个特定的域名,比如example.com,超级cookie可以恶意影响发向普通cookie的请求

supercookie(other uses)

不信赖http cookies的追踪技术,两种这种cookie机制最初在2011年发现在微软的网站上,后来被移除

Zombie cookie

被删除后自动重新创建的 cookie, 由客户端脚本完成,它把 cookie 存储在不同地方,如,flash local storage, html5 storage, 以及其他客户端存储位置,一旦发现 cookie 消失就利用存储在别处的 cookie 重新创建之

cookie 属性

cookie 为 ASCII 字符,除了 ‘,’ , ‘;’ , ‘=’ 和空白符

cookie 也有其他属性,但浏览器只发送名-值对,其他属性由浏览器理解,决定是否删除,拒绝或发送 cookie

域名(domain) 和路径 (path)

如果没给出域名和路径,默认为当前域名和路径,有域名和没域名的区别是没有域名 cookie 只会发送到当前域名,而有域名则其所有子域名被访问时都会发送 cookies,域名前 ‘.’ 可选,带上与 RFC2109 兼容

过期时间和最大时间

格式:Wdy, DD Mon YYYY HH:MM:SS GMT

cookie 也可由客户端创建,对浏览器的要求如下: 支持最大 4096 个字节的 cookies 每个域名可最少存储 50 个 cookies 可以存储最少 3000 个 cookies

Cookie 的安全隐患

Cookie提供了一种手段使得HTTP请求可以附加当前状态, 现今的网站也是靠Cookie来标识用户的登录状态的: 用户提交用户名和密码的表单,这通常是一个POST HTTP请求。

服务器验证用户名与密码,如果合法则返回200(OK)并设置Set-Cookie为authed=true。 浏览器存储该Cookie。 浏览器发送请求时,设置Cookie字段为authed=true。

服务器收到第二次请求,从Cookie字段得知该用户已经登录。 按照已登录用户的权限来处理此次请求。

这里面的问题在哪里? 我们知道可以发送HTTP请求的不只是浏览器,很多HTTP客户端软件(包括curl、Node.js)都可以发送任意的HTTP请求,可以设置任何头字段。 假如我们直接设置Cookie字段为authed=true并发送该HTTP请求, 服务器岂不是被欺骗了?这种攻击非常容易,Cookie是可以被篡改的!

Cookie 防篡改机制

服务器可以为每个Cookie项生成签名,由于用户篡改Cookie后无法生成对应的签名, 服务器便可以得知用户对Cookie进行了篡改。一个简单的校验过程可能是这样的: 在服务器中配置一个不为人知的字符串(我们叫它Secret),比如:x$sfz32。

当服务器需要设置Cookie时(比如authed=false),不仅设置authed的值为false, 在值的后面进一步设置一个签名,最终设置的Cookie是authed=false|6hTiBl7lVpd1P。

签名6hTiBl7lVpd1P是这样生成的:Hash(‘x$sfz32’+’true’)。 要设置的值与Secret相加再取哈希。 用户收到HTTP响应并发现头字段Set-Cookie: authed=false|6hTiBl7lVpd1P。

用户在发送HTTP请求时,篡改了authed值,设置头字段Cookie: authed=true|???。 因为用户不知道Secret,无法生成签名,只能随便填一个。 服务器收到HTTP请求,发现Cookie: authed=true|???。

服务器开始进行校验: Hash(‘true’+’x$sfz32’),便会发现用户提供的签名不正确。 通过给Cookie添加签名,使得服务器得以知道Cookie被篡改。然而故事并未结束。

因为Cookie是明文传输的, 只要服务器设置过一次authed=true|xxxx我不就知道true的签名是xxxx了么, 以后就可以用这个签名来欺骗服务器了。因此Cookie中最好不要放敏感数据。 一般来讲Cookie中只会放一个Session Id,而Session存储在服务器端。

Session 的实现机制

Session 是存储在服务器端的,避免了在客户端Cookie中存储敏感数据。 Session 可以存储在HTTP服务器的内存中,也可以存在内存数据库(如redis)中, 对于重量级的应用甚至可以存储在数据库中。

我们以存储在redis中的Session为例,还是考察如何验证用户登录状态的问题。 用户提交包含用户名和密码的表单,发送HTTP请求。

服务器验证用户发来的用户名密码。 如果正确则把当前用户名(通常是用户对象)存储到redis中,并生成它在redis中的ID。 这个ID称为Session ID,通过Session ID可以从Redis中取出对应的用户对象, 敏感数据(比如authed=true)都存储在这个用户对象中。 设置Cookie为sessionId=xxxxxx|checksum并发送HTTP响应, 仍然为每一项Cookie都设置签名。

用户收到HTTP响应后,便看不到任何敏感数据了。在此后的请求中发送该Cookie给服务器。 服务器收到此后的HTTP请求后,发现Cookie中有SessionID,进行放篡改验证。 如果通过了验证,根据该 ID 从 Redis 中取出对应的用户对象, 查看该对象的状态并继续执行业务逻辑。

Web应用框架都会实现上述过程,在Web应用中可以直接获得当前用户。 相当于在HTTP协议之上,通过Cookie实现了持久的会话。这个会话便称为Session。

如果你对这篇文章有疑问,欢迎到本站 社区 发帖提问或使用手Q扫描下方二维码加群参与讨论,获取更多帮助。

扫码加入群聊

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

目前还没有任何评论,快来抢沙发吧!

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

2583 文章
29 评论
84935 人气
更多

推荐作者

清欢

文章 1 评论 0

贱贱哒

文章 3 评论 0

悸初

文章 2 评论 0

西瓜杏

文章 0 评论 0

各自安好

文章 0 评论 0