个人总结:这篇文章讲解了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. SpringMVC+Spring+Hibernate框架整合原理,作用及使用方法

    转自:https://blog.csdn.net/bieleyang/article/details/77862042 SSM框架是spring MVC ,spring和mybatis框架的整合,是标 ...

  2. C++如何调用C#编写的 DLL

    由于C#编绎出来的DLL不是计算机所能直接识别的二进制指令码,需要CLS进行再解释,说到这,我想有些朋友应该知道C#项目需要引用C++编写的DLL时,可以直接引用DLLMPORT来实现调用,而反向的话 ...

  3. kafka的使用

    kafka基于zookeeper. 需要安装kafka.zookeeper. 安装方法参考:http://tzz6.iteye.com/blog/2401197 启动zookeeper:点击zkSer ...

  4. Photoshop CC (2015.5) 2016.6 版已发布

    Photoshop CC (2015.5) 2016.6 版已发布 adobe-cc-no-more-direct-download-links.html 不再有直接下载的升级包了,不开心 :( 下载 ...

  5. Android 使用retrofit时,怎样获取响应的头信息

    这个问题,我前段时间在项目中使用Retrofit遇到过,最后查到的解决办法有两种获取Response Headers的方法,第一种是直接在定义接口是让接口返回Retrofit的Response对象,在 ...

  6. Android Fragment中调用getActivity为null的问题

       在使用fragment的时候经常会遇到getActivity()为null的情况.比如我在一个异步网路请求的回调中调用了getActivity()就会出现空指针问题.之前解决这个问题,通常都是直 ...

  7. Android ScrollView 滚动到顶部

    有时候使用ScrollView,里边控件比较多的时候,打开界面,会滑到底部,如果要设置滑动到顶部,一般有两种方法 1.使用fullScrol(),scrollView.fullScroll(Scrol ...

  8. SpringCloud学习笔记(8)----Spring Cloud Netflix之负载均衡-Ribbon的负载均衡的策略

    一. 内置 负载均衡策略的介绍的 IRule的实现类 2. 通过代码实现负载均衡 在第六节Riddom的使用的工程中,随机策略配置类 package com.wangx.cloud.springclo ...

  9. springMVC小项目实例

    一.什么是 Spring MVC Spring MVC 属于 SpringFrameWork 的后续产品,已经融合在 Spring Web Flow 里面,是一个强大灵活的 Web 框架.Spring ...

  10. ES6学习之环境配置

    环境配置 一.建立工程目录 新建dist文件夹(用于存放转化的es5文件).新建src文件夹(用于存放es6文件),在该文件夹下建立index.js文件 二.编写index.html 在根目录下新建i ...