Skip to content

Manually set sessionID#159

Closed
JasperH8g wants to merge 4 commits into
expressjs:masterfrom
JasperH8g:master
Closed

Manually set sessionID#159
JasperH8g wants to merge 4 commits into
expressjs:masterfrom
JasperH8g:master

Conversation

@JasperH8g

Copy link
Copy Markdown

This will fix #158 by making it possible to manually set the sessionID. This is most helpful in environments where cookies are unavailable or unreliable. Tests are missing at the moment, but I can do that after someone gives this a sanity-check (because I'm not very experienced in express-middlewares).

var express = require('express');
var session = require('express-session');
var app = express();

app
  .use(session({
    secret: 'secret',
    httpOnly: false,
    resave: false,
    saveUninitialized: true,
    sessionid: function(req) {
      return req.query.sessionID;
    }
  }))
  .get('/session', function(req, res) {
    req.session.count = req.session.count + 1 || 0;
    req.session.save();
    res.send('count: ' + req.session.count);
  })
  .listen(3000, function() {
    console.log('Listening...');
  });

@dougwilson

Copy link
Copy Markdown
Contributor

So I'm not yet on a computer to make as much feedback as I wat, but my main feedback is I think it should work where if you provide a custom ID reader (so it's not reading from a cookie), then this module shouldn't be trying to set a cookie still (perhaps require the user to provide a custom setting mechanism). Thoughts anyone?

@JasperH8g

Copy link
Copy Markdown
Author

That makes sense, but I'd like to leave that for someone who's a bit more familiar to this module, if you don't mind :)

@dougwilson dougwilson added the pr label May 17, 2015
@gabeio

gabeio commented May 27, 2015

Copy link
Copy Markdown
Contributor

this sounds similar to #40 ...

@shaunwarman

Copy link
Copy Markdown

I think it would be nice to at least look to see if req.sessionID exists instead of overriding with getCookies() call. This way you can establish your own middleware to handle your use case. For us, we are sending a custom header that middleware with high priority will look for and set req.sessionID based on the incoming value. That way it's still in a header and not in the open.

See use case: #185

@xelaz

This comment has been minimized.

@joepie91

joepie91 commented Sep 10, 2016

Copy link
Copy Markdown

What environments are you envisioning? It's a really, really bad idea to put session IDs into the URL in browser environments.

@olalonde

Copy link
Copy Markdown

Could be useful with JSON web token auth.

@hugovart

Copy link
Copy Markdown

@joepie91 why it's a really really bad idea? Aport from how it appears in the address bar, how is undesirable? Do you think it is insecure? what if you are just using it for ajax requests without revealing to the end user directly?

@joepie91

Copy link
Copy Markdown

@olalonde: Could be useful with JSON web token auth.

You should not be using JWT for sessions, at all.

@hugovart: why it's a really really bad idea? Aport from how it appears in the address bar, how is undesirable? Do you think it is insecure? what if you are just using it for ajax requests without revealing to the end user directly?

Because the assumption of HTTP-implementing software and devices is that URLs are not sensitive information to the parties involved. Your URL including session ID will persist in:

  1. Browser history
  2. Proxy logs
  3. Server access logs
  4. Any kind of URL that the user copypastes from their address bar or from an on-page link
  5. ... and so on.

Not to mention that on-site scripts, even if third-party, will be able to read out the URL and extract the session ID from it, thus allowing cookie/session stealing when XSS occurs. A similar problem is described here, under "Storing Sessions".

There's a very good reason why sessions require cookies, and that reason is security. Any attempt to implement this without cookies is virtually guaranteed to result in security issues. The right solution here is to tell your users to enable cookies for your domain (and not give them a reason to keep them disabled, such as tracking).

And yes, AJAX requests are prone to all of the above problems as well, with the exception of point 4 and maybe point 1.

@olalonde

Copy link
Copy Markdown

@joepie91 I read your article and 1) disagree with it (e.g. cookies can be simulated with JWT) 2) some libraries like passport-twitter leave you no choice but to have a req.session.

@weepy

weepy commented Sep 11, 2016

Copy link
Copy Markdown

I'm using an embedded webkit in an iOS app. The security rules mean I can't use cookies properly cross-domain so I'm forced to send the sid as a query param. It's all https so it's not insecure.

@joepie91

Copy link
Copy Markdown

@olalonde: disagree with it (e.g. cookies can be simulated with JWT)

No, they cannot. Not securely. If you believe they can, then feel free to explain exactly how.

some libraries like passport-twitter leave you no choice but to have a req.session.

What does this have to do with the article I linked?

@weepy: I'm using an embedded webkit in an iOS app. The security rules mean I can't use cookies properly cross-domain so I'm forced to send the sid as a query param.

No, you're not. You can just exchange a token issued by server A for a session on server B using a specific endpoint for that purpose, removing the need for cross-domain cookies.

It's all https so it's not insecure.

HTTPS is completely unrelated to all of the problems I've described. It's insecure, HTTPS or not.

@weepy

weepy commented Sep 12, 2016

Copy link
Copy Markdown

@joepie91 How can I auth subsequent requests without cookies or a query param ?

@weepy

weepy commented Sep 12, 2016

Copy link
Copy Markdown

@joepie91 FYI embedded webkit has a nasty unresolved bug that randomly deletes cross domain cookies.

@joepie91

Copy link
Copy Markdown

How can I auth subsequent requests without cookies or a query param ?

It wouldn't be without cookies, the cookies would just be set by the domain itself.

Example flow:

  1. Log in at http://server-a.com/login
  2. server-a.com gives you a session
  3. You request http://server-a.com/token and get back a JWT
  4. You POST the token to http://server-b.com/obtain-session
  5. http://server-b.com/obtain-session validates the token as originating from server-a.com and gives you a session
  6. You now have a session on server-b.com that can be used to make authenticated requests.

The sessions on server A and B (and their corresponding cookies) are separate, and the JWT is just used for the 'one-time claim' from server A to server B that you've really authenticated.

FYI embedded webkit has a nasty resolved bug that randomly deletes cross domain cookies.

Without knowing more about the specific bug, I can only really say one thing: demand that the vendor fixes it. This is their problem, and it's not up to you to fix it, especially when it introduces security issues.

@weepy

weepy commented Sep 12, 2016

Copy link
Copy Markdown

No - it's my problem - I can't demand anything of Apple - I have to release my software soon and can't wait for them. Sometimes we need to use work arounds.

@olalonde

olalonde commented Sep 12, 2016

Copy link
Copy Markdown

@joepie91 I think you are misunderstanding what this PR is about.

From your article:

A lot of people mistakenly try to compare "cookies vs. JWT". This comparison makes no sense at all, and it's comparing apples to oranges - cookies are a storage mechanism, whereas JWT tokens are cryptographically signed tokens.

They aren't opposites - rather, they can be used either together or independently. The correct comparisons are "sessions vs. JWT" and "cookies vs. Local Storage".

This PR isn't about replacing sessions with JWT. The goal here is to use JWT, some other http header or even a query field to transmit and retrieve session identifiers. Cookies are not the only HTTP authentication mechanism.

@joepie91

joepie91 commented Sep 12, 2016

Copy link
Copy Markdown

@weepy: No - it's my problem - I can't demand anything of Apple - I have to release my software soon and can't wait for them. Sometimes we need to use work arounds.

That is a terrible attitude. Yes, you can demand this of Apple. It's their software, potentially even their Developer Program license you're purchasing if you're an organization, and thus their problem to solve. If you do not hold them accountable for that, then you cannot expect them to fix it.

And as I've suggested before, "workarounds" with security implications are not okay. And even if you did need a workaround, why are you not implementing it yourself? Now you're holding the express-session team responsible for Apple's failures.

@olalonde: This PR isn't about replacing sessions with JWT. The goal here is to use JWT, some other http header or even a query field to transmit and retrieve session identifiers. Cookies are not the only HTTP authentication mechanism.

I am well aware. My point is that "JWT + session IDs" isn't a sensible or useful combination at all (and it severely misunderstands what JWTs are in the first place), and all the other ways of "transmitting and retrieving session identifiers" that I have seen have security implications in most environments, and are most likely completely unnecessary to begin with.

The examples mentioned so far - query parameters and headers - both fall into the "insecure and unnecessary" category. If you feel that there is a way that doesn't suffer from the same issues, then by all means share it - but until I see such a case, I'm sticking with my stance that this PR is unnecessary and dangerous to introduce.

@weepy

weepy commented Sep 12, 2016

Copy link
Copy Markdown

I wasn't really expecting you to change your stance @joepie91 ;-)

@joepie91

Copy link
Copy Markdown

You should perhaps wonder about why that is.

@olalonde

olalonde commented Sep 12, 2016

Copy link
Copy Markdown

You should perhaps wonder about why that is.

Seems like cargo culting to me.

The examples mentioned so far - query parameters and headers - both fall into the "insecure and unnecessary" category

No, lol. Cookies are an HTTP header. So are OAuth bearer tokens. Or HTTP basic auth. Query params used in AJAX requests do not show up in the URL bar or browser history. They might show up in server logs but that can easily be disabled.

The problem with express/session is that it does both authentication and session storage. We just want to decouple those two so that req.session can be used with other authentication mechanisms.

If you have strong opinion on HTTP headers or query params used for authentication, perhaps go raise your concerns on https://github.com/auth0/express-jwt/issues, https://github.com/jaredhanson/passport-http-bearer/issues or https://github.com/jshttp/basic-auth/issues.

@joepie91

joepie91 commented Sep 12, 2016

Copy link
Copy Markdown

Seems like cargo culting to me.

Cargo culting? What I consider "cargo culting" is blindly trying to apply JWT everywhere because it's supposedly "better than sessions" without even understanding what it does or what role it plays. I've provided several clear arguments by now, yet you've as of yet failed to address my request to demonstrate "simulating cookies securely with JWT".

No, lol. Cookies are an HTTP header. So are OAuth bearer tokens. Or HTTP basic auth.

Except they are not even remotely similar in how they are handled on the client-side. Cookies with httpOnly are stored in a client-side cookie store that cannot be accessed from on-page JS, something that is completely technically impossible with JS and custom headers. No, cookies are not just headers - the Cookie header is only a small part of the mechanism.

Query params used in AJAX requests do not show up in the URL bar or browser history. They might show up in server logs but that can easily be disabled.

Except,

  1. No, it can't be "easily disabled", not without disabling all access logs, which is something that people are generally unwilling to do, and
  2. This completely ignores the problem of malicious client-side JS that can intercept AJAX requests and steal the session ID even if it cannot directly access it.

The problem with express/session is that it does both authentication and session storage. We just want to decouple those two so that req.session can be used with other authentication mechanisms.

And as I've tried to explain numerous times by now, this is a terrible idea that should not be supported. It's not my problem that you don't seem to understand cookies or the security guarantees that they provide - I've attempted numerous times to explain it here and in the article, and frankly I'm starting to lose my patience.

If you insist on trying to find ways to 'replace cookies' despite this being impossible to do securely, that is your problem. But perhaps instead of constantly trying to argue against it, you could try asking questions to understand how cookies do solve this problem more securely.

If you have strong opinion on HTTP headers or query params used for authentication, perhaps go raise your concerns on https://github.com/auth0/express-jwt/issues, https://github.com/jaredhanson/passport-http-bearer/issues or https://github.com/jshttp/basic-auth/issues.

I regularly raise these concerns in many places, as time allows. What does that have to do with this thread or the arguments I am making?

@olalonde

olalonde commented Sep 12, 2016

Copy link
Copy Markdown

apply JWT everywhere because it's supposedly "better than sessions"

You keep showing you don't understand what this is about. I'm not trying to replace sessions with JWT or else I wouldn't need expressjs/session. I'm just using JWT for authentication which is an accepted practice and none of your arguments against it matter.

Also, you keep assuming that my clients are browsers, they are not.

Finally, I know very well the security implications of using/not using cookies in a browser environment, I do not need your help with that.

PS: Waiting for Apple to change their API is not an acceptable answer for anyone who needs to ship software.

PS: If your website is vulnerable to XSS, you are fucked whether you use httpOnly cookies or localStorage. Both situations allow you to send arbitrary HTTP requests as the logged in user, no more no less (who cares whether you can see the session id or not).

@joepie91

Copy link
Copy Markdown

I'm not trying to replace sessions with JWT or else I wouldn't need expressjs/session. I'm just using JWT for authentication which is an accepted practice and none of your arguments against it matter.

What, specifically, are you using JWT for? "Authentication" isn't specific enough.

Also, you keep assuming that my clients are browsers, they are not.

No, I don't. I'm not arguing against your use of query parameters or headers for session IDs, I'm arguing against its implementation in express-session because it is going to lead to a ton of broken implementations.

Finally, I know very well the security implications of using/not using cookies in a browser environment, I do not need your help with that.

Your claims (such as "cookies can be simulated with JWT", which you still haven't backed up) suggest otherwise.

PS: Waiting for Apple to change their API is not an acceptable answer for anyone who needs to ship software.

I don't see why not, if they are your vendor. And if they refuse to fix things to the point that you cannot implement something securely (which, for the record, I still strongly doubt), then perhaps you need to take further steps, like publicizing that fact and/or switching to another vendor.

Bugs aren't going to magically get fixed, and this kind of "well I'll just work around it, whatever" will just make things worse for everybody involved.

PS: If your website is vulnerable to XSS, you are fucked whether you use httpOnly cookies or localStorage. Both situations allow you to send arbitrary HTTP requests as the logged in user, no more no less (who cares whether you can see the session id or not).

That is completely wrong. Yes, you can send arbitrary HTTP requests if you have XSS, but you can only do it noisily (shows up in Developer Tools and extensions that track HTTP requests) and for as long as the tab is open.

The ability to steal session cookies is much, much more severe, because it allows an attacker to use the session persistently, and from any system, without you ever knowing about it.

@rdfedor

rdfedor commented Oct 16, 2016

Copy link
Copy Markdown

A session id is no more secure in a cookie vs any other place once the request leaves the computer. If a system is compromised through any sort of XSS, the cookie store is just as vulnerable as any other place in the browser unless httpOnly is set.

@olalonde 's argument of decoupling the authentication with the session store management makes complete sense to me because it adds versatility to the system. A session id can be just as easily manipulated in the cookie store through the use of browser addons or via a man-in-the-middle attack.

The problem that cookies introduce though for me is that they make all requests uncachable through load-balancers like Nginx or Varnish whereas if the session id is passed via the URL, it creates uniqueness in the URL which allows requests to be cached while retaining the session state. It's in essence security through obscurity as it would be difficult for anyone to guess a valid session id (AKA client id) and couple that w/ an access token, you have the beginnings of an OAuth implementation.

There are valid uses for such an implementation other than just purely in a browser setting and just because it allows a system to possibly be compromised is not a valid enough reason for me to not allow this, not that I have any say on it being merged but just my 2 cents. Then again, when passing session ids by url such as that, much of the code becomes useless in my opinion.

@joepie91

Copy link
Copy Markdown

@Nemesis9765: A session id is no more secure in a cookie vs any other place once the request leaves the computer. If a system is compromised through any sort of XSS, the cookie store is just as vulnerable as any other place in the browser unless httpOnly is set.

Which is precisely why you set httpOnly. What are you arguing here?

@olalonde 's argument of decoupling the authentication with the session store management makes complete sense to me because it adds versatility to the system. A session id can be just as easily manipulated in the cookie store through the use of browser addons

No, it cannot. That's what httpOnly is for.

or via a man-in-the-middle attack.

This is out of scope, not related to the issue at hand, and is already solved through TLS. Transport != storage.

The problem that cookies introduce though for me is that they make all requests uncachable through load-balancers like Nginx or Varnish

No, they don't. There's no reason why caching systems cannot take into account a specific cookie when determining how/what to cache - and as far as I am aware, this is precisely what eg. Varnish already does.

it creates uniqueness in the URL which allows requests to be cached while retaining the session state.

No such technical requirement exists.

It's in essence security through obscurity as it would be difficult for anyone to guess a valid session id (AKA client id)

No, it isn't. Security through obscurity explicitly does not apply to keys - of course, those keys still need to have sufficient entropy to be secure.

and couple that w/ an access token, you have the beginnings of an OAuth implementation.

I don't see what that has to do with anything. OAuth is meant to be used for a different set of problems than sessions are.

There are valid uses for such an implementation other than just purely in a browser setting

Valid uses? Perhaps, in theory. But every time somebody claims to have such a valid usecase, they conveniently forget to include a rationale as to why it is supposedly a better solution than session cookies, instead coming up with an endless stream of non-reasons like "you can't use cookies on mobile" (this is false), "you can't use cookies outside of a browser" (this is also false), and so on.

And realistically, even when you're building a stateless API that isn't browser-focused, why would you even be using any kind of session-like mechanism at all? It's already in the name - stateless - that you wouldn't use such a mechanism, and would instead just send along API credentials for each individual request. Meaning that no integration with express-session is required.

I'm really getting quite tired of the endless discussions with people trying to argue that some kind of non-cookie-based approach is needed, when they don't even fully understand how cookies work, and just have some vague notion of them being "bad". If you're going to claim that something is broken or doesn't work for you, then at the very least make the effort of understanding how it works and providing a rationale as to why it doesn't work for you.

@simllll

simllll commented Oct 21, 2016

Copy link
Copy Markdown

Any good news on that? I'm looking for something like this as well. And my current workaround
req.signedCookies[config.session.name] = req.headers['x-session-token'];
seems to have other issues with "saveUninitialized: false" option.

Well, I've seen the last few comments on this it and it seems people believe that cookies and sessions are somehow married these days? my point of view to this:

  • cookies are as insecure as any other approach. and by insecure I mean the user is able to modify it.
  • sessions are sessions, cookies are cookies. two seperat things, which really helps to have a seamless user experience in browsers, but are not the best option on other implementations (e.g. API calls or browser less clients). I agree that also cookies could be used on browser less implementations, but why would you insist on using a technology that is built for web browsers for clients that are simply not web browsers?
  • Caching and related stuff should not be part of this thread, don't uns "unqiue" session urls to prevent caching. it's just a wrong approach. there are e.g. cache-control headers for this.
  • as there are thousands of implementations out there who use different ways to handle sessions (url params, own http headers,...) this PR gives the needed flexibility for all of us. I agree that it could be wrongly used, but yeah.. a lot of stuff in javascript can be used wrong - that's the reason why there are developers deciding how to design things and not computer bots ;) so therefore, please don't follow the "American approach" of preventing in advance any free decisions and let's follow the European way of let people decide how to save a simple thing like a session id.

thanks 👍

@joepie91

joepie91 commented Oct 22, 2016

Copy link
Copy Markdown

cookies are as insecure as any other approach. and by insecure I mean the user is able to modify it.

No, they are not. "The user is able to modify it" is only one (very narrow) threat model of many, and it is not the threat model that is being discussed here. The threat model here is concerning on-page scripts, and cookies are absolutely more secure in that situation.

"Cookies are as insecure as any other approach" is patently false.

sessions are sessions, cookies are cookies. two seperat things, which really helps to have a seamless user experience in browsers, but are not the best option on other implementations (e.g. API calls or browser less clients).

While they technically are separate, storing the session identifier in a cookie is the most widely support and, in browsers, most secure approach. It's usually the only one that makes sense.

I agree that also cookies could be used on browser less implementations, but why would you insist on using a technology that is built for web browsers for clients that are simply not web browsers?

Cookies were not "built for web browsers". They are specified as a generic state mechanism for HTTP in RFC 6265. I'm insisting on using them in non-browsers as well because it prevents a giant footgun in how people deal with sessions, because there's no reason not to, and because if you're using sessions your requests are inherently stateful and so cookies are a good solution.

If you want to build a stateless API, you shouldn't be using any stateful mechanism at all. Not session cookies, not JWT tokens, not session IDs transmitted through some other channel, etc. Just accept an API keypair.

as there are thousands of implementations out there who use different ways to handle sessions (url params, own http headers,...) this PR gives the needed flexibility for all of us.

Thousands of implementations? Hardly. There's the widely-understood-to-be-dangerous old-PHP approach of passing the session ID as a URL parameter, and then there's cookies. Using "own HTTP headers" makes absolutely no sense - cookies are already transmitted as HTTP headers, so why would you change it to a different header that nothing understands? Just use cookies.

I agree that it could be wrongly used, but yeah.. a lot of stuff in javascript can be used wrong - that's the reason why there are developers deciding how to design things and not computer bots ;) so therefore, please don't follow the "American approach" of preventing in advance any free decisions and let's follow the European way of let people decide how to save a simple thing like a session id.

Aside from the nonsensical political jab (and I'm saying that as a European, to be clear): you are free to implement things in whatever way you wish. However, it is not reasonable to expect express-session to support usage that at best replicates behaviour that's already there, and at worst adds vulnerabilities into the mix. I still have yet to see a strong argument in favour of merging something like this.

@weepy

This comment has been minimized.

@joewagner

Copy link
Copy Markdown
Member

I'd would be great if one of the repo owners could weigh in at this point.

My vote is to make this a version 2 feature that lets the application developer choose a module that handles the way the session ID is passed back forth from client to server, e.g. if the session ID is in a header this middleware doesn't need to set/parse cookies.

I'm not sure who's in charge any more ?

@dougwilson volunteers a huge amount of time, and probably has the strongest understanding of what this modules best interests are.

There's quite a few other issues concerning this feature https://github.com/expressjs/session/search?q=sessionID&state=open&type=Issues&utf8=%E2%9C%93

@dougwilson

Copy link
Copy Markdown
Contributor

I will find some time to express my thoughts, because there is a lot going on here. Personally, I think the main contention here is that people don't quite understand the separate of the session store and the session management, and that is made worse by the way this module is written. I think that the underlying ask, which is to take a session ID and load it (already possible--simply access the store object passed into store option directly), and then get that to become req.session with the automatic management (not possible), has merit, but these PRs are, in the end, just simply hacks to make this work without accepting that the entire module simply needs to be redesigned to properly decouple the concerns. I have not quite had the time to work on this publicly, but have some drafts I would like to make public soon.

@dougwilson

This comment has been minimized.

@kenneth-gray

This comment has been minimized.

@JasperH8g

Copy link
Copy Markdown
Author

let's close this because of inactivity

@JasperH8g JasperH8g closed this Jan 3, 2018
@maximelebastard

This comment has been minimized.

@expressjs expressjs deleted a comment from weepy Nov 8, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Can't set manual sessionID