前端网络基础 - Cookie

目录

HTTP协议的无状态性

HTTP协议三大特点

HTTP协议无状态性的优缺点

HTTP协议无状态性的解决方案

Cookie是啥

Cookie技术的具体工作流程

cookie的组成

服务器端手动生成cookie

浏览器端自动保存以及发送cookie

cookie的有效期

expires和max-age的区别

expires取值要求

max-age取值要求

expires和max-age的优先级

cookie有效期的对于cookie的影响

cookie的作用范围

domain

path

cookie的安全限定

document.cookie引发的cookie安全问题及解决方案httponly

http协议明文传输引发的cookie安全问题及解决方案secure

跨站请求cookie及解决方案samesite

Cookie实现身份认证小案例

需求描述

需求实现

express官推中间件cookie-parser模拟实现


HTTP协议的无状态性

HTTP协议三大特点

  • 请求应答模式
  • 无连接
  • 无状态

“请求应答模式” : 每个HTTP请求都必然对应一个HTTP响应。

“无连接” :客户端在发送HTTP请求前需要与服务器建立连接,而在收到服务器的HTTP响应后,就会断开连接,即一次连接只支持一次HTTP请求应答。无连接可以理解为HTTP协议不会持久维持连接。

“无状态”:HTTP协议本身不支持每次HTTP请求应答的状态的保存,只负责状态的传递。无状态可以理解为HTTP协议无法保存状态。

HTTP协议无状态性的优缺点

HTTP是一种简单(请求报文,响应报文结构简单清晰,无状态性),灵活(请求头,响应头容易扩展),对服务器友好(无连接特性,不会持久占用服务器的连接资源)的协议。

无状态性 保证了HTTP协议的简单性。假设我们需要HTTP协议支持HTTP请求应答的状态的保存,那么HTTP协议至少需要考虑如下问题:

  1. 状态的保存(介质选择内存还是硬盘,位置选择服务器还是浏览器)
  2. 状态的管理(增删改查)
  3. 状态的安全性(防盗,防伪,防乱发)

而HTTP协议加入了以上需求,必然导致HTTP协议设计的复杂性变高,使用难度变大。

那么HTTP协议的无状态性的缺点是啥呢?

在业务上关联的两次HTTP请求应答,比如后一次HTTP请求需要前一次HTTP响应的数据。由于HTTP无状态性,前一次的HTTP响应数据无法被保存,所以后一次HTTP请求也无法获得。对应的业务场景就是身份认证。

HTTP协议无状态性的解决方案

解决方案很简单,既然HTTP协议本身不支持保存状态,那么就在浏览器和服务器实现状态的保存。下面示例是浏览器与服务器对于登录状态的保存:

  1. 浏览器发起HTTP请求进行登录
  2. 服务器收到请求,进行身份认证,认证用户名密码成功,返回HTTP响应,响应包含登录成功状态
  3. 浏览器收到响应,并保存登录成功的状态
  4. 浏览器发起HTTP请求进行个人资料查询,并携带登录成功的状态
  5. 服务器收到请求,进行身份认证,认证登录状态成功,进行个人资料查询

分析上面流程,可得:

浏览器需要支持:登录状态的保存,以及将登录状态加入HTTP请求

服务器需要支持:登录状态的生成,以及对登录状态的验证

以上这种HTTP无状态性的解决方案就是Cookie技术。

Cookie是啥

Cookie是浏览器端创造的,用于解决HTTP协议无状态性的技术。

Cookie技术的具体工作流程

浏览器与服务器约定,如果某次HTTP响应中的状态需要保存,则

  1. 服务器需要手动将状态数据设置为HTTP响应头Set-Cookie的值
  2. 浏览器在收到服务器的HTTP响应后,会自动解析出HTTP响应头Set-Cookie的值,并按需保存在内存或硬盘中。
  3. 浏览器再次请求服务器时,会自动将之前保存的Set-Cookie值从内存或硬盘中取出,并加入到HTTP请求头Cookie中,发送到服务器
  4. 服务器收到浏览器请求后,需要手动解析HTTP请求头Cookie的值,进行状态校验等操作

我们这里将“状态”简称为cookie。

总结可得:

  • cookie由服务器生成,由浏览器保存。
  • 服务器通过“Set-Cookie”响应头将cookie传递给浏览器,浏览器可以自动解析“Set-Cookie”响应头,并自动保存解析到的cookie。
  • 浏览器通过“Cookie”请求头将cookie传递给服务器,服务器需要手动解析“Cookie”请求头,并手动对解析到的cookie进行验证。

cookie的组成

cookie可以分为两部分内容,一部分是cookie具体内容,一部分是cookie的描述属性

cookie的具体内容就是一个键值对,表现为 cookie_name=cookie_value形式

而cookie的描述属性,主要用于描述cookie的生命周期,作用范围,以及安全限定,包含如下

expirescookie失效日期,不设置,表示cookie生命周期是本次会话
max-agecookie存活时间,不设置,表示cookie生命周期是本次会话,当同时存在expires和max-age时,以max-age为准
domaincookie作用域,默认为服务器域名
pathcookie作用路径,默认根路径“/”
httponlycookie仅用于http请求,不可通过javascript获取
secure浏览器只有在加密协议 HTTPS 下,才能将这个 Cookie 发送到服务器。
samesite

跨站cookie是否发送

  • strict
  • lax
  • none

服务器端手动生成cookie

cookie由服务器端生成,服务器端通过HTTP响应头Set-Cookie将cookie传递给浏览器保存。

浏览器支持解析的cookie格式为 cookieName=cookieValue;cookiePropName=cookiePropValue

如下示例

上面代码,生成了一个cookie,cookie名为用户名,cookie值为密码,cookie存活时间为60s,cookie作用域为qfc.com及其子域,cookie作用路径为根路径,cookie不允许跨站

浏览器端自动保存以及发送cookie

cookie由浏览器端保存,浏览器请求服务器/login接口后,可以自动从HTTP响应头Set-Cookie获取cookie值

浏览器会将解析到cookie值自动保存在内存或硬盘中

当浏览器再次发送HTTP请求到qfc.com及其子域时就会自动携带该cookie值(前提是cookie未失效)

cookie的有效期

expires和max-age的区别

cookie有两个属性控制有效期,分别是expires和max-age,二者作用是相同的,只是取值不同。

expires取值要求

expires取值为标准的日期字符串,浏览器会解析日期字符串作为cookie失效日期,服务器可以使用Date.prototype.toUTCString()获取标准的日期字符串。注意一定要是标准的日期字符串,否则浏览器会解析失败。

max-age取值要求

max-age取值为正整数,默认单位为秒,表示cookie被浏览器保存后存活的时长。

expires和max-age的优先级

如果同时设置了expires和max-age,则以max-age为准

可以发现max-age设置了30s失效,expires设置了60s失效,最终以30s失效为准,这里相差不足30s是因为我截图慢了。

cookie有效期的对于cookie的影响

cookie的有效期主要影响了

  • cookie在浏览器端存储的位置
  • cookie在浏览器端的生命周期

如果我们通过expires或max-age设置了cookie有效期,则浏览器端会将cookie保存在硬盘中,当有效期到了后,浏览器端就会自动清除硬盘中的失效cookie。

如果我们未设置expires和max-age值,则浏览器端会将cookie保存在内存中,cookie的生命周期是本次会话,即浏览器关闭时cookie会被清除。

可以发现此时cookie生命周期值为Session,表示会话生命周期

我们需要注意的是会话生命周期是浏览器关闭为结束点,而不是当前网页关闭。实际上会话级别cookie保存在浏览器进程的内存中,当浏览器关闭,意味着浏览器进程的死亡,以及进程所占用的内存,CPU的释放,因此保存在浏览器进程内存中的cookie也会被释放。

cookie的作用范围

domain

cookie的domain属性旨在帮助浏览器认识当前cookie用于哪些域名或IP地址对应的服务器,但是服务器端设置cookie的domain以及浏览器端使用cookie的domain需要注意如下

服务器HTTP响应头Set-Cookie中domain设置是否合法依据如下:

  • 如果domain设定的IP地址就是服务器所在IP地址,则合法
  • 如果domain设定的域名就是服务器所在域名,或者服务器所在域名的父域,则合法
  • 否则不合法

通过如上设置,服务器IP地址为192.168.3.9,对应域名为www.qfc.com。则服务器响应头中Set-Cookie的domain属性合法值为www.qfc.com或者qfc.com,其他都为非法值。

以上domain为非法值,浏览器端会解析失败

以上domain为合法值,浏览器端成功解析后保存

浏览器发送http请求是否携带cookie需要根据cookie的domain属性:

  • 如果请求的服务器所在IP地址就是domain指定的IP地址,则浏览器允许携带cookie
  • 如果请求的服务器所在域名就是domain指定的域名,或者domain指定域名的子域,则浏览器允许携带cookie
  • 否则不允许携带cookie

以上验证的是虽然cookie的domain为qfc.com,但是当浏览器请求www.qfc.com时,依旧会携带属于qfc.com下的cookie 

domain默认值

如果服务器HTTP响应头Set-Cookie中不包含domain,则domain默认取值服务器所在域名或IP地址

domain为域名时取值要求

服务器端HTTP响应头的Set-Cookie的domain值不能是顶级域名 . 或一级域名如com、cn等,也不能是公开域名如github.io

path

cookie的path值为一个资源路径,它需要结合cookie的domain属性使用,这样path就能帮助浏览器识别当前cookie可以用于服务器的哪些资源了。

需要注意的是cookie可以用于path指定路径下的所有资源,path的默认值为根路径“/”,表示浏览器访问服务器所有资源时都需要携带cookie。

cookie的安全限定

Cookie技术诞生的初衷是为了解决HTTP协议无状态性引发的用户登录状态无法保存的问题。

而用户登录状态属于用户敏感的隐私数据,一旦被泄漏或者盗取或者伪造,则会造成用户信息财产的损失。所以cookie的安全一定要得到保障。

而cookie的安全性问题主要来自于浏览器端。

document.cookie引发的cookie安全问题及解决方案httponly

Cookie就是浏览器端创造的技术,浏览器端也提供了一个操作cookie的方式 document.cookie,通过document.cookie就可以在浏览器端通过javascript进行cookie的增删改查

首先,document.cookie操作的是当前网页所在域名(及其父域)和路径的cookie

查询

新增

新增操作即直接给document.cookie赋值

修改

 修改操作也是给document.cookie赋值,但是赋值时需要保证cookieName,expires/max-age,domain,path必须和要修改的cookie相同,cookieValue和要修改的cookie不同。

删除

删除操作本质也是修改操作,主要是改变对应cookie的max-age属性为0,或者expires属性值为一个过期时间,这样浏览器就可以自动清除失效的cookie了。

以上是document.cookie对于cookie的基本操作。

下面我们通过DOM型XSS注入脚本来盗取他人cookie,比如常见的网站评论中,经常有人在评论区发一些奇怪的链接诱惑其他人去点击 比如上面例子,该评论区没有做用户输入过滤,导致网页被注入了一个a标签,a标签被click就会发送一个fetch请求,该fetch请求会将当前网页的document.cookie发送到http://127.0.0.1/heike接口。

当该包含恶意代码的评论被别人点击后,就会把自己的cookie信息发送给黑客服务器

为了避免这种情况,cookie有一个属性httponly可以禁止javascript操作cookie,比如使用document.cookie操作cookie。

当cookie设置了httponly后,则无法通过document.cookie操作httponly限定的cookie了

http协议明文传输引发的cookie安全问题及解决方案secure

目前http协议已经不是一种推荐的安全协议,因为http协议在传输层没有对报文进行加密,这样就有可能造成网络传输中http请求被拦截,然后直接解析出明文的cookie。

而https在传输层对报文使用了证书公钥进行加密,解密只能依赖服务器独有的证书私钥才可以,所以https协议不担心网络传输过程中报文被破解的问题,除非证书私钥泄漏。

为了保护cookie不被以明文的方式在网络中传输,我们可以设置cookie的secure属性,表示cookie只有在https协议下才允许被使用,在http协议下禁止使用。

但是目前goole内核浏览器,已经禁止了https网页发送http请求

从上面结果可以看出https://www.qfc.com/other.html被禁止请求http://test.qfc.com/heike,原因是https网页必须请求https服务器接口

所以其实cookie的secure属性功能已经被浏览器涵盖了。

我们还需要注意cookie的secure属性的一个情况,那就是如果服务器cookie设置了secure,但是以http协议传输给浏览器,则浏览器会拒绝存储此cookie

跨站请求cookie及解决方案samesite

跨站请求和跨域请求

当浏览器网页 与 服务器接口 的 协议,主机,端口任一个不同时,浏览器网页发送给服务器接口请求就是跨域请求。

当浏览器网页 与 服务器接口 的 主机 不同,浏览器网页发送给服务器接口的请求就是跨站请求。

分析可得:

跨站请求就是跨域请求,而跨域请求不一定是跨站请求。

跨站请求与cookie

情况浏览器网页所在域服务器接口所在域cookie携带情况
同源请求

http://www.qfc.com/

http://www.qfc.com/携带domain为www.qfc.com和qfc.com的cookie
同站请求http://www.qfc.com/http://www.qfc.com:8080/携带domain为www.qfc.com和qfc.com的cookie
同父域站点请求http://www.qfc.com/http://test.qfc.com/携带domain为qfc.com的cookie
跨站请求http://www.qfc.com/http://127.0.0.1/

跨站请求是否携带cookie受到cookie的samesite属性影响,cookie的samesite属性有如下取值:

  • strict
  • lax     (默认值)
  • none

当cookie的samesite=strict时,表示浏览器禁止跨站请求携带cookie

当cookie的samesite=none时,表示浏览器允许跨站请求携带cookie,但设置samesite=none的前提是,cookie的secure属性打开,即samesite=none的cookie只能用于https请求

当cookie的samesite=lax时,表示浏览器禁止了绝大部分跨站请求携带cookie,只允许少量导航到目标网址的 GET 请求可以携带cookie,具体如下:

  • 超链接
  • 预加载
  • GET表单

服务器代码如上,若要/pay,则必须要有登录cookie

 如上,登录cookie已存在

如上,GET表单 跨站请求 可以携带跨站cookie

 如上,超链接 跨站请求 可以携带跨站cookie

需要注意的是以上跨站cookie包含了父域cookie

上面的基于超链接和GET表单本质都是通过网页刷新发起的GET请求,所以只要是它们都算是导航到目标网址的GET请求。

另外对于打开了secure和samesite=none的cookie来说,理论上支持所有跨站请求携带

 但是实际上似乎已经不行了

所以目前,整体而言,浏览器已经不支持非导航性质的跨站请求携带cookie了。

xhr,fetch,axios默认同站请求时携带cookie,跨站请求时不携带cookie,如果想要跨站请求携带cookie,则需要:

  • xhr,axios设置请求头withCredentials为true,表示跨站时也携带cookie;fetch设置请求头credentials为include,表示跨站时也携带cookie
  • 服务器需要设置响应头Access-Control-Allow-Credentials为true

但是由于浏览器已经不支持非导航性质的跨站请求携带cookie了,所以上述操作无效。

Cookie实现身份认证小案例

需求描述

共三个页面:首页index.html,登录页login.html,注册页register.html

首页需求:对用户登录cookie进行认证,

认证成功则显示“欢迎xxx”,并附带退出登录按钮;

 认证失败则显示“欢迎游客”,并附带登录页超链接“请登录”

登录页需求:包含三个用户输入,分别是用户名,密码,七天免登录,通过提交按钮可以提交三个用户输入信息,另外附带了注册页超链接

注册页需求:包含两个用户输入,分别是用户名,密码,通过提交按钮可以提交,另外附带登录页超链接

需求实现

cookie身份认证小案例-Node.js文档类资源-CSDN文库

0积分下载,大家安心使用

数据库使用的mongoose,基于如下代码进行数据库,集合的创建。首先使用mongo命令进入mongo shell,然后执行如下代码

use usermgr

db.user.insertOne({name:'test', pass:'test'})

即可完成数据库,集合创建

代码中,主要需要修改的地方是db/index.js中数据库ip,以及web/*.html中fetch请求的URL地址,将他们改为自己本地的即可。

express官推中间件cookie-parser模拟实现

module.exports = function(options) {
  return function(req, res, next) {
    req.cookies = {}
    if(req.headers && req.headers.cookie) {
      let cookieStr = req.headers.cookie
      let cookieItems = cookieStr.split(';')
      cookieItems.forEach(item => {
        let [key, value] = item.trim().split('=')
        req.cookies[key] = value
      })
    }
    next()
  }
}
  • 6
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伏城之外

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值