3

I have a Chicken and Egg problem.

I am using a Node.js HTTPServer to route web requests to different Node.js workers, depending on their Connect.SID cookie that is sent in the request headers.

Each of my Node.js workers use Connect / Express and assign Connect.SID cookies when a request connects for the first time.

Problem

Apparently, if the request has a Connect.SID that is not registered with that instance of Node, Connect assigns a new ID.

So, I have a new Connect.SID. My HTTP Proxy logs ID 12345 and routes it to worker #5. Worker #5 sends back new ID 56344 to the browser. Next browser request - HTTP Proxy sees a brand new ID - 56344 - logs it and sends to Worker #6. Worker #6 sees new SID and....

The Connect Session middleware is too... simple...

app.use(express.cookieParser());
app.use(express.session({ secret: "niceTry", cookie: { /* ... */ } }));

Is there a way to inject logic into its assignment of a new Session ID so it skips it if there is already an unregistered, but valid, Connect.SID?

Community
  • 1
  • 1
dthree
  • 19,847
  • 14
  • 77
  • 106

2 Answers2

5

Have you considered using distributed session management (i.e. Redis) with Node/Express? That should prevent what is effectively a session pinning issue.

MichaelMilom
  • 3,024
  • 1
  • 16
  • 25
  • I'm running Windows servers, unfortunately. From what I hear, Redis and Windows don't vibe too well, though if it did work out that would be my first choice. The only other objection to that as well is I am trying to keep my application as simple as possible in terms of running external applications. I'm running `forever` and `http-proxy` as they are both Node.js-based, and so the idiot systems admin who isn't really a systems admin at the other end of the planet has to worry about keeping 0-1 programs running, not 15. – dthree Nov 25 '13 at 07:26
  • I wonder if you could install a custom handler between the cookie parser and the session use declarations to handle what you need done? I have no idea how that would work, but that seems like it might be worth exploring. The other alternative is to fork the Express session code and try to work it out there. Ick. – MichaelMilom Nov 25 '13 at 07:30
  • Yeah, ick is right. The custom handler is kind of what I am asking to do. I am no middleware expert. – dthree Nov 25 '13 at 07:32
  • Okay, so what should happen if there is a Connect.SID cookie, but that particular node did not create the session? Should that worker do anything with the request? – MichaelMilom Nov 25 '13 at 07:40
  • Well, that's exactly what's happening. Ideal Scene - That worker then 'registers' the cookie, and gets on with it, not sending a new cookie. Perhaps I could look around Connect's source and find the hash where sessions are stored and inject it in before the middleware handles the cookie.... – dthree Nov 25 '13 at 07:42
  • So I found it in the Connect source code, maybe I can sort of figure out how to circumvent it... Unfortunately my console.logs aren't showing up on my server... strange. – dthree Nov 25 '13 at 07:51
  • One other option to think about is ginning up a quick alternative to MemoryStore that skips the check on a stored session. The code doesn't really look all that heinous. https://github.com/senchalabs/connect/blob/master/lib/middleware/session/memory.js – MichaelMilom Nov 25 '13 at 07:53
  • Yeah i'm almost done diving in - I think I am just gonna insert the session into the Connect store object. – dthree Nov 25 '13 at 08:11
  • Good luck with that! Great question, by the way. – MichaelMilom Nov 25 '13 at 08:19
  • Thanks! Well, I got it working by modifying Connect's source code. Now if I can only find a way to get it to my server.js file so I don't have to carry around a forked Connect.... – dthree Nov 25 '13 at 08:27
  • Finally figured it out (see answer below). – dthree Nov 25 '13 at 19:22
2

I managed to modify Connect's source code to get this working, as below. However, I am still looking for a way to move these modifications out of Connect, so I don't have to mess around with a fork of it.

node_modules\connect\lib\middleware\session.js - append after line 312:

if (!sess) {
  store.sessions[req.sessionID] = JSON.stringify({
    "cookie": {
      "originalMaxAge": void 0,
      "expires": void 0,
      "httpOnly": void 0,
      "path": void 0,
      "passport": void 0,
    }
  });
  next();
  return;
} 

By default, Connect creates a new session if the given SID is not in its store. This bypasses that, inserting it manually into the store.

How does one port this out of Connect.js to some middle-ware, perhaps, right above:

app.use(express.cookieParser());
app.use(express.session({ secret: "niceTry", cookie: { /* ... */ } }));

UPDATE

Okay I finally figured it out:

I added middleware to my main server.js file, and this handles the problem without modifying connect's source code.

var MemoryStore = require('connect/lib/middleware/session/memory');
app.store = new MemoryStore();

app.use(express.cookieParser());

app.use(function(req, res, next){

  var cookie = req.signedCookies['connect.sid'];
  if (!cookie && req.cookies['connect.sid']) {
    cookie = connect.utils.parseSignedCookie(req.cookies['connect.sid'], app.cookieSecret);
  }

  app.store.get(cookie, function(err, sess){
    if (!sess) {
      app.store.sessions[cookie] = JSON.stringify({
        "cookie": {
          "originalMaxAge": void 0,
          "expires": void 0,
          "httpOnly": void 0,
          "path": void 0,
          "passport": void 0,
        }
      });
      next();
    } else {
      next();
    }
  });
});

app.use(express.session({ store: app.store, secret: app.cookieSecret, cookie: { httpOnly: false, maxAge: null } }));
dthree
  • 19,847
  • 14
  • 77
  • 106