一、Cookie 是什么?
HTTP Cookie(也叫 Web Cookie 或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。使用场景:
- 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
- 个性化设置(如用户自定义设置、主题等)
- 浏览器行为跟踪(如跟踪分析用户行为等)
二、Cookie 生成过程
1、生成 cookie
服务器生成了 cookie 数据 并设置为 Set-Cookie
属性,包含在 HTTP 协议的 Header 中 ,来告诉浏览器保存这些数据(除非浏览器禁用了 Cookie)。
// 服务端发给浏览器的数据
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
2、存储 cookie 并回传
浏览器会在接下来的请求中,把存储的 cookie 数据,设置为 Cookie
属性,包含 HTTP 协议的 Header 中 ,连同请求一起发送给服务器(除非设置了不发送 cookie)。
// 浏览器发给服务器的数据
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
三、第一方 和 第三方 Cookie
-
Cookie 中的域名 与 当前站点域名相同,称为 第一方cookie( first-party cookie);
-
Cookie 中的域名 与 当前站点域名不同,称为 第三方cookie( third-party cookie);
当前站点会使用一些其他站点资源(譬如图片、广告等),在请求第三方服务器获取这些资源时,也会返回 Set-Cookie
属性,让浏览器保留第三方的 cookie,这些cookie 主要用于用户跟踪,流量分析等。
四、cookie 的重要属性
1、Secure 和 HttpOnly
功能:限制访问 Cookie 的方式。
- Secure :表示 cookie 只能用 https 加密的方式发送给请求站点;
- HttpOnly :JavaScript API 无法访问带有 HttpOnly 属性的cookie(
Document.cookie
无法读取cookie)当 cookie 中的数据,只用于服务器时,可以设置此属性;可防止通过 JavaScript 访问 cookie 值; - 这两个属性可以有效防御 大部分 XSS 攻击。
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
2、Domain 和 Path
功能:允许 Cookie 发送给哪些URL?
- Domain :表示 cookie 可以发送给那个域名包括其子域名。如果不设置Domain,就取值为 origin 但不包含origin 的子域名。如果设置
Domain=mozilla.org
,那么 cookie 也能发送给子域名developer.mozilla.org
。 - Path :表示 cookie 可以发送给那些路径。如果 设置
Path=/docs
,那么 cookie 也能发送给子路径/docs/Web/
。
3、SameSite
功能:可以限制 cookie 的跨域发送,此属性可有效防止大部分 CSRF 攻击,有三个值可以设置:
-
None :同站、跨站请求都发送 cookie,但需要 Secure 属性配合一起使用。
Set-Cookie: flavor=choco; SameSite=None; Secure
-
Strict :当前页面与跳转页面是相同站点时,发送 cookie;
Set-Cookie: key=value; SameSite=Strict
-
Lax :与 Strict 类似,但用户从外部站点导航至URL时(例如通过链接)除外。 在新版本浏览器中,为默认选项,Same-site cookies 将会为一些跨站子请求保留,如图片加载或者 frames 的调用,但只有当用户从外部站点导航到URL时才会发送。如 link 链接
4、__Host-
和 __Secure-
可以创建 cookie 的地方很多,很难判断 cookie 的来源,但是可使用 cookie 前缀来断言 cookie 的来源。
-
__Host-
带有这个前缀的 cookie,必须具备这三个特性:有 Secure 属性、没有 Domain 属性、Path 值为 /,此类 cookie 被称之为
domain-locked
。 -
__Secure-
带有这个前缀的 cookie,表示必须具有 Secure 属性。
由于应用服务器仅在确定用户是否已通过身份验证或 CSRF 令牌正确时才检查特定的 cookie 名称,因此,这有效地充当了针对会话劫持的防御措施。
五、操作 Cookie 的方法
1、JavaScript API
JavaScript 代码中通过 Document.cookie 来创建 Cookie,也能用其访问不带 HttpOnly 标志的 Cookie。
document.cookie = "yummy_cookie=choco";
document.cookie = "tasty_cookie=strawberry";
console.log(document.cookie);
// logs "yummy_cookie=choco; tasty_cookie=strawberry"
2、cookie库
Node.js 项目用 http 创建的 Web服务,可以用 cookie库
操作 cookie,实例如下:
var cookie = require('cookie');
var escapeHtml = require('escape-html');
var http = require('http');
var url = require('url');
function onRequest(req, res) {
// Parse the query string
var query = url.parse(req.url, true, true).query;
if (query && query.name) {
// Set a new cookie with the name
res.setHeader('Set-Cookie', cookie.serialize('name', String(query.name), {
httpOnly: true,
maxAge: 60 * 60 * 24 * 7 // 1 week
}));
// Redirect back after setting cookie
res.statusCode = 302;
res.setHeader('Location', req.headers.referer || '/');
res.end();
return;
}
// Parse the cookies on the request
var cookies = cookie.parse(req.headers.cookie || '');
// Get the visitor name set in the cookie
var name = cookies.name;
res.setHeader('Content-Type', 'text/html; charset=UTF-8');
if (name) {
res.write('<p>Welcome back, <b>' + escapeHtml(name) + '</b>!</p>');
} else {
res.write('<p>Hello, new visitor!</p>');
}
res.write('<form method="GET">');
res.write('<input placeholder="enter your name" name="name"> <input type="submit" value="Set Name">');
res.end('</form>');
}
http.createServer(onRequest).listen(3000);
3、cookie-parser 库
express
项目 中使用 cookie-parser
来操作 cookie,实例如下:
var express = require('express')
var cookieParser = require('cookie-parser')
var app = express()
app.use(cookieParser())
app.get('/', function (req, res) {
// Cookies that have not been signed
console.log('Cookies: ', req.cookies)
// Cookies that have been signed
console.log('Signed Cookies: ', req.signedCookies)
})
app.listen(8080)
// curl command that sends an HTTP request with two cookies
// curl http://127.0.0.1:8080 --cookie "Cho=Kim;Greet=Hello"
六、大量数据存储问题
大量服务端数据存储于 Cookie ,降低了网络访问性能(特别是移动环境下),可通过 Web Storage API
和 IndexedDB
来解决。