Nodejs 客户端session vs 服务端session

Nodejs 客户端session vs 服务端session

session想来大家都不会陌生,session主要的作用就是用来记录用户状态,表明用户身份的,session的存储方式也有多样,最为传统的就是服务端保存session的内容,客户端浏览器cookie保存sessionid,服务端通过客户端每次http请求带上的cookie中的sessionid去找到对应此用户的session内容。当然我之前也发过一篇文章讲到过通过etag来做为sessionid,识别用户身份。相关文章链接:http://cnodejs.org/topic/5212d82d0a746c580b43d948

最近在做公司一个项目,我们用到了python的flask框架,一个轻量级,易上手的python web框架,有点类似nodejs中的expressjs,一些周边相关的模块集成的比expressjs好一些,题外话,写了阵python回来写node,代码那是各种整齐啊。 说重点,flask和我之前用过的其他框架有一点不同的是,它的session默认是完全保留在客户端浏览器中的,也就是说我往flask的session中写入数据,最终这些数据将会以json字符串的形式,经过base64编码写入到用户浏览器的cookie里,也就是说无须依赖第三方数据库保存session数据,也无需依赖文件来保存,这一点倒是挺有意思。

估计很多人要问我安全性问题了,session内容保存在客户端cookie,如何保证它的保密性、完整性和容量限制,保密性表示不被其他用户窥探,完整性表示不被恶意用户篡改,容量限制则表示cookie中必然无法保存过多的数据。

1、保密性,flask 这方面可以说是完全不设防,经过简单的base64编码,很容易就可以对它进行decode,然后看到其中的数据,不过话又说回来,session中本来记录的就是当前用户的一些相关身份信息和操作记录,就算被本用户看见又何妨,如果怕网络传输时被截获,那用户其他的一些 http 请求操作也照样被截获,无关乎客户端的cookie session设计

2、完整性,flask 利用开发者定义的一个私钥对数据进行简单的hash签名认证,所以我们会看到在 flask 保存在浏览器cookie中的session值,经过base64解码之后都是这样的:

{"userid":12345}.xxxxxxxxxxxxx

json字符串后面的xxxx就是利用开发人员定义的密钥对前面json字符串进行hash生成的签名,所以数据的完整性只要保证一个足够强大的密钥不被泄露就行了。

3、容量限制,这点确实是cookie session的不足,因为客户端cookie是有容量限制的,不可能像使用第三方数据库保存session那样存放大量的数据,不过一般我们开发一些web站点,session在大部分情况下主要是用来表示用户身份,比如一个 userid,一个头像地址,或者一个nickname等,真正用户的一些其他信息还是要通过userid去数据库获取的。

所以在开发一些对session保存容量要求不大,但是并发量比较大,而且需要多机器,多进程服务的项目来说,客户端session无疑是一个即轻松又节约设备的方法,至少节约了1台redis服务器。

我在开发完这个 flask 项目后,想到了node也应该在npm上有这样一个包,可惜我搜了下npmjs.org,似乎没有找到特别雷同的cookie session store的包,于是本着重复造轮的思想,写了一个让nodejs也支持客户端 cookie session保存的package,client-session,项目地址:

https://github.com/DoubleSpout/nodeClientSession

安装方法:

npm install client-session

client-session包主要解决了nodejs多进程下内存session无法同步的问题,让吊丝们的1G内存 vps 也能够不开redis,多进程同步session数据;同时在flask仅利用base64将session内容编码的基础上,做了一个小的加解密方法,让数据看上去更加安全一些,仅是安全一些,还是尽量不要保存用户密码,银行账户信息在里面哦。

client-session对数据加解密,base64编码以及签名和签名验证都是通过c++插件完成,所以相对于纯js性能上可能有那么点优势(没做过测试,不能妄下定论啊),另外在我的压力测试中,利用expressjs框架,多进程使用client-session要比使用本机开启redis数据库存session,性能高出20%左右,测试数据详见项目地址,client-session使用方法也很简单,一个结合expressjs框架返回计数器的代码例子:

 var express = require('express');
var path = require('path')
var cs = require('client-session');
var clientSession = cs('mysecretkey');
var app = express();
app.use(clientSession.connect()); //调用client-session中间件
app.get('/', function(req, res){
  var count = req.csession['count'];
  if(!count) count = 1;
  else count++;
  req.csession['count'] = count;
  //这边不想暴力的改掉express框架的res.send方法
 //就劳烦开发人员手动调用一下csflash,毕竟1台redis服务器是省下了
  req.csflush(); 
  res.send(count.toString());
});
app.listen(8124);
console.log('Server running at http://127.0.0.1:8124/');

利用ab命令:

ab -c 500 -n 20000 http://192.168.150.3:8124/

压力测试内网的一台2cpu 6Gmen linux云主机

express + redis + 2 process session::
Requests per second:    380.54 [#/sec] (mean)

express + client-session +2 process session: Requests per second: 492.25 [#/sec] (mean)

具体的client-session使用方法,详见项目github地址,有啥问题可以留言给我。

对于客户端session和服务端session的优劣特点,大家都明白了,最终如何选择还是看项目需求吧~

另外expressjs似乎没有在响应前触发的事件吧,比如:

res.on('beforeResponse', function(){
    //do something
})

19 回复

Nodejs 客户端session vs 服务端session

引言

Session 是一种常见的用于记录用户状态的技术,主要作用是标识用户身份。Session 的存储方式多样,最传统的方式是服务端保存 Session 内容,客户端浏览器通过 Cookie 保存 Session ID。当客户端每次 HTTP 请求时,会带上 Cookie 中的 Session ID,服务端通过这个 ID 找到对应的 Session 数据。

客户端 Session vs 服务端 Session

服务端 Session

在传统的服务端 Session 实现中,Session 数据通常存储在服务器端,而客户端只保存 Session ID。这种方式的优点在于安全性较高,因为 Session 数据不会暴露给客户端。但缺点是需要额外的存储资源(如 Redis 或数据库)来存储 Session 数据,并且在多进程或多机器部署时需要处理 Session 同步问题。

客户端 Session

客户端 Session 的实现方式则是将 Session 数据直接存储在客户端的 Cookie 中。这种方式的优点是简单且不需要额外的存储资源。例如,在 Flask 框架中,Session 数据会被序列化为 JSON 字符串,经过 Base64 编码后存储在 Cookie 中。

安全性考虑

虽然客户端 Session 看起来比较简单,但也存在一些安全问题:

  1. 保密性:客户端 Session 数据经过简单的 Base64 编码,容易被解码查看。但通常 Session 中存储的信息并不敏感,因此这并不是大问题。
  2. 完整性:Flask 使用开发者定义的私钥对数据进行 Hash 签名,确保数据不被篡改。客户端 Session 数据在传输过程中可以通过 HTTPS 加密来增强安全性。
  3. 容量限制:客户端 Session 的大小受限于 Cookie 的大小限制(通常为 4KB)。因此,不适合存储大量数据。

示例代码:Node.js 客户端 Session

为了在 Node.js 中实现客户端 Session,我编写了一个名为 client-session 的 npm 包。以下是使用该包的示例代码:

const express = require('express');
const path = require('path');
const cs = require('client-session');

// 初始化 client-session 中间件
const clientSession = cs('mysecretkey');

const app = express();

// 使用 client-session 中间件
app.use(clientSession.connect());

app.get('/', function(req, res) {
  let count = req.csession['count'];
  if (!count) count = 1;
  else count++;
  req.csession['count'] = count;

  // 手动调用 flush 方法更新 Cookie
  req.csflush();
  
  res.send(count.toString());
});

app.listen(8124, () => {
  console.log('Server running at http://127.0.0.1:8124/');
});

在这个示例中,我们使用 client-session 中间件来管理 Session 数据,并将其存储在客户端的 Cookie 中。每次访问根路径 / 时,计数器会递增并存储在 Session 中。

压力测试结果

在压力测试中,使用 express + client-session 的性能优于使用本地 Redis 存储 Session 的方案。具体测试数据如下:

express + redis + 2 process session:
Requests per second:    380.54 [#/sec] (mean)

express + client-session + 2 process session:
Requests per second:    492.25 [#/sec] (mean)

总结

客户端 Session 和服务端 Session 各有优缺点,最终选择哪种方案取决于项目的具体需求。对于高并发、多进程、多机器部署的项目,客户端 Session 可能是一个更轻量级的选择。但对于需要存储大量数据或对安全性要求较高的场景,服务端 Session 仍然是更好的选择。


koa 有 cookieSession 的中间件

cookieSession 性能肯定要更高,不过当 session 里需要存放的东西变多的时候就不是那么适合了

宝贝,别闹了,数据不会无缘无故的从空气中跳出来。 不管你用什么框架,总要有一个地方去存储数据,要么是磁盘要么是内存。

客户端发来cookie,你要验证cookie的正确性,就必须从服务器取出sessionid, 这个sessionid要么是在内存中,要么是在文件系统,要么是在数据库文件中。 但是归根结底,你都需要一个磁盘来放置sessionid。[文本文件或者数据库文件] 每次启动服务器,从此磁盘读取sessionid缓存到服务器上而已。

文本文件放sessionid就存在一个安全性问题,因为数据库服务器一般会防火墙限制IP,但是nodejs服务器可是对所有人开放的,极容易被攻击。

使用nodejs内核2进制编译过的crypto,你就能得到标准安全可靠单向的40位sha1加密,32位md5加密,而且只需要几行代码。

擦,又重复造了一个轮子,不过简单看了下 tj 大神的代码,似乎他对cookie session也只是做了一个base64编码和解码,koa cookie session这个模块对开发人员定义的session secret key没有做任何签名生成和签名认证,可能在框架其他地方做掉了把,我这个 client-session 好坏也对cookie里的值做了简单的加密,而且通用性好一些,可以在没有框架的地方使用,也算重复造了一个不一样的轮子把。

空间限制确实是cookie seesion的弊端,所以这个还是要根据自己的项目的实际需求选择server session和cookie session了,其实像我们cnode这种简单社区类的,完全可以用cookie session来存放一些用户的未读消息数,用户id等等数据。

你说的客户端通过sha1或md5生成sessionid存在浏览器cookie中,服务器根据sessionid获取session数据是比较传统的做法,你可以仔细看下我们的这篇文章,它所说的是另外一种session数据的存放方式,将整个session数据保存在用户浏览器的cookie里,通过签名和加密保证session数据的保密性和完整性,因为使用文件存储session数据会碰到多机器无法共享session的问题,最后只能通过前端代理服务器根据用户 ip 进行哈希将请求转发到指定服务器;而通过数据库保存session,由于用户每次请求都会产生一次对数据库的读写,所以数据库那边压力会比较大。

而使用cookie session恰恰适合对session空间存储不大的项目,同时cookie session也很好解决了上面2个问题。

”文本文件放sessionid就存在一个安全性问题,因为数据库服务器一般会防火墙限制IP,但是nodejs服务器可是对所有人开放的,极容易被攻击。“

这句话,我不是很明白你的意思

connect/express 一直都有这个东西啊… https://github.com/expressjs/cookie-session

关于加密那块 用的都是做验证 https://github.com/expressjs/keygrip

好吧,我又造了一个轮子。。。

这个包不错,用来做简单的验证,省的自己重复写了

关于这个更多讨论,可以去看一下ruby china …

https://ruby-china.org/topics/19520

顶一下 之前也简单这么干过 把用户信息md5加密之后赛到cookie 在server端简单解密 不过那时候还不知道这就是client-session 嘿嘿

只是个概念罢了~

貌似争论的很激烈

cookiestore最大的问题除了安全,就是传输cookie header的大小分分钟比响应body还要大。。。 也只能在session里面放很基本的信息,隐私信息完全不能放在里面。不过接手开发的同学未必知道这些安全问题。

session本来就不用来放大数据的, 如果出现session过大, 那是业务设计的问题。 至于说cookiestore存在莫须有的安全问题的, 劳架curl -I一下twitter和github, 看看cookie里的_xx_sess部分, 再顺手把他们黑了佐证一下自己的论点。

苏神一言中要害啊,根据实际需要自己选择呗,这些都要跟接手开发的同事事先说明好的~

同意你的观点 +1,那些认为cookiesession不安全的朋友不要臆想,可以试试自己实际攻击一次,拿到cookiesession再说cookiesession不安全

我在 https://github.com/alsotang/node-lessons/tree/master/lesson16 中写了这么一段:

不过 cookie-session 我个人建议不要使用,有受到回放攻击的危险。

回放攻击指的是,比如一个用户,它现在有 100 积分,积分存在 session 中,session 保存在 cookie 中。他先复制下现在的这段 cookie,然后去发个帖子,扣掉了 20 积分,于是他就只有 80 积分了。而他现在可以将之前复制下的那段 cookie 再粘贴回去浏览器中,于是服务器在一些场景下会认为他又有了 100 积分。

如果避免这种攻击呢?这就需要引入一个第三方的手段来验证 cookie session,而验证所需的信息,一定不能存在 cookie 中。这么一来,避免了这种攻击后,使用 cookie session 的好处就荡然无存了。如果为了避免攻击而引入了缓存使用的话,那不如把 cookie session 也一起放进缓存中。

Node.js 客户端 session vs 服务端 session

在Web应用开发中,session 是一种常见的用于管理用户会话的技术。session 的存储方式有两种主要类型:客户端 session 和服务端 session

服务端 session

服务端 session 是最常见的形式。在这种模式下,session 数据通常存储在服务器端的内存、数据库或文件系统中。客户端仅保存一个标识符(如 session ID),该标识符通过 cookie 发送到客户端。每次请求时,服务器根据这个标识符查找并更新对应的 session 数据。

优点:

  • 安全性较高:敏感数据不会直接暴露给客户端。
  • 易于实现:多数现代 Web 框架都内置了服务端 session 支持。

缺点:

  • 扩展困难:需要额外的机制(如 Redis)来处理分布式环境下的 session 同步。
  • 资源消耗:所有 session 数据都需要占用服务器内存或存储空间。

客户端 session

客户端 session 将所有的 session 数据直接存储在客户端的 cookie 中。这种方式在某些场景下非常有用,尤其是在高并发和分布式环境中。

优点:

  • 简单:不需要额外的存储层(如 Redis)。
  • 节省资源:减轻服务器端的存储压力。

缺点:

  • 安全性较低:数据暴露在客户端,可能会被篡改。
  • 容量有限:由于 cookie 大小限制,不适合存储大量数据。

示例代码

假设我们使用 Express 和 client-session 包来实现客户端 session

首先,安装 client-session

npm install client-session

接下来,在 Express 应用中使用 client-session

const express = require('express');
const cs = require('client-session');

// 创建客户端 session 中间件
const clientSession = cs('mysecretkey'); 

const app = express();

// 使用客户端 session 中间件
app.use(clientSession.connect());

app.get('/', (req, res) => {
  let count = req.csession['count'];
  if (!count) count = 1;
  else count++;
  
  req.csession['count'] = count;
  req.csflush(); // 确保 session 数据写回客户端
  
  res.send(count.toString());
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

总结

客户端 session 和服务端 session 各有优缺点。客户端 session 更适合高并发和分布式场景,而服务端 session 则在安全性方面表现更好。最终选择哪种方案取决于具体的应用需求和场景。

回到顶部