Giter VIP home page Giter VIP logo

node-client-sessions's Introduction

build status

client-sessions is connect middleware that implements sessions in encrypted tamper-free cookies. For a complete introduction to encrypted client side sessions, refer to Francois Marier's blog post on the subject;

NOTE: It is not recommended using both this middleware and connect's built-in session middleware.

Installation

npm install client-sessions

Usage

Basic usage:

var sessions = require("client-sessions");
app.use(sessions({
  cookieName: 'mySession', // cookie name dictates the key name added to the request object
  secret: 'blargadeeblargblarg', // should be a large unguessable string
  duration: 24 * 60 * 60 * 1000, // how long the session will stay valid in ms
  activeDuration: 1000 * 60 * 5 // if expiresIn < activeDuration, the session will be extended by activeDuration milliseconds
}));

app.use(function(req, res, next) {
  if (req.mySession.seenyou) {
    res.setHeader('X-Seen-You', 'true');
  } else {
    // setting a property will automatically cause a Set-Cookie response
    // to be sent
    req.mySession.seenyou = true;
    res.setHeader('X-Seen-You', 'false');
  }
});

You can control more specific cookie behavior during setup:

app.use(sessions({
  cookieName: 'mySession', // cookie name dictates the key name added to the request object
  secret: 'blargadeeblargblarg', // should be a large unguessable string
  duration: 24 * 60 * 60 * 1000, // how long the session will stay valid in ms
  cookie: {
    path: '/api', // cookie will only be sent to requests under '/api'
    maxAge: 60000, // duration of the cookie in milliseconds, defaults to duration above
    ephemeral: false, // when true, cookie expires when the browser closes
    httpOnly: true, // when true, cookie is not accessible from javascript
    secure: false // when true, cookie will only be sent over SSL. use key 'secureProxy' instead if you handle SSL not in your node process
  }
}));

You can have multiple cookies:

// a 1 week session
app.use(sessions({
  cookieName: 'shopping_cart',
  secret: 'first secret',
  duration: 7 * 24 * 60 * 60 * 1000
}));

// a 2 hour encrypted session
app.use(sessions({
  cookieName: 'authenticated',
  secret: 'first secret',
  duration: 2 * 60 * 60 * 1000
}));

In this example, there's a 2 hour authentication session, but shopping carts persist for a week.

Finally, you can use requestKey to force the name where information can be accessed on the request object.

var sessions = require("client-sessions");
app.use(sessions({
  cookieName: 'mySession',
  requestKey: 'forcedSessionKey', // requestKey overrides cookieName for the key name added to the request object.
  secret: 'blargadeeblargblarg', // should be a large unguessable string or Buffer
  duration: 24 * 60 * 60 * 1000, // how long the session will stay valid in ms
}));

app.use(function(req, res, next) {
  // requestKey forces the session information to be
  // accessed via forcedSessionKey
  if (req.forcedSessionKey.seenyou) {
    res.setHeader('X-Seen-You', 'true');
  }
  next();
});

Cryptography

A pair of encryption and signature keys are derived from the secret option via HMAC-SHA-256; the secret isn't used directly to encrypt or compute the MAC.

The key-derivation function, in pseudocode:

  encKey := HMAC-SHA-256(secret, 'cookiesession-encryption');
  sigKey := HMAC-SHA-256(secret, 'cookiesession-signature');

The AES-256-CBC cipher is used to encrypt the session contents, with an HMAC-SHA-256 authentication tag (via Encrypt-then-Mac composition). A random 128-bit Initialization Vector (IV) is generated for each encryption operation (this is the AES block size regardless of the key size). The CBC-mode input is padded with the usual PKCS#5 scheme.

In pseudocode, the encryption looks like the following, with || denoting concatenation. The createdAt and duration parameters are decimal strings.

  sessionText := cookieName || '=' || sessionJson
  iv := secureRandom(16 bytes)
  ciphertext := AES-256-CBC(encKey, iv, sessionText)
  payload := iv || '.' || ciphertext || '.' || createdAt || '.' || duration
  hmac := HMAC-SHA-256(sigKey, payload)
  cookie := base64url(iv) || '.' ||
    base64url(ciphertext) || '.' ||
    createdAt || '.' ||
    duration || '.' ||
    base64url(hmac)

For decryption, a constant-time equality operation is used to verify the HMAC output to avoid the plausible timing attack.

Advanced Cryptographic Options

The defaults are secure, but may not suit your requirements. Some example scenarios:

  • You want to use randomly-generated keys instead of using the key-derivation function used in this module.
  • AES-256 is overkill for the type of data you store in the session (e.g. not personally-identifiable or sensitive) and you'd like to trade-off decreasing the security level for CPU economy.
  • SHA-256 is maybe too weak for your application and you want to have more MAC security by using SHA-512, which grows the size of your cookies slightly.

If the defaults don't suit your needs, you can customize client-sessions. Beware: Changing keys and/or algorithms will make previously-generated Cookies invalid!

Configuring Keys

To configure independent encryption and signature (HMAC) keys:

app.use(sessions({
  encryptionKey: loadFromKeyStore('session-encryption-key'),
  signatureKey: loadFromKeyStore('session-signature-key'),
  // ... other options discussed above ...
}));

Configuring Algorithms

To specify custom algorithms and keys:

app.use(sessions({
  // use WEAKER-than-default encryption:
  encryptionAlgorithm: 'aes128',
  encryptionKey: loadFromKeyStore('session-encryption-key'),
  // use a SHORTER-than-default MAC:
  signatureAlgorithm: 'sha256-drop128',
  signatureKey: loadFromKeyStore('session-signature-key'),
  // ... other options discussed above ...
}));

Encryption Algorithms

Supported CBC-mode encryptionAlgorithms (and key length requirements):

Cipher Key length
aes128 16 bytes
aes192 24 bytes
aes256 32 bytes

These key lengths are exactly as required by the Advanced Encryption Standard.

Signature (HMAC) Algorithms

Supported HMAC signatureAlgorithms (and key length requirements):

HMAC Minimum Key Length Maximum Key Length
sha256 32 bytes 64 bytes
sha256-drop128 32 bytes 64 bytes
sha384 48 bytes 128 bytes
sha384-drop192 48 bytes 128 bytes
sha512 64 bytes 128 bytes
sha512-drop256 64 bytes 128 bytes

The HMAC key length requirements are derived from RFC 2104 section 3. The maximum key length can be exceeded, but it doesn't increase the security of the signature.

The -dropN algorithms discard the latter half of the HMAC output, which provides some additional protection against SHA2 length-extension attacks on top of HMAC. The same technique is used in the upcoming JSON Web Algorithms AES_CBC_HMAC_SHA2 authenticated cipher.

Generating Keys

One can easily generate both AES and HMAC-SHA2 keys via command line: openssl rand -base64 32 for a 32-byte (256-bit) key. It's easy to then parse that output into a Buffer:

function loadKeyFromStore(name) {
  var text = myConfig.keys[name];
  return Buffer.from(text, 'base64');
}

Key Constraints

If you specify encryptionKey or signatureKey, you must supply the other as well.

The following constraints must be met or an Error will be thrown:

  1. both keys must be Buffers.
  2. the keys must be different.
  3. the encryption key are exactly the length required (see above).
  4. the signature key has at least the length required (see above).

Based on the above, please note that if you specify a secret and a signatureAlgorithm, you need to use sha256 or sha256-drop128.

License

This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

node-client-sessions's People

Contributors

benadida avatar brianloveswords avatar busticated avatar cainus avatar davidben avatar fmarier avatar jclem avatar jfirebaugh avatar joegoldbeck avatar lloyd avatar mmalecki avatar ozten avatar pdehaan avatar q42jaap avatar ralucas avatar rdegges avatar romank8k avatar seanmonstar avatar stash avatar trestletech avatar trygve-lie avatar tschmittni avatar vladikoff avatar yhahn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

node-client-sessions's Issues

Session cookie is not returned by IE 11 on Windows 8.1

We have the following scenario:
Middleware initialization:
app.configure(function() {
...
app.use(clientSession({
cookieName: 'userinfo',
secret: 'SOME_SECRET',
cookie: {
domain: '.mydomain.ch'
}
}));
...
});

Later we only use the userinfo attribute on the request object.
On all browser this works fine.
Except:
IE 11 (11.09600.176239), Windows 8.1: NOT WORKING
IE 11 (11.09600.17207), Windows 7: OK
(The configuration of IE on windows 7 and Windows 8.1 is the same (authorize cookies...))
Chrome, Windows 8.1: OK

By analyzing the network traffic it turned out that the auth. request returned a set cookie in the response as follows:

Set-Cookie: userinfo=<<SOME_ENCRY_DATA>>; path=/; expires=Wed, 03 Sep 2014 09:09:46 GMT; domain=.mydomain.ch; httponly

All subsequent request do not contain the session cookie. So we get only 401 after the authentification. By analyzing the server logs it turned out that the auth. on the server side worked fine. Actually for some reason IE 11 on Windows 8.1 do not return the session cookie.

Can you reproduce this issue?
Is there is something wrong with our middleware initialization?
Is this is a bug in IE11 on Windows 8.1?

Just in case: we use a https connection for this server. In front of the node js application is a nginx server. The https channel ends in the nginx server (no ssl in node js application)

Please advise.

Improve security so secret can be used with multiple cookies

We can try to remember to not use the same secret for multiple cookies, but people are bound to do it and not realize there's a problem. So we should fix it anyway.

@benadida suggested we need to encrypt the cookieName into the cookie also, so you can't swap the cookies around? That's updating encode and decode? https://github.com/mozilla/node-client-sessions/blob/master/lib/client-sessions.js#L37

Would that just involve adding the cookieName to the hmac, separated by another period?

Cookies not marked as secure?

Hi,

I was inspecting the cookie generated by my app (using client-sessions, of course) and I see that it's not marked as "secure".

If you check the cookie in Chrome with the Cookies extension app, you'll see that it's marked as Session, Host-Only, Read-Only, but not Secure. Is this a bug in Cookies or is client-sessions not setting the right bits? Just curious...

Thanks!

set duration to forever

Hi,
as the title describes, i would like to keep a session forever, for example to understand if it is the first time a user visits the site or it isn't.

Which value of max duration I should specify?

please publish v0.1.0

I've tagged v0.1.0 which includes a changelog with pertinent changes - basically 0.1.0 support and a contributed bug fix

care to

$ git fetch origin
$ git checkout v0.1.0
$ npm publish

🍺 ?

Unable to determine the session expiration value

Given a session, is it possible to determine what the expiration date is? I was hoping to have access to expiresIn or a similar property, but I don't see any property I can use.

Any ideas? Thanks!

Prevent reassignment of req.session

As seen in #49, overriding the the session property will break client-sessions. Users may think that they can do something like this:

req.session = {
  user: '[email protected]',
  color: 'blue'
}

But really, that replaces the special sessions proxy.

Likely, we should use Object.defineProperty(req, cookieName, { writeable: false });

Bug: Expiry is set in seconds instead of milliseconds

Expiry is derived from maxage in the current code as such:

// support for maxAge
if (opts.cookie.maxAge) {
this.expires = new Date(new Date().getTime() + opts.cookie.maxAge);
}

However, maxage should be expressed in seconds, while expires is expressed in milliseconds.

The trivial change below solves this:

// support for maxAge
if (opts.cookie.maxAge) {
this.expires = new Date(new Date().getTime() + (opts.cookie.maxAge*1000));
}

Best,
Geert-Jan

exception under certain conditions

I don't understand what those conditions are yet (guessing it's when a cookie is set and I change the secret??). Here's my cookie:

Name:   session_state
Content:        PkZ1D8OWfcOZNm8BFMOcw4vDgsO0wrk.w4PDllrDl39zCMO9T8OVwpNUwp42woJwE8O6w7VDw5ttw5vClztRQ 8OnNMKVBMOYwqHDh3Zpw4luUsKLw7LDoMKWLMOAw6VKMw.1366288094330.wpfChGVmJhVTNcKQw6sWbcOFwrgqQFNhVB9vR8KWEcO5dcKWw7TDksO3Z8O4
Domain: mozilla.personatest.org
Path:   /
Send for:   Any kind of connection
Accessible to script:   No (HttpOnly)
Created:    Thursday, April 18, 2013 6:28:14 AM
Expires:    Thursday, May 2, 2013 6:28:14 AM

Here's the stack trace:

TypeError: Cannot read property 'content' of undefined
    at Object.Session.unbox (/home/app/code/node_modules/client-sessions/lib/client-sessions.js:202:24)
    at Object.Session.loadFromCookie (/home/app/code/node_modules/client-    sessions/lib/client-sessions.js:229:12)
    at Object.Session.monitor.sessionHandler.get (   (/home/app/code/node_modules/client-sessions/lib/client-sessions.js:262:37)
    at Object.module.exports [as handle]  (/home/app/code/node_modules/express/node_modules/connect/lib/middleware/csrf.js:77:28)
    at next (/home/app/code/node_modules/express/node_modules/connect/lib/http.js:204:15)
    at Object.cookieSession [as handle] (/home/app/code/node_modules/client-sessions/lib/client-sessions.js:345:5)
    at next (/home/app/code/node_modules/express/node_modules/connect/lib/http.js:204:15)
    at Object.methodOverride [as handle] (/home/app/code/node_modules/express/node_modules/connect/lib/middleware/methodOverride.js:35:5)
    at next (/home/app/code/node_modules/express/node_modules/connect/lib/http.js:204:15)
    at Object.bodyParser [as handle] (/home/app/code/node_modules/express/node_modules/connect/lib/middleware/bodyParser.js:88:61)

Why session cookie is set twice?

I am using restify with client-sessions.

server.use(sessions({
    cookieName: 'session',
    secret: 'abcdef',
    duration: 24 * 60 * 60 * 1000,
    activeDuration: 1000 * 60 * 5
}));

When I set session variable in a route:

function(req,res,next){
    req.session.anykey = "anyvalue";
    res.json({});
}

Then I get back this in response:

set-cookie:session=JlZ2g5qGgtBUwArHo19CCA.8NVuDweZ6MFPuJNFTL5ZyGF8H1oSoCjJc-2HYhck63E.1384044894098.86400000.MheV0OaZDTcFmmnvRNjySMtf4szA2e9wbpKWqKZOuy4; path=/; expires=Mon, 11 Nov 2013 00:54:55 GMT; httponly
set-cookie:session=WL2MVIidULyx-gGu9PAo3A.8sUMezXN2wy1Dyvyzg2kQSSmFRXh3TDQ_VZ_pGriENE.1384044894098.86400000.jLxQaqSGRAsQLHSDkux6wH-6AuoKSj1ai_CuMxJOu6A; path=/; expires=Mon, 11 Nov 2013 00:54:55 GMT; httponly

Why session cookie is set twice??

Moreover req.session is empty on every request even if I set it. So it seems that something is sending empty/corrupted session cookie...

Manually decrypt cookie?

I am attempting to incorporate integration tests into an application using this module. As the testing system (CasperJS) has no awareness of the back-end, I can't easily decrypt the cookies for testing.

I can access the cookies and know the secret used on the server-side. With those two items in hand, how can I decrypt the cookies (in order to ensure their contents are as expected and use them as input for further tests)?

Thanks!

npm unpublish node-client-sessions

npm search client-sessions returns

client-sessions       secure sessions stored in cookies  =benadida  2012-01-19 20:07
node-client-sessions  secure sessions stored in cookies  =benadida  2011-12-30 20:48

I think you need to edit your package.json and give it your old node-client-sessions, then

npm unpublish

Then replace package.json with client-sessions.

I've never done this, so ... it's a guess.

An empty session is created on reads when the cookie is missing

The readme says this:

console.log(req.session.baz)
// no updates to session results in no Set-Cookie header

However, that's not actually true. What happens is that when we try to read an empty session (i.e. cookie is missing), a new empty session is created (the loadFromCookie() function calls the reset() function). This can be demonstrated by running the following program and looking at the headers.

Where this can lead to subtle bugs is when you use different paths in your application and set the cookie to be on a specific one. For example, in this larger program, you get the following behavior:

  1. hit http://localhost:8000/api/foo and see that no cookie is set (this will create an empty cookie)
  2. login using http://localhost:8000/api/login and get redirected to /api/foo which will show you that you are logged in
  3. go to http://localhost:8000/foo which won't be able to read the cookie that's set under /api
  4. go back to http://localhost:8000/api/foo and see that the session has been overwritten :(

What happens in this case is that in step 3, client-sessions cannot read the cookie under the /api path but it can set a cookie under that path without problems. So after it fails to read the existing session cookie, it decides to write an empty one which unfortunately kills the session.

not really a bug but... (non-connect usage)

I'm trying this (which I realize is not really supported, because it's not using "connect"):

var clientSessions = require('client-sessions');
var http = require('http');

var server = http.createServer(function (req, res) {
  clientSessions({
    cookieName: 'session_state',    // defaults to session_state
    secret: 'testsecret', // MUST be set
    duration: 24 * 60 * 60 * 1000
  })(req, res, function(err){
      if (err) throw err;
      console.log(req.session);
      if (!req.session.user){
        req.session.user = 0;
      }
      console.log(req.session);
      req.session.user += 1;
      console.log(req.session);
      res.write('' + req.session.user++);
      res.end();
    });
});

// now that proxy is running
server.listen(1337, function() {
  console.log("listening on 1337");
});

Any idea why that shouldn't work? It seems like the Set-Cookie header just plain isn't being written, because I see no cookie-related headers in either direction.

Maybe I've got a more fundamental misunderstanding of this though.

Thanks!

Any way to turn off encryption?

For the sake of some tests I am writing, I am needing to turn off encryption of cookies in my testing environment. Is there an easy/recommended way to make this happen?

Thanks!

cookie config too clever

The secure flag of the cookie has different default values, depending on if it's being run from an HTTPS server or not.

This is too clever. Config should have default values and be overridden.

It would be great if node-client-sessions detected potentially insecure setups and warned or threw, but silently changing defaults is a bad idea.

I found this out by using cookie defaults, developing under https locally and then deploying in a way that client-sessions thought it wasn't under SSL, so it changed opt.cookie.secure behavior.

Increment duration on every request

Hello,

I'd like to make sure I understand the purpose of activeDuration:

// if expiresIn < activeDuration, the session will be extended by activeDuration milliseconds
  activeDuration: 1000 * 60 * 5 

How does this work exactly? Suppose I set the cookie with a 24 hour expiration:

var sessions = require("client-sessions");
app.use(sessions({
    cookieName: 'mySession', // cookie name dictates the key name added to the request object
    secret: 'blargadeeblargblarg', // should be a large unguessable string
    duration: 24 * 60 * 60 * 1000 // how long the session will stay valid in ms
}));

As the client performs requests, I'd like to extend the life of the cookie, that is, set its expiration to be 24 hours from the time of the last request. Is there a way to refresh the duration? Is this what activeDuration is for? If not, any idea how I can make it work?

Thanks!

Node crypto warning Decipher encoding

With version 0.2.0, we see the following warning logged:

node-crypto : Decipher .update encoding can be binary, hex or base64

I've tracked this down to where we pass utf8 into the update method.

Changing this to 'binary' suppresses the error message, but I don't know if it is the right thing to do.

var plaintext = cipher.update(ciphertext, 'binary');

/CC @benadida @jrgm @seanmonstar

.setDuration() should update session creation time

If a session is created with a duriation of one hour, then 5 minutes pass, and the client calls .setDuration(5 minutes), because currently createdAt is not updated, suddenly you have an invalid session.

I think .setDuration() should update session creation time. I don't think other modifications to the session neccesarily should.

Do you really need npm 1.1.17 to install this?

I believe npm 1.1.4 is default on Ubuntu 12.04 LTS and it is a %$#! to update.

Using the curl update thingie like so:
curl https://npmjs.org/install.sh | sudo sh

just smiles in your face like

All clean!
/usr/local/bin/npm -> /usr/local/lib/node_modules/npm/bin/npm-cli.js
npm@1.1.71 /usr/local/lib/node_modules/npm
It worked

while secretly it just stays at 1.1.4 FOREVER.

So do we really need 1.1.17? If so, why?

What happens when the cookie has been compromised?

Assume the following setup:

app.use(sessions({
  cookieName: 'mySession', // cookie name dictates the key name added to the request object
  secret: 'blargadeeblargblarg', // should be a large unguessable string
  duration: 24 * 60 * 60 * 1000, // how long the session will stay valid in ms
  cookie: {
    path: '/api', // cookie will only be sent to requests under '/api'
    httpOnly: true, // when true, cookie is not accessible from javascript
    secure: true   // when true, cookie will only be sent over SSL
  }
}));

If mySession is tampered with, how will client-sessions deal with this situation? Once node.js processes the request, what will happen when I access mySession?:

var mySession = req.mySession;

Will mySession be:

  1. Null?
  2. Empty?
  3. Something else?

Thanks!

There is two versions of this library in npm, delete one

Since this project has changed name it seems to be two versions of this library in npm: https://npmjs.org/package/client-sessions and https://npmjs.org/package/node-client-sessions

The npm package which has the same name as this git repo is the outdated one. This can cause confusion.

I think the outdate one can safely be removed. From the npm numbers it looks like a very small amount of users is using it.

Btw: This Mozilla article does point to the previous git repo and does now have some 404s: https://hacks.mozilla.org/2012/12/using-secure-client-side-sessions-to-build-simple-and-scalable-node-js-applications-a-node-js-holiday-season-part-3/

cookieName, client-session and passportjs

Hello,
I have seen that we can set cookieName to anything we want in this library.
and access it with req.cookieName now will this work with passportjs? can we set req.session directly with this library?

I am trying to implement client-session to work with passportjs. What would be the recommended configuration?

Bug when resetting session where cookie is an object

Hi
I am using Passport.js and Google Auth along with node-client-sessions to handle users' session. Everything is working quite well but when I try to reset the session, I get the error:

TypeError: Object { uid: 'AItOawmNEye27rYg8ginQ5vgZG8Gfd1sjvCqr_o',
ref: 'str',
_id: 51c924289782944904000001 } has no method 'reset']

In my cookie, I am storing the user if and a string called 'ref'. Any idea where it might come from and how to fix it ?
Cheers

Koa support ?

would anyone be interested in making this middleware work with generators-based frameworks like koa ?

Currently I'm using this workaround, but I'd prefer a less hack-ish way.

Make it possible to unbox a session cookie outside a http server

Please look at this issue as a suggestion for an improvement or a request for a better solution to the issue I would like to address.

As this module works now it looks like its very bound to being used in only a http server. Though, it would be very nice to be able to use this with, ex, a Web Socket server also. As I've seen it, one of the main issues when dealing with authentication in a Web Socket context is that a Web Socket server normally is a separate instance in the same server as the http server.

I'll try to explain the problem through an example: Most authentications where a Web Socket should be established only for authenticated clients is done like this:

  1. From the browser, post username / password to the http server.
  2. On the server, validate the post against a user database.
  3. If validation returns true, create a session object on the server and store it in a session storage with a session ID as the key in the storage. In many cases redis is used for this.
  4. Return OK to the browser and write a cookie in the browser which holds the session ID.
  5. Then the browser tries to open up a Web Socket connection.
  6. When the Web Socket server receives the initial http request (before the upgrade) it has access to the cookie in step 4. Decode and get the session ID stored in it.
  7. Look up in the session storage with the session ID from the cookie and if there is a matching session object in the session storage, establish the Web Socket connection.

In many cases the session storage is only used to stuff away the session information so the Web Socket server can access it. In other words it's added complexity.

With the approach this module has to sessions this could be done a bit simpler:

  1. From the browser, post username / password to the http server.
  2. On the server, validate the post against a user database.
  3. If validation returns true, create a client session as normal with this module.
  4. Return OK to the browser and set some kind of info in the client session that access through Web Sockets should be granted.
  5. Then the browser tries to open up a Web Socket connection.
  6. When the Web Socket server receives the initial http request (before the upgrade) it has access to the cookie in step 4. Decode and get the info about if a Web Socket connection should be granted access or not.
  7. If step 6 returns true, establish a Web Socket connection.

Here is an example of such a implementation (using ws as Web Socket library):

var http              = require('http'),
    express           = require('express'),
    clientSessions    = require('client-sessions'),
    WebSocketServer   = require('ws').Server,
    app               = express();

app.configure(function() {
  app.use(clientSessions({secret: 'foobar'}));
  app.use(app.router);
  app.use(express.static('./public'));
});

app.post('/login', function (req, res){
  req.session.wsaccess = true;
  res.json({auth:'ok'});
});

app.get('/logout', function (req, res) {
  req.session.reset();
  res.json({auth:'fail'});
});

var httpServer = http.createServer(app);

// Function to authenticate if web socket access 
// should be granted
function wsAuth(result) {
  // NOTE: This is the missing part:
  // Decode "result.req.headers.cookie" and then check
  // if "wsaccess" is "true" or not.
}

var wsServer = new WebSocketServer({
  server:  httpServer, 
  verifyClient: wsAuth
});

httpServer.listen(8080);

The issue I'm trying to address here is that in the last approach I'm not able to decode the cookie in a authentication function passed to a Web Socket server.

Is the last approach an OK solution to handle authentication of a Web Socket connection or is there a better ways to do this? If its an OK solution, would you consider adding support for unboxing the session cookie outside of the http server context?

csrf protection

connect requires req.session to respond to ._csrfSecret property for its csrf protection to work.

The secret needs to persist across server instances. connect expects server-side sessions, so the secret is not sent to client. But it is not an option to just store the secret in an instance of node-client-sessions (it will be happily dispatched to client).

It seems that encryptionKey could serve as _csrfSecret, if it is special-cased to be excluded from cookies.

Could a patch along these lines be accepted:

  • add a boolean csrf option
  • if csrf is true and cookieName is not session, provide req.session
  • if csrf is true and cookieName is session, exclude req.session._csrfSecret from being sent to client
    ?

regexes not declared first?

I'm seeing lots of regexes being used inline. Not sure with the new v8 stuff, but isn't this way slower than if they are just declared outside of the functions they're used in? My experience is that regexes are sloooowww ( to declare)..

var regex = /asdf/;
function compute(arg){
   arg.match(regex);
}

not

function compute(arg){
   arg.match(/asdf/);
}

Size limit on request session object

Is there a size limit on the session object?

When I attach an array with length 3 onto the session object, the object is persisted across requests to the server. However when the length is 16, for some reason the session object gets destroyed.

I don't know why this would be the case but thought I would ask incase anyone ran into the same issue.

Support UTF-8 strings in session dictionary

It seems that UTF-8 characters are not properly encoded by this middeware. For example the code below:

req.session.name = "Łukasz"

results in the value being garbled after decoding on next request:

console.log(req.session.name) //prints Aukasz

re-using cookie name as req. key is too limiting

in change 42c5f43 we said that no longer would cookies be attached to req.session, but instead to req.<cookie_name>.

This is useful because it allows us to have multiple cookies, but it's:

  1. unexpected (took me a while to figure out what was going on)
  2. poorly documented
  3. doesn't allow someone to migrate to client-sessions 0.3.0 without changing their cookie name or changing a bunch of code

Sharing cookies with non-node servers

I would like to use client-sessions for an app that accesses apis on multiple servers. There is a Ruby on Rails server among them. Is there a straightforward way to decrypt the cookies in the RoR server please?

Consider HMAC-SHA512?

The JSON Web Algorithms Draft Spec combines AES256 with HMAC-SHA512 using a distinct 32-byte (256-bit) key for each. The reason for this is that SHA512 provides a comparable security level to AES256 (as does SHA256 for AES128).

Would it make sense to offer this as a configuration option? If so, either the deriveKey function will need to get updated to support variable length output (HKDF) or the use of the .encryptionKey and .signatureKey options would be required.

session can expire while user is engaged

@lloyd proposes:

  • your session can live an extra ~6 hours and will be extended automatically if you continually access within an hour window.
  • if you're out of the 6 hour grace, or haven't accessed in an hour and your session is done, then it's reset.

tests should not output 'error'

We test codepaths where we attempt to 'have a secure cookie on an insecure socket and do not declare req.connection.proxySecure'. This codepath causes our middleware to call next() with an error value. This causes the string 'error' to be output on the terminal while running tests.

That is disheartening.

We should improve the error message, and we should not output it to the console while tests are run.

httpOnly option not respected

Code to reproduce:

'use strict';
var restify = require('restify'); 
var sessions = require('client-sessions');

var server = restify.createServer({
   name: 'test-sessions',
   version: '0.0.1'
});

server.use(sessions({
    cookieName: 'session',
    secret: 'example',
    httpOnly: false,
    duration: 24 * 60 * 60 * 1000
}));

server.get('/status', function (req, res, next) {
    req.session.username = 'timemachine';
    res.send(200, {
        status: 'Hello World!!!!'
    });
    next();
});

server.listen(3000);
console.log('Restify server listening on port: ' + 3000);

Installed Libraries:

[email protected]
[email protected]
[email protected]

Steps to reproduce:

start server: node server.js
GET /status: curl --include http://localhost:3000/status

Expected results

Set-Cookie does not contain httponly

Actual results

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 28
Set-Cookie: session=[...]; path=/; expires=Wed, 13 Aug 2014 15:29:05 GMT; httponly
Date: Tue, 12 Aug 2014 15:29:04 GMT
Connection: keep-alive

{"status":"Hello World!!!!"}

Additional notes

I also tried to spell it in all lowercase: httponly

Thank you for you attention in this!

Session Cookie Being Ignored By Chrome

It seems weird that this would be an issue with the module but other cookie session libraries work (cookie-session). For some reason chrome isn't playing nice. Works just fine with safari though!

//Response Headers:

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 307
ETag: W/"133-a2350808"
Set-Cookie: session=ml7xACUMh29wu0Jg7kgUsQ.g2EE0vg1smZ4eYkguUh_vf-J4wRVI3W2lkypw9hSX99FV33JlIHcRCfFDDh2cpn7gj9DTLwkvRg88xoM2yQmBS9lItYVR2MYd1EiItgyjOzF2hqUss1q_A-w3iRJmKT9MlPMTt25PJoZGLx46jUxGHsMQ9T6DKndMnCLpVpUJj1mfpFgb2uI4u3KN1dohOEz_NLtpscAV-in5d4j4mkxYg.1431146556801.86400000.eAn4DCHAik0lYlAN9vuiHBjsHqo82eyx0mpsbGMkRTE; path=/; expires=Sun, 10 May 2015 04:42:37 GMT; domain=localhost
Date: Sat, 09 May 2015 04:42:36 GMT
Connection: keep-alive

redirect does not call updatecookie()

If using express/connect, calling res.redirect() doesn't trigger a res.writeHead, which is where client-sessions.js intercepts the response pipeline and calls updateCookie().

So, if implementing a logout feature in express the session will never get reset and reflected back on the client:

app.get('/logout', function(req, res, next) {
  req.session.reset()
  res.redirect('/dashboard/');

 // req.session.userId will still be set in subsequent requests
});

So, what's the best practice for clearing out and destroying the session if you intend on redirecting the user to another location?

Workaround

Here's the workaround I used instead of req.session.reset(); which bypasses client-session completely:

var cookies = new Cookies(req, res);
cookies.set('<name of session cookie>');

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.