第一部分 cookie
首先了解一下会话
会话是一种持久的网络协议,用于完成服务器和客户端之间的一些交互行为。会话是一个比连接粒度更大的概念,一次会话可能包含多次连接,每次连接都被认为是会话的一次操作。
Cookie是由网景公司的前雇员Lou Montulli在1993年发明的,在RFC2109(已废弃,被RFC2965取代)里初次被描述的,每个客户端最多保持三百个cookie,每个域名下最多20个Cookie(实际上一般浏览器现在都比这个多,如Firefox是50个),而每个cookie的大小为最多4K,不过不同的浏览器都有各自的实现。对于cookie的使用,最重要的就是要控制cookie的大小,不要放入无用的信息,也不要放入过多信息。
1.1 cookie概述
HTTP 是一个无状态协议,所以客户端每次发出请求时,下一次请求无法得知上一次请求所包含的状态数据;
Cookie是解决HTTP无状态性的有效手段,服务器可以设置或读取Cookie中所包含的信息。
简单的说,cookie是服务器发给用户的一个标识,用来帮助服务器识别用户,从而记录用户状态。
1. 用户第一次访问网站时,服务器向客户端发送 cookie。通常使用 HTTP 协议规定的 set-cookie 头操作。规范规定 cookie 的格式为 name = value 格式,且必须包含这部分。
2. 浏览器将 cookie 保存。
3. 每次请求浏览器都会将 cookie 发向服务器。
cookie 其他参数option:
1. path:表示 cookie 影响到的路径,匹配该路径才发送这个 cookie。
2. expires 和 maxAge:告诉浏览器这个 cookie 什么时候过期,expires 是 UTC 格式时间,maxAge 是 cookie 多久后过期的相对时间。当不设置这两个选项时,会产生 session cookie,session cookie 是 transient 的,当用户关闭浏览器时,就被清除。一般用来保存 session 的 session_id。
3. secure:当 secure 值为 true 时,cookie 在 HTTP 中是无效,在 HTTPS 中才有效。
4. httpOnly:浏览器不允许脚本操作 document.cookie 去更改 cookie。一般情况下都应该设置这个为 true,这样可以避免被 xss 攻击拿到 cookie。
1.2 Cookie分类
1. 内存式Cookie: 存储在内存中,浏览器关闭后就会消失;
2. 硬盘式Cookie: 保存在硬盘中,其不会随浏览器的关闭而消失,除非用户手工清理或到了过期时间。
1.3 Cookie 的安全隐患
Cookie提供了一种手段使得HTTP请求可以附加当前状态, 现今的网站也是靠Cookie来标识用户的登录状态的:
``
- 用户提交用户名和密码的表单,这通常是一个POST HTTP请求。
- 服务器验证用户名与密码,如果合法则返回200(OK)并设置Set-Cookie为authed=true。
- 浏览器存储该Cookie。
- 浏览器发送请求时,设置Cookie字段为authed=true。
- 服务器收到第二次请求,从Cookie字段得知该用户已经登录。 按照已登录用户的权限来处理此次请求。
``
这里面的问题在哪里?
假如我们直接设置Cookie字段为authed=true并发送该HTTP请求, 服务器岂不是被欺骗了?这种攻击非常容易,Cookie是可以被篡改的!
1.4 Cookie 防篡改机制
服务器可以为每个Cookie项生成签名,由于用户篡改Cookie后无法生成对应的签名, 服务器便可以得知用户对Cookie进行了篡改。
1. 在服务器中配置一个不为人知的字符串(我们叫它Secret),比如:x$sfz32。
2. 当服务器需要设置Cookie时(比如authed=false),不仅设置authed的值为false, 在值的后面进一步设置一个签名,最终设置的Cookie是authed=false|6hTiBl7lVpd1P。
3. 签名6hTiBl7lVpd1P是这样生成的:Hash('x$sfz32'+'true')。 要设置的值与Secret相加再取哈希。
4. 用户收到HTTP响应并发现头字段Set-Cookie: authed=false|6hTiBl7lVpd1P。
5. 用户在发送HTTP请求时,篡改了authed值,设置头字段Cookie: authed=true|???。 因为用户不知道Secret,无法生成签名,只能随便填一个。
6. 服务器收到HTTP请求,发现Cookie: authed=true|???。服务器开始进行校验: Hash('true'+'x$sfz32'),便会发现用户提供的签名不正确。
第二部分: cookie-parser
实例代码(不签名形式):
var express = require('express');
// 首先引入 cookie-parser 这个模块
var cookieParser = require('cookie-parser');
var app = express();
app.listen(3000);
//不采用签名形式
app.use(cookieParser());
// 如果请求中的 cookie 存在 bwf, 则输出 cookie
// 否则,设置 cookie 字段 bwf, 并设置过期时间为1分钟
app.get('/', function (req, res) {
//不采用签名形式获取cookie的方法: req.cookies.
if (req.cookies.bwf) {
console.log(req.cookies);
res.send("再次欢迎访问");
} else {
//name value 过期时间
res.cookie('bwf', '1234', {maxAge: 6 * 1000, path: '/'});
res.send("欢迎第一次访问");
}
});
chrome中cookie显示为name=value
2.2 cookie-parser API
cookie-Parser和 express-session 这两个中间件在express 4版本之后解耦了,最开始如果用express-session 的话一定也要用cookieParser,
中间件包含三个模块:cookie
cookie-praser
cookie-signature.
;
其中,cookie cookie-signature这两个模块是private的。所有的public API都在cookie-Parser中。
cookie-Parser 有四个接口;
module.exports = cookieParser;
module.exports.JSONCookie = JSONCookie;
module.exports.JSONCookies = JSONCookies;
module.exports.signedCookie = signedCookie;
module.exports.signedCookies = signedCookies;
req.secret:传入的秘钥用于对cookie进行加密
req.cookies:对req.headers.cookie中的cookie进行解析,返回的一个对象
req.signedCookies:保存的是解析后的cookie的真实值,但是可能还会被JSONCookie进行处理
2.2.1 cookieParser(secret, options) 返回的是一个中间件函数
1. secret(可选): 可以用string 或是 array 来签名cookie;默认无
2. options: 第一部分中cookie的可选对象;
2.2.2 cookieParser.JSONCookie(str)
将cookie作为json格式解析, 如果是json格式的value,就返回, 否则返回过去式;
2.2.3 cookieParser.JSONCookies(cookies)
给定一个对象,会重复调用cookieParser.JSONCookie
2.2.4 cookieParser.signedCookie(str, secret)
解析签名cookie,
2.2.5 cookieParser.signedCookies(cookies, secret)
给定一个对象, 重复调用cookieParser.signedCookie(str, secret)
实例代码(签名):
信息可见不可篡改
var express = require('express');
// 首先引入 cookie-parser 这个模块
var cookieParser = require('cookie-parser');
var app = express();
/*--------------采用签名方式--------------*/
//第一步 设置签名 string
app.use(cookieParser('singedMyCookie'));
app.get('/', function (req, res) {
if (req.signedCookies.bwf) {
//第三步: 使用signedCookies获取cookie (采用签名形式获取cookie的方法: req.signedCookies.
console.log(req.signedCookies);
res.send("再次欢迎访问");
} else {
//第二步: 设置{signed: true}
res.cookie("bwf", "hhw", {signed: true});
res.send("欢迎第一次访问");
}
});
app.listen(3000);
chrome中cookie显示为bwf=s%3......... 签名前是bwf=hhw
参考
Cookie-Parser是如何解析签名后的cookie的
cookie-praser & express-session 的工作原理
Cookie/Session机制详解
阿里巴巴攻城师分享nodeJS精华:cookie 和 session
Cookie/Session的机制与安全
更多精彩内容请关注“IT实战联盟”哦~~~