1

The scope is server-side session management with session payloads being sent to the client in a cookie.

I'm researching session management for web applications. I've been looking at a couple places, and from my understanding is we shouldn't use a secret as a session identifier(index). Because it can lead to timing attacks.

Let's say for the sake of performance sessions on the server-side are stored in cache/memory. And the index is reset(e.g: starts back at 1) every time the server restarts or they are all purged.

session_payload = index || HMAC(server_key, index)

But doing it like that would leave room for replay attacks, right? An attacker could generate a bunch of session payloads and store them for later to hijack sessions. Something is needed to make each session payload unique to prevent that, right?

So what about:

payload = index || nonce
session_payload = payload || HMAC(server_key, payload)

If my understanding is correct, the nonce just needs to be unique to make the session payload unique. Should it be just the output of a CSPRNG, RNG or the current time(milliseconds?, nanoseconds?)? What are the caveats of each?

So if the above is done right, it should be able to avoid:

  • Timing attacks.
  • Volume attacks.
  • Replay attacks.*
  • Tampering.

Right? And is there any other attacks I should be aware of? Please exclude session fixation, that can be mitigated via session payload regeneration on privilege escalation.

  • What I define by a replay attack, is adversaries could store pre-computed session payloads and hijack sessions later, hence the use of the nonce.
Dew
  • 13
  • 4
  • To be clear: defeating timing attacks is pretty easy. Either use a constant-time comparison function, or hash at least the user-facing (and possibly both) values before comparison. – CBHacking Oct 11 '19 at 20:20
  • @CBHacking Yes, but a lot of session management either did or are using secrets as indexes for session identifiers. – Dew Oct 11 '19 at 20:21
  • You're also ignoring some common session token representations, such as JWTs, and anything else that stores the identity and/or access of the user within the token. For those, you don't actually need to store any data server-side at all - indeed, that's the usual reason they're used; it aids load balancing - though revoking/terminating such sessions gets tricky. – CBHacking Oct 11 '19 at 20:25
  • @CBHacking You're right. They are tricky, and the scope of my question is server-side with cookies. I'll edit it to make it clearer. While JWTs are brought up, I don't think they are a good solution for session management, as do other security experts. – Dew Oct 11 '19 at 20:31
  • I guess I'm wondering: what exactly are you trying to do? It looks like you're trying to re-solve a solved problem in ways that make a lot of really bad assumptions, like that the server would ever, under any circumstance, reuse session tokens; any system that would do that should be thrown out entirely, as it was obviously written by somebody with no security mindset at all and has no place in auth code. Just use one of the existing systems that are considered at least acceptably good. If you can identify a flaw (in security, perf, etc.) in one of those, then talk about ways to improve it. – CBHacking Oct 11 '19 at 20:31
  • @CBHacking This is merely for research, not for implementing my own session management library. And the session payload with only the index being used was to get a point across. – Dew Oct 11 '19 at 20:35

1 Answers1

1

If you're storing session identifier tokens server-side, tokens from a cryptographically secure PRNG work fine, assuming you use constant-time checks (either through evaluating the full length every time or via hashing before comparison). If you want to make the lookup into your session collection faster, and you're willing to trade away revealing some info about the number of active sessions in exchange for the performance improvement of using an array lookup instead of either a hashtable or a binary search, you can add an index to the random token... but the index's only job is to be a performance optimization.

HMACing the whole token afterward shouldn't be necessary, since there's neither predictable or time-able data to tamper with, so tampering gets you nothing. Replay is eliminated by just using a decently-long token. Adding an HMAC will (marginally) increase server load for no clear value (it might reduce the already-minimal risk of compromise from an attack on something below the application layer, such as CPU caching, but it's also additional complexity and thus avenues for things to go wrong with minimal expected value).

You say this is for research, not implementation, but it's still not obvious why you'd try going this route. It looks like it started with a clearly terrible idea (use an index as a token), a failed attempt to patch that idea (hmac the index... oh wait, replay), and attempted to patch the bad patch. Security is often improved iteratively, but the basis of this whole approach is such a bad idea it's not worth trying to fix.

Assuming the MAC key is kept safe and that the nonce is stored and checked server-side, this does work. Neither the generation nor the checking of the nonce has many specific requirements so long as the tuple (index, nonce) are never reused; you could (if you didn't mind leaking even more info about how many sessions have ever been created) use a simple counter (long enough to prevent wrap-around) for the nonce. Assuming you were really wedded to this index-as-the-primary-identifier-of-session idea but wanted to stop the info leaks, you could even encrypt the token. At that point, though, your system is deeply over-complex, your token much longer than it needs to be, your server doing more computation on every request than there's a need for, and you've gained approximately nothing over a simple secure-random token + constant-time check.

CBHacking
  • 40,303
  • 3
  • 74
  • 98
  • So are you saying that `index || unpredictable_unique_token` is the best way to go? Index for performant look ups, and then comparing tokens in constant time. – Dew Oct 11 '19 at 21:17
  • Thank you, I understand now. I went the long and more computationally expensive and complex route doing the index, nonce and HMAC which I could've just had an index for an unpredictable unique token. – Dew Oct 11 '19 at 21:23
  • In practice, binary search or hashtable is quite fast enough; the index really isn't needed. You can make binary search immune to timing attacks, too. For example, hashing the token before storing it / doing the lookup still works. – CBHacking Oct 11 '19 at 23:45
  • Oh. I've seen this done before, storing the hashed token. Then doing a look up on a hashed token again. But I've heard there is still possible(but not very practical) timing attacks doing that. – Dew Oct 12 '19 at 06:12
  • Regarding your idea about hashing, have a look [here](https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy). The regular separate incremental index still seems like the more secure solution since there is no easy way to go with HMAC and random key because the hashed token would be the index. – Dew Oct 12 '19 at 06:43