如何防止 node.js 应用程序中的会话溢出?
How to prevent overflow of sessions in node.js app?
我有一个使用 express 框架的 nodejs 网络应用程序,可以通过互联网访问它。
我正在使用一个会话存储,它将会话作为普通文件存储在磁盘上,并且在当前的实现中,每个没有 cookie 的请求都将获得一个新的会话 ID,从而在磁盘上为新会话生成一个新文件。
由于应用程序可以通过互联网访问,我收到了很多无效请求,这些请求当然不会发送 cookie,但会在我的文件系统上产生更多会话,这真是一团糟。
我使用 OWASP 会话管理作弊 sheet 作为实施指南 (https://www.owasp.org/index.php/Session_Management_Cheat_Sheet),但它没有详细介绍来宾会话的主题。它仅说明应用程序可能会发现将会话也分配给未经身份验证的(来宾)用户很有用,因此来宾会话通常似乎是一个有效的功能。
所以现在我不知道如何正确解决 invalid/malicious 请求不必要地创建 sessions/session 文件的问题。有推荐的方法吗?
我想到的可能是 'guest'-sessions 的非常短的到期时间(< 5 分钟)和 IP 范围的白名单之类的组合,其中不在白名单中的任何 IP 都不会收到访客会话(但当然成功验证后的会话)。
关于我应该如何解决这个问题的任何提示?
适合您的用例的最佳会话存储是 Redis、Memcached 或其他一些快速内存数据存储,但请注意,您的所有会话数据都必须适合 RAM。
另一种选择是使用基于磁盘的数据库,如任何 RDBMS 或 Mongo、Couch、Rethink 等,但要确保它的速度很快,否则你的性能会大幅下降。
具有最高可扩展性特征的最快方法是不在您的服务器上存储任何会话数据,而是依赖于 cookie 或其他客户端存储中发送的数据,例如使用 JWT - 请参阅:https://jwt.io/ - 但请注意,通过这种方式,您将无法控制一旦发出的会话令牌,除非您引入数据库来检查它们是否有效以及使它们无效的机制,但在这一点上您遇到的问题与将数据存储在服务器上时遇到的问题相同,可能有一个例外,即要存储的数据可能更少,而且不必经常更新。
此处的每种方法都有利有弊,但将数据存储在文件系统上的文件中从来都不是任何数据生产的最佳解决方案,不仅仅是会话数据。如果您的用例可以接受其缺点,您应该为此使用数据库或将数据存储在客户端上。
这是您要避免的事情:
app.use(session({ ... }));
app.use(express.static(...));
这将为所有静态请求创建会话。
您可以通过禁用 saveUninitialized
setting:
来缓解这种情况
app.use(session({
saveUninitialized : false,
...
}));
app.use(express.static(...));
这将防止存储新的但未修改的会话。因为静态资源不修改会话,所以不会为它们创建会话。
另一种选择是只为您的部分路线启用会话:
const session = require('express-session');
let sessionMiddleware = session({ ... });
app.use('/api', sessionMiddleware, apiRouter);
无论您如何存储会话,您都会遇到同样的问题。在某些时候,您的会话存储会溢出(运行 磁盘不足 space、运行 内存不足、运行 节点不足等)。
您需要做的是 p运行e 您的会话。除非你真的有能力无限期地存储会话,否则你应该在你的会话 cookie 上设置一个到期日期。对于客户端,浏览器将负责删除 cookie。对于服务器,您需要定期检查所有会话以查看是否有任何会话已过期。
接下来要做的很简单。无论您选择何种技术来存储会话,您只需删除过期的会话。这可以在您的节点进程内(在某些 setTimeout()
处理程序内)或在您的节点进程外(可能是一个简单的每日 cron 作业)完成。
在删除陈旧的会话文件之前,您应该允许一些宽限期(1 分钟、1 小时、1 天等),以防止在您删除会话文件和用户加载网站之间出现竞争情况。
您可能还希望允许用户在每次访问时刷新会话到期日期。对于基于文件的会话存储,只需触摸文件即可更新上次修改时间。
在一种情况下,此策略不起作用。当您出于性能原因删除数据时,某些数据库不会释放磁盘 space(例如 MySQL 与 InnoDB)。相反,数据只是被标记为已删除,但数据库仍在不断增长。在这种情况下,您唯一的出路就是更改会话存储。但由于您使用的是文件存储,这不是您需要担心的问题。
使用 Redis 存储会话,每个会话在一小时后过期。
https://github.com/tj/connect-redis
安装必要的包
npm install yarn
yarn add connect-redis, express, express-session, express, uid-safe
app.js
var connectRedis = require('connect-redis')(expressSession),
uid = require('uid-safe').sync
sessionMiddleware = expressSession({
genid: req => {
if(req.session && req.session.uid)
return req.session.uid + '_' + uid(24)
return uid(24)
},
store: new connectRedis({
port: 6379,
ttl: 3600 // 1 hour
}),
secret: 'sfsa487838787we',
name: 'live_session',
rolling: true,
saveUninitialized: false,
resave: false,
proxy: true,
logErrors: false,
cookie: {
path: '/',
domain: '.yourdomain.io',
httpOnly: true,
secure: 'yourdomain.io',
expires: new Date(Date.now() + 3600000),
maxAge: 3600000
}
我有一个使用 express 框架的 nodejs 网络应用程序,可以通过互联网访问它。 我正在使用一个会话存储,它将会话作为普通文件存储在磁盘上,并且在当前的实现中,每个没有 cookie 的请求都将获得一个新的会话 ID,从而在磁盘上为新会话生成一个新文件。
由于应用程序可以通过互联网访问,我收到了很多无效请求,这些请求当然不会发送 cookie,但会在我的文件系统上产生更多会话,这真是一团糟。
我使用 OWASP 会话管理作弊 sheet 作为实施指南 (https://www.owasp.org/index.php/Session_Management_Cheat_Sheet),但它没有详细介绍来宾会话的主题。它仅说明应用程序可能会发现将会话也分配给未经身份验证的(来宾)用户很有用,因此来宾会话通常似乎是一个有效的功能。
所以现在我不知道如何正确解决 invalid/malicious 请求不必要地创建 sessions/session 文件的问题。有推荐的方法吗?
我想到的可能是 'guest'-sessions 的非常短的到期时间(< 5 分钟)和 IP 范围的白名单之类的组合,其中不在白名单中的任何 IP 都不会收到访客会话(但当然成功验证后的会话)。
关于我应该如何解决这个问题的任何提示?
适合您的用例的最佳会话存储是 Redis、Memcached 或其他一些快速内存数据存储,但请注意,您的所有会话数据都必须适合 RAM。
另一种选择是使用基于磁盘的数据库,如任何 RDBMS 或 Mongo、Couch、Rethink 等,但要确保它的速度很快,否则你的性能会大幅下降。
具有最高可扩展性特征的最快方法是不在您的服务器上存储任何会话数据,而是依赖于 cookie 或其他客户端存储中发送的数据,例如使用 JWT - 请参阅:https://jwt.io/ - 但请注意,通过这种方式,您将无法控制一旦发出的会话令牌,除非您引入数据库来检查它们是否有效以及使它们无效的机制,但在这一点上您遇到的问题与将数据存储在服务器上时遇到的问题相同,可能有一个例外,即要存储的数据可能更少,而且不必经常更新。
此处的每种方法都有利有弊,但将数据存储在文件系统上的文件中从来都不是任何数据生产的最佳解决方案,不仅仅是会话数据。如果您的用例可以接受其缺点,您应该为此使用数据库或将数据存储在客户端上。
这是您要避免的事情:
app.use(session({ ... }));
app.use(express.static(...));
这将为所有静态请求创建会话。
您可以通过禁用 saveUninitialized
setting:
app.use(session({
saveUninitialized : false,
...
}));
app.use(express.static(...));
这将防止存储新的但未修改的会话。因为静态资源不修改会话,所以不会为它们创建会话。
另一种选择是只为您的部分路线启用会话:
const session = require('express-session');
let sessionMiddleware = session({ ... });
app.use('/api', sessionMiddleware, apiRouter);
无论您如何存储会话,您都会遇到同样的问题。在某些时候,您的会话存储会溢出(运行 磁盘不足 space、运行 内存不足、运行 节点不足等)。
您需要做的是 p运行e 您的会话。除非你真的有能力无限期地存储会话,否则你应该在你的会话 cookie 上设置一个到期日期。对于客户端,浏览器将负责删除 cookie。对于服务器,您需要定期检查所有会话以查看是否有任何会话已过期。
接下来要做的很简单。无论您选择何种技术来存储会话,您只需删除过期的会话。这可以在您的节点进程内(在某些 setTimeout()
处理程序内)或在您的节点进程外(可能是一个简单的每日 cron 作业)完成。
在删除陈旧的会话文件之前,您应该允许一些宽限期(1 分钟、1 小时、1 天等),以防止在您删除会话文件和用户加载网站之间出现竞争情况。
您可能还希望允许用户在每次访问时刷新会话到期日期。对于基于文件的会话存储,只需触摸文件即可更新上次修改时间。
在一种情况下,此策略不起作用。当您出于性能原因删除数据时,某些数据库不会释放磁盘 space(例如 MySQL 与 InnoDB)。相反,数据只是被标记为已删除,但数据库仍在不断增长。在这种情况下,您唯一的出路就是更改会话存储。但由于您使用的是文件存储,这不是您需要担心的问题。
使用 Redis 存储会话,每个会话在一小时后过期。
https://github.com/tj/connect-redis
安装必要的包
npm install yarn
yarn add connect-redis, express, express-session, express, uid-safe
app.js
var connectRedis = require('connect-redis')(expressSession),
uid = require('uid-safe').sync
sessionMiddleware = expressSession({
genid: req => {
if(req.session && req.session.uid)
return req.session.uid + '_' + uid(24)
return uid(24)
},
store: new connectRedis({
port: 6379,
ttl: 3600 // 1 hour
}),
secret: 'sfsa487838787we',
name: 'live_session',
rolling: true,
saveUninitialized: false,
resave: false,
proxy: true,
logErrors: false,
cookie: {
path: '/',
domain: '.yourdomain.io',
httpOnly: true,
secure: 'yourdomain.io',
expires: new Date(Date.now() + 3600000),
maxAge: 3600000
}