Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Static Range Request Creating Multiple Sessions and Clobbering Cookie #394

Closed
seanabrahams opened this Issue · 15 comments

5 participants

@seanabrahams

I have a GET that stores some data to a session (req.session.token = token) and responds with a page that loads a video (809k) using HTML5's video tag.

The video is served up via static middleware as a range request (status code 206). Interestingly, it appears on each chunk Connect seems to be creating a new session in Redis and clobbering the original cookie in the user's browser.

Since the user's cookie is clobbered with the most recent session key created we no longer have access to the original session data.

Using:

node 0.4.12
├── connect-redis@1.1.0 
├─┬ express@2.4.3 
│ ├── connect@1.7.1 
│ ├── mime@1.2.4 
│ └── qs@0.3.1 
├─┬ jade@0.16.2 
│ ├── commander@0.1.0 
│ └── mkdirp@0.0.7 
└── redis@0.6.7

app.js

/**
 * Module dependencies.
 */

var express = require('express'),
  redis = require("redis"),
  RedisStore = require('connect-redis')(express);

var app = module.exports = express.createServer();

// Configuration

app.configure(function(){
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(express.cookieParser());
  app.use(express.session({ secret: "alkjaslkfjslkdfjdf", store: new RedisStore(),  key: 'express.sid' }));
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});

app.configure('development', function(){
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); 
});

app.configure('production', function(){
  app.use(express.errorHandler()); 
});

// Routes

app.get('/', function(req, res){
  req.session.token = 'Hello Token';
  res.render('index', {
    title: 'Express'
  });
});

app.listen(3000);
console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);

index.jade

h1= title
p Welcome to #{title}
video(src="/videos/3.mp4", autoplay="yes", loop="loop", preload="auto", poster="/videos/3.jpg")
  source(src="/videos/3.mp4")

HTML output

<!DOCTYPE html>
<html>
  <head>
    <title>Express</title>
    <link rel="stylesheet" href="/stylesheets/style.css"/>
  </head>
  <body>
    <h1>Express</h1>
    <p>Welcome to Express</p>
    <video src="/videos/3.mp4" autoplay="yes" loop="loop" preload="auto" poster="/videos/3.jpg">
      <source src="/videos/3.mp4"></source>
    </video>
  </body>
</html>

KEYS * in Redis before request:

redis 127.0.0.1:6379> keys *
(empty list or set)

KEYS * in Redis after 1 request:

redis 127.0.0.1:6379> keys *
1) "sess:1at93LcqrZiFAbJDj4Xdgjvk.VxUwdqRxeQDepllW763Q5xzdwWgPiXV3h7W+ktlyLJs"
2) "sess:cbf0k5BjIXgMrNoQWGaPLg2z.HdQxyfsBNAwLKcsirMxlNAGhQGQbGslBAhZB+i3c0vw"
3) "sess:rJHmen3u910vUC5L7HculveC.l++B9kqO35sEUQp79+rKaGl4nLAoNECmnE5TtQ8Ua/I"
@tj
Owner
tj commented

this is probably a two-part problem. a) we Set-Cookie always right now, which really needs to be changed (I have an issue open), b) perhaps the browser is not sending the cookie for these requests, causing connect to generate a new one. I'll see if I can reproduce this

@tj
Owner
tj commented

awesome thanks, appreciate it

@tj
Owner
tj commented

seems fine to me, I tried your repo and my own. though there is a chance that the res.end() saving the session may not have fired before assets start loading, clobbering the sess

@seanabrahams

Looks like it may be specific to Safari...

@frank06

Ran into the same Safari-specific issue. It appears to be fixed on 2.5.7.

@frank06

I retract what I just said, it does not. Testing with Express 2.5.7 and Connect 1.8.5. Only mp4 files, only on Safari cause this issue. part[0] of the cookie string != hash(part[1])... will report back if I can find it.

@seanabrahams did you find a fix?

@seanabrahams

@frank06 Unfortunately I did not. We switched directions and this became a non-issue for us.

@frank06

Ok found it (applies at least to Safari 5.0.3)

It's the default fingerprint function in Connect that makes the comparison fail and therefore triggers new session creation upon mp4 requests from Safari.

This is because mp4 requests do not send the real user agent, and the default fingerprint relies on it.

For any standard request, Safari will send something like: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-us) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4

For mp4 files, it will send: Apple Mac OS X v10.6.4 CoreMedia v1.0.0.10F569.

@visionmedia You may want to either change the default fingerprint function (at the moment I don't know to what, really) or put a big notice that it will make mp4 requests fail.

@seanabrahams

Nice!

@tj
Owner
tj commented

@frank06 I've removed it actually, it was causing too many issues (chrome frame etc), I might not have removed it for 1.x though

@lemonad

It took me a long time to debug my way here but I'm pretty sure I've hit the same issue with just using static .wav and .mp3's in Safari 5.1.3 (works fine in Chrome 18.0.1025.45 beta).

That is: executing new Audio('/sound/hello.wav'); in the client gets you a new session id.

The reason seems to be that Safari hits the .wav twice. First using the regular user-agent string:

"Mozilla/5.0 (Macintosh; Intel Mac OS X 1073) AppleWebKit/534.53.11 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10"

and the second time (through a plugin?) with another user-agent string:

"AppleCoreMedia/1.0.0.11D50b (Macintosh; U; Intel Mac OS X 1073; en_us)"

As I'm using express 2.5.8 which depends on 1.8.5, I resorted to the following workaround:

app.use(express.session({
  fingerprint: function (req) {
    return "hello";
  }
}));
@bijoutrouvaille

Same here, express 2.5.8 but on Opera 11.61 is creating an additional, seemingly unused, session keys in Redis. The opera cookie seems to remain the same. Debugging the code shows that the cookie sent by the browser is the same as what opera shows. My static middleware checks for css and javascript requests first then goes on to the custom logic, afterwards the extra session is created. The fingerprint workaround doesn't seem to work for me.

@bijoutrouvaille

It was the favicon. Apparently opera doesn't send cookie headers for those. So far I'm working around this by placing this middleware function right after my static server (which serves the favicon also). I wonder if this could be done more elegant...

app.use ( function ( req, rs, next ) {
    if ( req.url.indexOf('favicon.ico') == -1 ) {
        next ()
    } else {
        rs.end()
    }
} )
@tj
Owner
tj commented

1.8.6 has the fingerprint defaulted to '', and it's removed all together in 2.x

@tj tj closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.