type
status
date
slug
summary
tags
category
icon
password
Edited
Sep 15, 2022 01:17 PM
Created
Sep 13, 2022 02:14 PM
Cookie
Cookie 是直接存储在浏览器中的一小串数据。它们是 HTTP 协议的一部分,由 RFC 6265 规范定义。
Cookie 通常是由 Web 服务器使用响应
Set-Cookie
HTTP-header 设置的。然后浏览器使用 Cookie
HTTP-header 将它们自动添加到(几乎)每个对相同域的请求中。使用document.cookie
属性从浏览器访问 cookie
document.cookie
document.cookie
的值由 name=value
对组成,以 ;
分隔。每一个都是独立的 cookie。document.cookie
是一个 访问器(getter/setter)。对其的赋值操作会被特殊处理。对
document.cookie
的写入操作只会更新其中提到的 cookie,而不会涉及其他 cookie。因为
document.cookie=
操作不是重写整所有 cookie。它只设置代码中提到的 cookie user
。从技术上讲,cookie 的名称和值可以是任何字符。为了保持有效的格式,它们应该使用内建的
encodeURIComponent
函数对其进行转义限制
encodeURIComponent
编码后的name=value
对,大小不能超过 4KB。因此,我们不能在一个 cookie 中保存大的东西。
- 每个域的 cookie 总数不得超过 20+ 左右,具体限制取决于浏览器。
参数
path
path=/mypath
url 路径前缀必须是绝对路径。它使得该路径下的页面可以访问该 cookie。默认为当前路径。
如果一个 cookie 带有
path=/admin
设置,那么该 cookie 在 /admin
和 /admin/something
下都是可见的,但是在 /home
或 /adminpage
下不可见。通常,我们应该将
path
设置为根目录:path=/
,以使 cookie 对此网站的所有页面可见。domain
domain=site.com
domain 控制了可访问 cookie 的域。但是在实际中,有一些限制。我们无法设置任何域。
默认情况下,cookie 只有在设置的域下才能被访问到。
请注意,默认情况下,cookie 也不会共享给子域,例如
forum.site.com
。如果我们想允许像
forum.site.com
这样的子域在 site.com
上设置 cookie,也是可以实现的。因此,当在
site.com
设置 cookie 时,我们应该明确地将 domain
选项设置为根域:domain=site.com
。那么,所有子域都可以访问到这样的 cookie。expires & max-age
默认情况下,如果一个 cookie 没有设置这两个参数中的任何一个,那么在关闭浏览器之后,它就会消失。此类 cookie 被称为 "session cookie”。
为了让 cookie 在浏览器关闭后仍然存在,我们可以设置
expires
或 max-age
选项中的一个。expires=Tue, 19 Jan 2038 03:14:07 GMT
日期必须完全采用 GMT 时区的这种格式(可以使用
date.toUTCString
来获取它)示例:一天后过期
max-age=3600
指明了 cookie 的过期时间距离当前时间的秒数(上例就是1h后过期)
secure
secure
Cookie 应只能被通过 HTTPS 传输。
默认情况下,如果我们在
http://site.com
上设置了 cookie,那么该 cookie 也会出现在 https://site.com
上,反之亦然。使用此选项,如果一个 cookie 是通过
https://site.com
设置的,那么它不会在相同域的 HTTP 环境下出现,例如 http://site.com
。如果一个 cookie 包含绝不应该通过未加密的 HTTP 协议发送的敏感内容,那么就应该设置
secure
标识。samesite
这是另外一个关于安全的特性。它旨在防止 XSRF(跨网站请求伪造)攻击。
Cookie 的
samesite
选项提供了另一种防止此类攻击的方式,(理论上)不需要要求 “XSRF 保护 token”。samesite=strict
(和没有值的samesite
一样)
如果用户来自同一网站之外,那么设置了
samesite=strict
的 cookie 永远不会被发送。换句话说,无论用户是通过邮件链接还是从
evil.com
提交表单,或者进行了任何来自其他域下的操作,cookie 都不会被发送。samesite=lax
- HTTP 方法是“安全的”(例如 GET 方法,而不是 POST)。
- 该操作执行顶级导航(更改浏览器地址栏中的 URL)。
一种更轻松的方法,该方法还可以防止 XSRF 攻击,并且不会破坏用户体验。
宽松(lax)模式,和
strict
模式类似,当从外部来到网站,则禁止浏览器发送 cookie,但是增加了一个例外。如果以下两个条件均成立,则会发送含
samesite=lax
的 cookie:所有安全的 HTTP 方法详见 RFC7231 规范。基本上,这些都是用于读取而不是写入数据的方法。它们不得执行任何更改数据的操作。跟随链接始终是 GET,是安全的方法。
这通常是成立的,但是如果导航是在一个
<iframe>
中执行的,那么它就不是顶级的。此外,用于网络请求的 JavaScript 方法不会执行任何导航,因此它们不适合。httpOnly
这个选项禁止任何 JavaScript 访问 cookie。我们使用
document.cookie
看不到此类 cookie,也无法对此类 cookie 进行操作。这是一种预防措施,当黑客将自己的 JavaScript 代码注入网页,并等待用户访问该页面时发起攻击,而这个选项可以防止此时的这种攻击。这应该是不可能发生的,黑客应该无法将他们的代码注入我们的网站,但是网站有可能存在 bug,使得黑客能够实现这样的操作。
XSRF攻击
想象一下,你登录了
bank.com
网站。此时:你有了来自该网站的身份验证 cookie。你的浏览器会在每次请求时将其发送到 bank.com
,以便识别你,并执行所有敏感的财务上的操作。现在,在另外一个窗口中浏览网页时,你不小心访问了另一个网站
evil.com
。该网站具有向 bank.com
网站提交一个具有启动与黑客账户交易的字段的表单 <form action="https://bank.com/pay">
的 JavaScript 代码。你每次访问
bank.com
时,浏览器都会发送 cookie,即使该表单是从 evil.com
提交过来的。因此,银行会识别你的身份,并执行真实的付款。这就是所谓的“跨网站请求伪造(Cross-Site Request Forgery,简称 XSRF)”攻击。
Cookie 函数
getCookie(name)
setCookie(name, value, options)
deleteCookie(name)
GDPR
GDPR
欧洲有一项名为 GDPR 的立法,该法规针对网站尊重用户实施了一系列规则。其中之一就是需要明确的许可才可以跟踪用户的 cookie。
这仅与跟踪/识别/授权 cookie 有关。
所以,如果我们设置一个只保存了一些信息的 cookie,但是既不跟踪也不识别用户,那么我们可以自由地设置它。
但是,如果我们要设置带有身份验证会话(session)或跟踪 id 的 cookie,那么必须得到用户的允许。
LocalStorage & sessionStorage
Web 存储对象
localStorage
和 sessionStorage
允许我们在浏览器上保存键/值对。它们有趣的是,在页面刷新后(对于
sessionStorage
)甚至浏览器完全重启(对于 localStorage
)后,数据仍然保留在浏览器中。与 cookie 不同,Web 存储对象不会随每个请求被发送到服务器。因此,我们可以保存更多数据。大多数现代浏览器都允许保存至少 5MB 的数据(或更多),并且具有用于配置数据的设置。
方法和属性
两个存储对象都提供相同的方法和属性:
setItem(key, value)
—— 存储键/值对。
getItem(key)
—— 按照键获取值。
removeItem(key)
—— 删除键及其对应的值。
clear()
—— 删除所有数据。
key(index)
—— 获取该索引下的键名。
length
—— 存储的内容的长度。
localStorage
localStorage
最主要的特点是:- 在同源的所有标签页和窗口之间共享数据。
- 数据不会过期。它在浏览器重启甚至系统重启后仍然存在。
只需要在同一个源(域/端口/协议),URL 路径可以不同。在所有同源的窗口之间
localStorage
数据可以共享。因此,如果我们在一个窗口中设置了数据,则在另一个窗口中也可以看到数据变化。请注意,键和值都必须是字符串。
遍历
for in
它会遍历所有的键,但也会输出一些我们不需要的内建字段。使用
Object.keys
获取只属于“自己”的键sessionStorage
sessionStorage
对象的使用频率比 localStorage
对象低得多。属性和方法是相同的,但是它有更多的限制:
sessionStorage
的数据只存在于当前浏览器标签页。- 具有相同页面的另一个标签页中将会有不同的存储。
- 但是,它在同一标签页下的 iframe 之间是共享的(假如它们来自相同的源)。
- 数据在页面刷新后仍然保留,但在关闭/重新打开浏览器标签页后不会被保留。
sessionStorage
不仅绑定到源,还绑定在同一浏览器标签页。Storage 事件
当
localStorage
或 sessionStorage
中的数据更新后,storage 事件就会触发,它具有以下属性:key
—— 发生更改的数据的key
(如果调用的是.clear()
方法,则为null
)。
oldValue
—— 旧值(如果是新增数据,则为null
)。
newValue
—— 新值(如果是删除数据,则为null
)。
url
—— 发生数据更新的文档的 url。
storageArea
—— 发生数据更新的localStorage
或sessionStorage
对象。
重要的是:该事件会在所有可访问到存储对象的
window
对象上触发,导致当前数据改变的 window
对象除外。如果两个窗口都在监听
window.onstorage
事件,那么每个窗口都会对另一个窗口中发生的更新作出反应。并且,
event.storageArea
包含存储对象 —— sessionStorage
和 localStorage
具有相同的事件,所以 event.storageArea
引用了被修改的对象。我们可能会想设置一些东西,以“响应”更改。这允许同源的不同窗口交换消息。
IndexedDB
IndexedDB 是一个浏览器内建的数据库,它比
localStorage
强大得多。- 通过支持多种类型的键,来存储几乎可以是任何类型的值。
- 支撑事务的可靠性。
- 支持键值范围查询、索引。
- 和
localStorage
相比,它可以存储更大的数据量。
IndexedDB 适用于离线应用,可与 ServiceWorkers 和其他技术相结合使用。
基本用法可以用几个短语来描述:
- 获取一个 promise 包装器,比如 idb。
- 打开一个数据库:
idb.openDb(name, version, onupgradeneeded)
- 在
onupgradeneeded
处理程序中创建对象存储和索引,或者根据需要执行版本更新。
- 对于请求:
- 创建事务
db.transaction('books')
(如果需要的话,设置 readwrite)。 - 获取对象存储
transaction.objectStore('books')
。
- 按键搜索,可以直接调用对象库上的方法。
- 要按对象字段搜索,需要创建索引。
- 如果内存中容纳不下数据,请使用光标。
参考链接:
- 作者:JinSo
- 链接:https://jinso365.top/article/modern-javascript-others-data-storage
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。