【问题标题】:How to prevent overflow of sessions in node.js app?如何防止 node.js 应用程序中的会话溢出?
【发布时间】:2017-09-07 22:18:20
【问题描述】:

我有一个使用 express 框架的 nodejs web 应用程序,它可以通过互联网访问。 我正在使用一个会话存储,它将会话存储为磁盘上的普通文件,并且在当前实现中,每个没有 cookie 的请求都将获得一个新的会话 ID,从而在磁盘上为新会话生成一个新文件。

由于可以通过 Internet 访问该应用程序,因此我收到了很多无效请求,这些请求当然不会发送 cookie,但会在我的文件系统上产生更多会话,这真是一团糟。

我使用 OWASP 会话管理备忘单作为实施指南 (https://www.owasp.org/index.php/Session_Management_Cheat_Sheet),但它没有详细介绍访客会话的主题。它只是说明应用程序可能会发现将会话也分配给未经身份验证的(访客)用户很有用,因此访客会话通常似乎是一个有效的功能。

所以现在我不知道如何正确解决无效/恶意请求不必要地创建会话/会话文件的问题。有什么推荐的方法吗?
我想可能是“访客”会话(

关于我应该如何解决这个问题的任何提示?

【问题讨论】:

  • 为什么您认为在纯文件中创建和存储会话是安全的方式? session 最好存储在标记有过期日期的内存/缓存服务器中。此外,如果您收到许多无效请求,请尝试限制 IP 甚至禁止该特定 IP。无论哪种方式,我相信您都想多了,并添加了许多预防措施,这将使您的应用程序无法维护。尝试使用头盔等模块并阅读有关保护 express 应用程序的文章。

标签: node.js security session express owasp


【解决方案1】:

无论您如何存储会话,您都将面临同样的问题。在某些时候,您的会话存储会溢出(磁盘空间不足、内存不足、inode 不足等)。

您需要做的是修剪会话。除非您真的有能力无限期地存储会话,否则您应该在会话 cookie 上设置到期日期。对于客户端,浏览器将负责删除 cookie。对于服务器,您需要定期检查所有会话以查看是否有过期。

接下来要做的很简单。无论您选择何种技术来存储会话,您只需删除过期的会话即可。这可以在您的节点进程内(在一些 setTimeout() 处理程序内)或在您的节点进程外(可能是一个简单的日常 cron 作业)完成。

您应该在删除陈旧的会话文件之前留出一些宽限期(1 分钟、1 小时、1 天等),以防止在您删除会话文件和用户加载网站之间出现竞争状况。

您可能还希望允许用户在每次访问时刷新会话到期日期。对于基于文件的会话存储,这可以像触摸文件以更新上次修改时间一样简单。

这种策略在一种情况下不起作用。出于性能原因删除数据时,某些数据库不会释放磁盘空间(例如 MySQL 和 InnoDB)。相反,数据被简单地标记为已删除,但数据库不断增长。在这种情况下,您唯一的出路是更改会话存储。但由于您使用的是文件存储,因此您无需担心。

【讨论】:

  • 你在这里得到了一些好处。我已经有 OWASP 要求的会话超时和循环检查(空闲超时、绝对超时、自动更新)。它将根据这些超时删除会话。但是应用程序要求包括经过身份验证的用户会话的相当长的生命周期,因此,访客会话目前也受到影响。因此,也许我应该引入一个单独的(短)访客会话超时,并结合不将访客会话存储到持久性。
  • @user826955:是的,就是这样做的。通常,一旦您的网站变得流行,未登录的用户将远远超过已登录的用户。特别是一旦您的网站被谷歌索引。如果你在谷歌上搜索,它会被其他几个爬虫抓取。至少会有必应、雅虎和百度
【解决方案2】:

最适合您的用例的会话存储是 Redis、Memcached 或其他一些快速的内存数据存储,但请注意,您的所有会话数据都必须适合 RAM。

另一种选择是使用基于磁盘的数据库,例如任何 RDBMS 或 Mongo、Couch、Rethink 等,但要确保它的速度很快,否则您的性能会大大降低。

具有最高可扩展性特征的最快方法是不在您的服务器上存储任何会话数据,而是依赖在 cookie 或其他客户端存储中发送的数据,例如使用 JWT - 请参阅:https://jwt.io/ - 但请注意,除非您引入数据库来检查它们是否有效以及使它们无效的机制,否则您将无法控制一旦发出的会话令牌,但此时您有与将数据存储在服务器上的问题相同,但可能存在一个例外,即要存储的数据可能较少并且不必经常更新。

这里的每一种方法都有一些优点和缺点,但是将数据存储在文件系统上的文件中绝不是任何数据生产的最佳解决方案,不仅仅是会话数据。如果您的用例可以接受这样的缺点,您应该为此使用数据库或将数据存储在客户端上。

【讨论】:

  • 正如你已经提到的,正如@slebetman 提到的,存储技术是无关紧要的,因为任何可能的技术都会在某个时候溢出。我想坚持使用服务器端会话存储,因为这(至少对于我的理解/研究/知识而言)更安全,并且也被 OWASP 推荐。
【解决方案3】:

这是您要避免的事情:

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);

【讨论】:

  • 虽然我正在使用会话管理的自定义实现(并且没有根据公司要求的快速会话中间件),但我喜欢不将访客会话刷新到存储空间的想法,而只是将它们保存在内存中。可能这可以解决@slebetman 提到的想法。
  • @user826955 在请求的生命周期内不保存未修改的新会话的想法可能已经帮助您节省大量文件 I/O。
【解决方案4】:

使用 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
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-16
    • 2022-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多