个人总结:这篇文章讲解了express框架种cookie的使用,需要引用cookie-parser这个包。读完这篇文章需要10分钟。

摘选自网络

文章导读

cookie-parser是Express的中间件,用来实现cookie的解析,是官方脚手架内置的中间件之一。

它的使用非常简单,但在使用过程中偶尔也会遇到问题。一般都是因为对Express + cookie-parser的签名、验证机制不了解导致的。

本文深入讲解Express + cookie-parser的签名和验证的实现机制,以及cookie签名是如何增强网站的安全性的。

入门例子:cookie设置与解析

先从最简单的例子来看下cookie-parser的使用,下面采用默认配置。

  • cookie设置:使用Express的内置方法res.cookie
  • cookie解析:使用cookie-parser中间件。
var express = require('express');
var cookieParser = require('cookie-parser');
var app = express(); app.use(cookieParser()); app.use(function (req, res, next) {
console.log(req.cookies.nick); // 第二次访问,输出chyingp
next();
}); app.use(function (req, res, next) {
res.cookie('nick', 'chyingp');
res.end('ok');
}); app.listen(3000);

在当前场景下,cookie-parser中间件大致实现如下:

app.use(function (req, res, next) {
req.cookies = cookie.parse(req.headers.cookie);
next();
});

进阶例子:cookie签名与解析

出于安全的考虑,我们通常需要对cookie进行签名。

例子改写如下,有两个注意点:

  1. cookieParser初始化时,传入secret作为签名的秘钥。
  2. 设置cookie时,将signed设置为true,表示对cookie进行签名。
  3. 获取cookie时,可以同时通过req.cookies,也可以通过req.signedCookies获取。
var express = require('express');
var cookieParser = require('cookie-parser');
var app = express(); // 初始化中间件,传入的第一个参数为singed secret
app.use(cookieParser('secret')); app.use(function (req, res, next) {
console.log(req.cookies.nick); // chyingp
console.log(req.signedCookies.nick); // chyingp
next();
}); app.use(function (req, res, next) {
// 传入第三个参数 {signed: true},表示要对cookie进行摘要计算
res.cookie('nick', 'chyingp', {signed: true});
res.end('ok');
}); app.listen(3000);

签名前的cookie值为chyingp,签名后的cookie值为s%3Achyingp.uVofnk6k%2B9mHQpdPlQeOfjM8B5oa6mppny9d%2BmG9rD0

下面就来分析下,cookie的签名、解析是如何实现的。

cookie签名、解析实现剖析

Express完成cookie值的签名,cookie-parser实现签名cookie的解析。两者公用同一个秘钥。

cookie签名

Express对cookie的设置(包括签名),都是通过res.cookie这个方法实现的。

精简后的代码如下:

res.cookie = function (name, value, options) {
var secret = this.req.secret;
var signed = opts.signed; // 如果 options.signed 为true,则对cookie进行签名
if (signed) {
val = 's:' + sign(val, secret);
} this.append('Set-Cookie', cookie.serialize(name, String(val), opts)); return this;
};

sign为签名函数。伪代码如下,其实就是把cookie的原始值,跟hmac后的值拼接起来。

敲黑板划重点:签名后的cookie值,包含了原始值。

function sign (val, secret) {
return val + '.' + hmac(val, secret);
}

这里的secret哪来的呢?是cookie-parser初始化的时候传入的。如下伪代码所示:

var cookieParser = function (secret) {
return function (req, res, next) {
req.secret = secret;
// ...
next();
};
}; app.use(cookieParser('secret'));

签名cookie解析

知道了cookie签名的机制后,如何"解析"签名cookie就很清楚了。这个阶段,中间件主要做了两件事:

  1. 将签名cookie对应的原始值提取出来
  2. 验证签名cookie是否合法

实现代码如下:

// str:签名后的cookie,比如 "s:chyingp.uVofnk6k+9mHQpdPlQeOfjM8B5oa6mppny9d+mG9rD0"
// secret:秘钥,比如 "secret"
function signedCookie(str, secret) { // 检查是否 s: 开头,确保只对签过名的cookie进行解析
if (str.substr(0, 2) !== 's:') {
return str;
} // 校验签名的值是否合法,如合法,返回true,否则,返回false
var val = unsign(str.slice(2), secret); if (val !== false) {
return val;
} return false;
}

判断、提取cookie原始值比较简单。只是是unsign方法名比较有迷惑性。

一般只会对签名进行合法校验,并没有所谓的反签名。

unsign方法的代码如下。首先,从传入的cookie值中,分别提取出原始值A1、签名值B1。用同样的秘钥对A1进行签名,得到A2。根据A2、B1是否相等,判断签名是否合法。

exports.unsign = function(val, secret){
var str = val.slice(0, val.lastIndexOf('.'))
, mac = exports.sign(str, secret); return sha1(mac) == sha1(val) ? str : false;
};

cookie签名的作用

主要是出于安全考虑,防止cookie被篡改,增强安全性。

举个小例子来看下cookie签名是如何实现防篡改的。

基于前面的例子展开。假设网站通过nick这个cookie来区分当前登录的用户是谁。在前面例子中,登录用户的cookie中,nick对应的值如下:(decode后的)

s:chyingp.uVofnk6k+9mHQpdPlQeOfjM8B5oa6mppny9d+mG9rD0

此时,有人试图修改这个cookie值,来达到伪造身份的目的。比如修改成xiaoming

s:xiaoming.uVofnk6k+9mHQpdPlQeOfjM8B5oa6mppny9d+mG9rD0

当网站收到请求,对签名cookie进行解析,发现签名验证不通过。由此可判断,cookie是伪造的。

hmac("xiaoming", "secret") !== "uVofnk6k+9mHQpdPlQeOfjM8B5oa6mppny9d+mG9rD0"

签名就能够确保安全吗

当然不是。

上个小节的例子,仅通过nick这个cookie的值来判断登录的是哪个用户,这是一个非常糟糕的设计。虽然在秘钥未知的情况下,很难伪造签名cookie的,但原始值相同的情况下,签名也是相同的。这种情况下,其实是很容易伪造的。

另外,开源组件的算法是公开的,因此秘钥的安全性就成了关键,要确保秘钥不泄露。

还有很多,这里不展开。

小结

本文主要对Express + cookie-parser的签名和解析机制进行相对深入的介绍。不少类似的总结文章中,把cookie的签名说成了加密,这是一个常见的错误,读者朋友可以注意一下。

签名部分的介绍,稍微涉及一些简单的安全知识,对这块不熟悉的同学可以留言交流。为讲解方便,部分段落、用词可能不够严谨。如有错漏,敬请指出。

相关链接

https://github.com/expressjs/cookie-parser

NodeJS学习笔记 进阶 (9)express+cookie-parser:签名机制深入剖析(ok)的更多相关文章

  1. NodeJS学习笔记 进阶 (7)express+session实现简易身份认证(ok)

    个人总结: 这篇文章讲解了express框架中如何使用session,主要用了express-session这个包.更多可以参考npm.js来看,读完这篇文章需要10分钟. 摘选自网络: 文档概览 本 ...

  2. NodeJS学习笔记 进阶 (8)express+morgan实现日志记录(ok)

    个人总结:这篇文章讲解了Express框架中日志记录插件morgan的示例.读完这篇文章需要10分钟 摘选自网络 章节概览 morgan是express默认的日志中间件,也可以脱离express,作为 ...

  3. NodeJS学习笔记 进阶 (13)Nodejs进阶:5分钟入门非对称加密用法

    个人总结:读完这篇文章需要5分钟,这篇文章讲解了Node.js非对称加密算法的实现. 摘录自网络 地址: https://github.com/chyingp/nodejs-learning-guid ...

  4. NodeJS学习笔记 进阶 (12)Nodejs进阶:crypto模块之理论篇

    个人总结:读完这篇文章需要30分钟,这篇文章讲解了使用Node处理加密算法的基础. 摘选自网络 Nodejs进阶:crypto模块之理论篇 一. 文章概述 互联网时代,网络上的数据量每天都在以惊人的速 ...

  5. NodeJS学习笔记 进阶 (1)Nodejs进阶:服务端字符编解码&乱码处理(ok)

    个人总结:这篇文章主要讲解了Nodejs处理服务器乱码及编码的知识,读完这篇文章需要10分钟. 摘选自网络 写在前面 在web服务端开发中,字符的编解码几乎每天都要打交道.编解码一旦处理不当,就会出现 ...

  6. NodeJS学习笔记 进阶 (4)基于express+muter的文件上传(ok)

    个人总结:这篇文章主要讲了multer插件的使用,类似于formidable,可以用来处理post表单中的文件上传,读完这篇文章需要10分钟. 摘选自网络 概览 图片上传是web开发中经常用到的功能, ...

  7. NodeJS学习笔记 进阶 (3)Nodejs 进阶:Express 常用中间件 body-parser 实现解析(ok)

    个人总结:Node.js处理post表单需要body-parser,这篇文章进行了详细的讲解. 摘选自网络 写在前面 body-parser是非常常用的一个express中间件,作用是对http请求体 ...

  8. NodeJS学习笔记 进阶 (11)Nodejs 进阶:调试日志打印:debug模块

    个人总结:读完这篇文章需要5分钟,讲解了debug模块的使用 摘选自网络 前言 在node程序开发中时,经常需要打印调试日志.用的比较多的是debug模块,比如express框架中就用到了.下文简单举 ...

  9. NodeJS学习笔记 进阶 (5)将图片转成datauri嵌入到html(ok)

    个人总结:这篇文章讲解了使用Node处理转换base64编码图片,读完这篇文章需要5分钟. 摘选自网络 问题:将图片转成datauri 今天,在QQ群有个群友问了个问题:“nodejs读取图片,转成b ...

随机推荐

  1. 使用ffmpeg批量合并flv文件

    title: 使用ffmpeg批量合并flv文件 toc: false date: 2018-10-14 16:08:19 categories: methods tags: ffmpeg flv 使 ...

  2. 如何做到Ubuntu14.04下的mongdb远程访问?(图文详解)

    不多说,直接上干货! 本教程详细指导大家如何开启并设置用户权限.MongoDB默认是没有开启用户权限的,如果直接在公网服务器上如此搭建MongoDB,那么所有人都可以直接访问并修改数据库数据了. 其实 ...

  3. springMVC下ajax获取后台参数直接走错误或者报406错误问题解决

    直接走错误: 返回的结果是否json化,直接返回非字符串的结果会走ajax的error函数. 报406错误: 我在学习springmvc过程中(我的项目是配置的后缀是.html),从controlle ...

  4. vs2015汉化

    VS2015汉化 VS2015安装打开后默认是英文的,将它改成中文的VS 1.安装下载好的语言包进行安装 2.正在安装 3.安装完成后关闭 4.打开VS2015默认不是中文的,点击Tools--> ...

  5. mysql索引的注意事项

    索引的优点 大大加快数据的查询速度 使用分组和排序进行数据查询时,可以显著减少查询时分组和排序的时间 创建唯一索引,能够保证数据库表中每一行数据的唯一性 在实现数据的参考完整性方面,可以加速表和表之间 ...

  6. (转)js document.all的用法

    document.all用法 一. document.all是页面内所有元素的一个集合.例如:      document.all(0)表示页面内第一个元素 二.document.all可以判断浏览器 ...

  7. 使用rman恢复数据小结

    恢复前提有数据备份 以 alter database open resetlogs 开机以后多要做一次全备(以前的备份失效了) 恢复参数文件: restore spfile from '/home/o ...

  8. oracle数据的启动

    打开实例 , 数据库到nomount状态: startup nomount;  参数文件 内存,进程 指定控制文件数据库打开到mount状态: alter datatbase mount;  控制文件 ...

  9. pthread_join/pthread_exit的用法解析

    官方说法: 函数pthread_join用来等待一个线程的结束.函数原型为: extern int pthread_join __P ((pthread_t __th, void **__thread ...

  10. (WC2016模拟十八)【BZOJ4299】[CodeChef]FRBSUM

    咕了若干天我终于来补坑了qwq HINT $1\leq N,M\leq 10^5$ $1\leq \sum A_i\leq 10^9$ 题解: 虽然场上做出来了但还是觉得好神啊! 假设当前集合能凑出$ ...