完整阅读本文大约须要二十分钟时间,可根据文章结构图直接阅读本身须要的部分。javascript
全部新技术的出现都是为了解决某一痛点。 ——《前端三昧》html
咱们都知道,HTTP 协议
是无状态的,服务器没法知道两个请求是否来自同一个浏览器,也不知道用户上一次作了什么,每次请求都是彻底相互独立,这严重阻碍了交互式 Web
应用程序的实现。例子:前端
HTTP
的无状态性,不经过额外的手段,服务器并不知道用户到底买了什么。Cookie
记住了登陆凭据,想要实现该功能将会很复杂。正是为了解决这些交互方面存在的痛点,Cookie
应运而生。java
Cookie
( 也叫Web Cookie
或浏览器 Cookie
)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。git
存储 Cookie
是浏览器提供的功能。Cookie
实际上是存储在浏览器中的纯文本,浏览器的安装目录下会专门有一个 Cookie
文件夹来存放各个域下设置的 Cookie
(非内存 Cookie
)。数据库
一般,它用于告知服务端两个请求是否来自同一浏览器,或者用来保存一些状态信息,Cookie
使基于无状态的 HTTP
协议记录稳定的状态信息成为了可能。经常使用的有如下方面:跨域
session
)管理:保存登陆、购物车等须要记录的信息。
Cookie
主要是用来存储状态的。浏览器
Cookie
曾一度用于客户端数据的存储,因当时并无其它合适的存储办法而做为惟一的存储手段。如今来讲,这样作虽然可行,可是并不推荐,由于 Cookie
的设计目标并非这个,它:缓存
客户端储存应该更多的考虑使用 localStorage
、sesseionStorage
和 IndexedDB
。安全
查看浏览器上存储的 Cookie
的方法以下图:
固然,浏览器能够设置不接受 Cookie
,也能够设置不向服务器发送 Cookie
。window.navigator.cookieEnabled
属性返回一个布尔值,表示浏览器是否打开 Cookie
功能。
// 浏览器是否打开 Cookie 功能
window.navigator.cookieEnabled // true
复制代码
本文全部的讨论都是在浏览器的
window.navigator.cookieEnabled
为 true 的前提下进行的。
Cookie
的工做流程Cookie
只能存储纯文本格式,由于:
Cookie
的大小有限制Cookie
中存储的是不可执行语句因为 Cookie
是保存在客户端上的,因此浏览器加入了一些限制确保 Cookie
不会被恶意使用,同时不会占据太多磁盘空间,因此 Cookie
的数量和大小是有限的。
不一样浏览器对 Cookie
数量和大小的限制,是不同的。通常来讲,单个域设置的 Cookie
不该超过 50个,每一个 Cookie 的大小不能超过 4KB 。超过限制之后,Cookie
将被忽略,不会被设置。
其限制的缘由,主要在于阻止
Cookie
的滥用,并且Cookie
会被发送到服务器端,若是数量太大的话,会严重影响请求的性能。以上这两个限制条件,就是Cookie
为何会被浏览器自动删除的缘由了。
不可跨域读取,Cookie
是被哪一个域写入的,就只能被这个域及其子域读取。好比:
由 test.com
写入的 Cookie
能够被 test.com
和 test.com/child
读取,而不能被 example.com
读取。
存储 Cookie
时会指定路径,该路径的子级能够读取该 Cookie
,可是它的父级却读取不到——子能够读取父,但父不能拿到子,例如:
由 test.com/parent/child
存储下的 Cookie
,能够被 test.com/parent/child/child
读取,但不能被 test.com/parent
读取。
通常会将
Cookie
存在根路径下,能够避免这种状况的发生。
每一个 Cookie
都有时效性,默认的有效期是会话级别( Seesion Cookie
):就是当浏览器关闭,那么 Cookie
当即销毁,可是咱们也能够在存储的时候手动设置 Cookie
的过时时间,具体设置方法会在下文讲到。
设置 Cookie
的名称及相对应的值,对于认证 Cookie
,Value
值包括 Web
服务器所提供的访问令牌 。
指定了能够访问该 Cookie
的 Web 站点或域。
Cookie
机制并未遵循严格的同源策略,容许一个子域能够设置或获取其父域的 Cookie
。
当须要实现单点登陆方案时,Cookie
的上述特性很是有用,然而也增长了 Cookie
受攻击的危险,好比攻击者能够借此发动会话定置攻击。于是,浏览器禁止在 Domain
属性中设置 .org、.com 等通用顶级域名、以及在国家及地区顶级域下注册的二级域名,以减少攻击发生的范围。
Path
标识指定了主机下的哪些路径能够接受 Cookie
(该 URL 路径必须存在于请求 URL 中)。以字符 %x2F ("/")
做为路径分隔符,子路径也会被匹配。
设置 Cookie
的生存期。有两种存储类型的 Cookie
:会话性与持久性。
Expires
属性指定一个具体的到期时间,到了这个指定的时间以后,浏览器就再也不保留这个 Cookie
,它的值是 UTC 格式,可使用 Date.prototype.toUTCString()
格式进行转换。
Max-Age
属性制定了从如今开始 Cookie
存在的秒数,好比 60 * 60 * 24 * 365(即一年)。过了这个时间之后,浏览器就再也不保留这个 Cookie
若是没有设置这两个选项,则会使用默认值。
domain
的默认值为设置该Cookie
的网页所在的域名,path
默认值为设置该Cookie
的网页所在的目录。
Expires
属性缺省时,为会话性 Cookie(Session Cookie)
,仅保存在客户端内存中,并在用户关闭浏览器时失效。Cookie
会保存在用户的硬盘中,直至生存期到或用户直接在网页中单击“注销”等按钮结束会话时才会失效。当
Cookie
的过时时间被设定时,设定的日期和时间只与客户端相关,而不是服务端。
这个选项用来设置 Cookie
是否能经过 JavaScript
去访问。默认状况下, Cookie
不会带 HTTPOnly
选项(即为空),因此默认状况下,客户端是能够经过 JavaScript
代码去访问(包括读取、修改、删除等)这个 Cookie
的。当 Cookie
带 HTTPOnly
选项时,客户端则没法经过js代码去访问(包括读取、修改、删除等)这个 Cookie
。
用于防止客户端脚本经过 document.cookie
属性访问 Cookie
,有助于保护 Cookie
不被跨站脚本攻击窃取或篡改。可是,HTTPOnly
的应用仍存在局限性,一些浏览器能够阻止客户端脚本对 Cookie
的读操做,但容许写操做;此外大多数浏览器仍容许经过 XMLHTTP
对象读取 HTTP
响应中的 Set-Cookie
头 。
在客户端是不能经过
JAvaScript
代码去设置一个httpOnly
类型的Cookie
的,这种类型的Cookie
只能经过服务端来设置。
指定是否使用 HTTPS
安全协议发送 Cookie
。
使用 HTTPS
安全协议,能够保护 Cookie
在浏览器和 Web
服务器间的传输过程当中不被窃取和篡改。该方法也可用于 Web
站点的身份鉴别,即在 HTTPS
的链接创建阶段,浏览器会检查 Web
网站的 SSL
证书的有效性。
可是基于兼容性的缘由(好比有些网站使用自签署的证书)在检测到 SSL
证书无效时,浏览器并不会当即终止用户的链接请求,而是显示安全风险信息,用户仍能够选择继续访问该站点。因为许多用户缺少安全意识,于是仍可能链接到 Pharming
攻击所伪造的网站 。
若是当前协议是 HTTP,浏览器会自动忽略服务器发来的 Secure。
Cookie
容许服务器要求某个 Cookie
在跨站请求时不会被发送,(其中 Site
由可注册域定义),从而能够阻止跨站请求伪造攻击(CSRF
)。
SameSite cookies
是相对较新的一个字段,全部主流浏览器都已经获得支持。下面是例子:
Set-Cookie: key=value; SameSite=Strict
复制代码
SameSite
能够有下面三种值:
None
浏览器会在同站请求、跨站请求下继续发送 Cookies
,不区分大小写。Strict
浏览器将只在访问相同站点时发送 Cookie
。(在原有 Cookies
的限制条件上的增强)。Lax
与 Strict
相似,但用户从外部站点导航至URL时(例如经过连接)除外。 在新版本浏览器中,为默认选项,Same-site cookies
将会为一些跨站子请求保留,如图片加载或者 frames
的调用,但只有当用户从外部站点导航到 URL
时才会发送。如 link 连接。之前,若是
SameSite
属性没有设置,或者没有获得运行浏览器的支持,那么它的行为等同于None
,Cookies
会被包含在任何请求中——包括跨站请求。大多数主流浏览器正在将
SameSite
的默认值迁移至Lax
。若是想要指定Cookies
在同站、跨站请求都被发送,如今须要明确指定SameSite
为None
。
Cookie
机制的使得服务器没法确认 Cookie
是在安全来源上设置的,甚至没法肯定 Cookie
最初是在哪里设置的。
子域上的易受攻击的应用程序可使用 Domain
属性设置 Cookie
,从而能够访问全部其余子域上的该 Cookie
。会话定置攻击中可能会滥用此机制。
可是,做为 深度防护措施
,可使用 Cookie
前缀来断言有关 Cookie
的特定事实。有两个前缀可用:
__Host-
若是 Cookie
名称具备此前缀,则仅当它也用 Secure
属性标记,是从安全来源发送的,不包括 Domain
属性,并将 Path
属性设置为 /
时,它才在 Set-Cookie
标头中接受。这样,这些 Cookie
能够被视为 "domain-locked
”。
__Secure-
若是 Cookie
名称具备此前缀,则仅当它也用 Secure
属性标记,是从安全来源发送的,它才在 Set-Cookie
标头中接受。该前缀限制要弱于 __Host-
前缀。
带有这些前缀点 Cookie
, 若是不符合其限制的会被浏览器拒绝。请注意,这确保了若是子域要建立带有前缀的 Cookie
,那么它将要么局限于该子域,要么被彻底忽略。因为应用服务器仅在肯定用户是否已经过身份验证或 CSRF 令牌正确时才检查特定的 Cookie
名称,所以,这有效地充当了针对会话劫持的防护措施。
Cookie
各个属性的兼容性以下图所示:
服务器若是但愿在浏览器保存 Cookie
,就要在 HTTP
回应的头信息里面,放置一个Set-Cookie
字段。
浏览器收到响应后一般会保存下 Cookie
,以后对该服务器每一次请求中都经过 Cookie
请求头部将 Cookie
信息发送给服务器。另外,Cookie
的过时时间、域、路径、有效期、适用站点均可以根据须要来指定。
HTTP
回应能够包含多个 Set-Cookie
字段,即在浏览器生成多个 Cookie
。下面是一个例子。
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
[page content]
复制代码
除了 Cookie
的值,Set-Cookie
字段还能够附加 Cookie
的属性。
Set-Cookie: <cookie-name>=<cookie-value>; Expires=<date>
Set-Cookie: <cookie-name>=<cookie-value>; Max-Age=<non-zero-digit>
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>
Set-Cookie: <cookie-name>=<cookie-value>; Path=<path-value>
Set-Cookie: <cookie-name>=<cookie-value>; Secure
Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly
复制代码
一个 Set-Cookie
字段里面,能够同时包括多个属性,没有次序的要求。
若是服务器想改变一个早先设置的
Cookie
,必须同时知足四个条件:Cookie
的key
、domain
、path
和secure
都匹配。不然,会建立一个新的Cookie
。
浏览器接收了响应头提供的 Cookie
以后,每一次访问该域时,都会携带该 Cookie
值:
Cookie
字段能够包含多个 Cookie,使用分号(;
)分隔。
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
复制代码
经过 document.cookie
属性可建立新的 Cookie
,也可经过该属性访问非 HttpOnly 标记的 Cookie
。
上图从 document.cookie
一次性读出多个 Cookie
,它们之间使用分号分隔。必须手动还原,才能取出每个 Cookie
的值。
写入的时候,Cookie
的值必须写成 key=value
的形式。注意,等号两边不能有空格。另外,写入 Cookie
的时候,必须对分号、逗号和空格进行转义(它们都不容许做为 Cookie
的值),这能够用 encodeURIComponent
方法达到。好比,咱们要存储一个对象到 Cookie
中,能够经过下面代码实现:
设置完成后,在浏览器查看:
那要怎么才能读取到此次设置的 Cookie
呢?方法以下:
读取到的结果以下:
document.cookie
一次只能写入一个Cookie
,并且写入并非覆盖,而是添加。
信息被存在
Cookie
中时,须要明白Cookie
的值时能够被访问,且能够被终端用户所修改的。根据应用程序的不一样,可能须要使用服务器查找的不透明标识符,或者研究诸如JSON Web Tokens
之类的替代身份验证/机密机制。当机器处于不安全环境时,切记不能经过
HTTP Cookie
存储、传输敏感信息。
攻击者能够经过木马等恶意程序,或使用跨站脚本攻击等手段偷窃存放在用户硬盘或内存中的 Cookie
。借助网络攻击手段,包括:
DNSPharming
(域欺骗)攻击,经过 DNS 缓存中毒
、DNS 应答欺骗
、或修改用户端的本地域名解析文件等方法攻击 DNS
系统,致使用户对合法网站的访问请求被重定向到恶意网站等等,一样可能窃取 Cookie
。对于捕获到的认证 Cookie
,攻击者每每会猜想其中的访问令牌,试图获取会话ID、用户名与口令、用户角色、时间戳等敏感信息;或者直接重放该 Cookie
,假冒受害者的身份发动攻击 。
Cookies
是文本文件, 通常状况下认为它不会形成安全威胁。 可是,若是在 Cookies
中经过特殊标记语言,引入可执行代码,就极可能给用户形成严重的安全隐患。HTML
为区别普通文本和标记语言,用符号 “<>”
来指示 HTML
代码。 这些 HTML
代码或者定义 Web
网页格式,或者引入 Web
浏览器可执行代码段。 Web
服务 器可使用 Cookies
信息建立动态网页。假使 Cookies
包含可执行恶意代码段,那么在显示合成有该 Cookies
的网页时,就会自动执行这段恶意代码。固然,恶意代码可否真正形成危害,还取决于 Web
站点的安全配置策略 。
会话定置(Session Fixation
)攻击是指,攻击者向受害者主机注入本身控制的认证 Cookie
等信息,使得受害者以攻击者的身份登陆网站,从而窃取受害者的会话信息。
注入 Cookie
的方法包括:
跨站请求伪造(Cross-Site Request Forgery
,简称CSRF
)是指:
攻击者可能利用网页中的恶意代码强迫受害者浏览器向被攻击的 Web
站点发送伪造的请求,篡夺受害者的认证 Cookie
等身份信息,从而假冒受害者对目标站点执行指定的操做。
Firefox、Opera 等浏览器使用单进程机制,多个窗口或标签使用同一个进程,共享 Cookie
等会话数据。IE 则混合使用单进程与多进程模式,一个窗口中的多个标签,以及使用 “CTRL+N” 或单击网页中的连接打开的新窗口使用同一进程,共享会话数据;只有直接运行IE可执行程序打开窗口时,才会建立新的进程。Chrome 虽然使用多进程机制,然而经测试发现,其不一样的窗口或标签之间仍会共享会话数据,除非使用隐身访问方式。
于是,用户同时打开多个浏览器窗口或标签访问互联网资源时,就为 CSRF
攻击篡夺用户的会话 Cookie
创造了条件。另外,若是一个Web 站点提供持久化 Cookie
,则 CSRF
攻击将更直接、更容易。
缓解 Cookie 攻击的方法以下:
- 对用户输入进行过滤来阻止 XSS;
- 任何敏感操做都须要确认;
- 用于敏感信息的 Cookie 只能拥有较短的生命周期;
有两种方法能够确保 Cookie
被安全发送,而且不会被意外的参与者或脚本访问:Secure
属性和 HttpOnly
属性。
标记为 Secure
的 Cookie
只应经过被 HTTPS
协议加密过的请求发送给服务端,所以能够预防 man-in-the-middle
攻击者的攻击。但即使设置了 Secure
标记,敏感信息也不该该经过 Cookie
传输,由于 Cookie
有其固有的不安全性,Secure
标记也没法提供确实的安全保障, 例如,能够访问客户端硬盘的人能够读取它。
JavaScript Document.cookie API
没法访问带有 HttpOnly
属性的 Cookie
;此类 Cookie
仅做用于服务器。例如,例如,持久化服务器端会话的 Cookie
不须要对 JavaScript
可用,而应具备 HttpOnly
属性。此预防措施有助于缓解跨站点脚本(XSS
)攻击。
因为 Cookie
在使用上存在较多限制,近年来,随着技术的发展成熟,出现了几种可替代 Cookie
的方案,且已被大多数主流浏览器支持。
在浏览器中存储数据的另外一种方法是 Web Storage API。window.sessionStorage
和 window.localStorage
属性与持续时间中的会话和永久 Cookie
相对应,可是存储限制比 Cookie
大,而且永远不会发送到服务器。
可使用 IndexedDB API
或基于它构建的库来存储更多结构化的数据。
Web SQL
是一种利用数据库进行数据存储并利用 SQL 处理检索任务的 API。
欢迎你们来到个人「山头」,我是「前端三昧」的做者 隐逸王 —— 一个想要作山大王的男人!
愿和你一块儿领略前端三昧,发现前端之美!