简体   繁体   中英

How to prevent overflow of sessions in node.js app?

I have a nodejs web application which uses the express framework, and it is reachable via internet. I am using a session store which stores the sessions as plain files on disk, and with the current implementation, each request without cookie will get a new session-id, resulting in a new file on disk for the new session.

Since the application is reachable via internet, I receive a lot of invalid requests, which of course never send cookies, but produce more sessions on my filesystem, which is a real mess.

I used the OWASP session management cheat sheet as a guideline for the implementation ( https://www.owasp.org/index.php/Session_Management_Cheat_Sheet ), but it does not cover the topic of guest sessions in detail. It only states that applications might find it useful to assign sessions also to unauthenticated (guest) users, so guest-sessions seem to be a valid feature in general.

So right now I dont know how to properly fight the problem of unnecessarily created sessions/session files by invalid/malicious requests. Is there any recommended way to do this?
I thought of maybe a combination of a very short expiration of 'guest'-sessions (< 5min) and a whitelist for IP ranges or something, where any IP not in the whitelist will not receive a guest-session (but of course a session once successfully authenticated).

Any tips on how I should approach this problem?

Regardless of how you store your session you will face this same issue. At some point your session storage will overflow (run out of disk space, run out of ram, run out of inodes etc).

What you need to do is prune your sessions. Unless you really can afford to store sessions indefinitely you should set an expiry date on your session cookie. For the client the browser will take care of deleting the cookie. For the server you need to periodically check all sessions to see if any have expired.

What you do next is simple. Regardless of the technology you choose to store sessions you simply delete expired sessions. This can be done either within your node process (inside some setTimeout() handler) or outside your node process (maybe a simple daily cron job).

You should allow some grace period (1 minute, 1 hour, 1 day etc.) before deleting stale session files to prevent race condition between you deleting the session file and a user loading the website.

You may also want to allow users to refresh the session expiry date on each visit. For a file-based session store this can be as simple as touching the file to update last modified time.

There is one situation where this strategy won't work. Some databases won't release disk space when you delete data for performance reasons (MySQL with InnoDB for example). Instead the data is simply marked as deleted but the database keep growing. In such cases your only way out is to change your session storage. But since you are using file storage it's not an issue you need to worry about.

The best session storage for your use case would be Redis, Memcached or some other fast in-memory data store, but note that all of your session data will have to fit in RAM.

Another option would be to use a disk-based database like any RDBMS or Mongo, Couch, Rethink etc. but make sure that it's fast or otherwise your performance will degrade tremendously.

The fastest way with highest scalability characteristics would be to not store any session data on your server and instead rely on data sent in cookies or other client-side storage, eg using JWT - see: https://jwt.io/ - but note that this way you will not have control over once issued session tokens unless you introduce a database to check if they are valid or not and a mechanism of invalidating them, but at this point you have the same problems as with storing that data on the server, maybe with an exception that there could be potentially less data to store and that it wouldn't have to be updated often.

Every approach here has some pros and cons but storing data in the files on the file system is never an optimal solution for production for any data, not only for session data. You should use a database for that or store data on the client if the disadvantages of that are acceptable in your use case.

This is something that you want to avoid:

app.use(session({ ... }));
app.use(express.static(...));

That would create sessions for all static requests.

You can mitigate that by disabling the saveUninitialized setting :

app.use(session({
  saveUninitialized : false,
  ...
}));
app.use(express.static(...));

That will prevent new-but-unmodified sessions to be stored. Because static resources don't modify sessions, no sessions will be created for them.

Another option would be to enable sessions for only a subset of your routes:

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

let sessionMiddleware = session({ ... });

app.use('/api', sessionMiddleware, apiRouter);

Use Redis for sessions storage and expire each session after one hour.

https://github.com/tj/connect-redis

Install necessary packages

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
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM