wamp-proto icon indicating copy to clipboard operation
wamp-proto copied to clipboard

Security considerations for Session ID?

Open ecorm opened this issue 2 years ago • 14 comments

Are there any security considerations that need to be documented for the WAMP session ID? Does its random generator need to be cryptographically secure? Does it matter if a session ID is leaked, for example, via router logs?

I checked the Session Resumption draft in PR #264, and it fortunately does not derive the resume-token from the session ID.

I was naively using SHA-256 to anonymize session IDs in the logs of my work-in-progress router. But I realize now that this is futile in the face of rigs that can run billions of SHA-256 hashes per second.

I'm trying to mimic NGINX's access log, except that it logs summaries of messages sent to/from clients (not a full trace). The message payloads are stripped out, but I'd like to keep the options/details dictionaries that accompany many of the message types, in order to better troubleshoot problems. If session IDs cannot be leaked due to security, then I'll have to sanitize those options dictionaries to remove/anonymize the session IDs.

ecorm avatar Feb 07 '23 06:02 ecorm

Session IDs might as well be cryptographically-random because those generators are fast now. (Common advice from cryptographers -- which I am not -- is "just use /dev/urandom" still, I believe).

It also should be fine to use SHA256 still for obscuring logs -- you could also use an HMAC construction so that you retain a key (then, someone would need both your logs and the private key for the HMAC). Although SHA256 hashing is certainly a well-optimized problem (thanks to bitcoin etc) it's still "hard" to reverse it (especially when the target is itself a random string).

(Note that it's been too long since I looked in depth at all the uses of Session-IDs to know if there are other considerations around leaking them -- hope this helps!).

meejah avatar Feb 07 '23 07:02 meejah

Session IDs might as well be cryptographically-random because those generators are fast now. (Common advice from cryptographers -- which I am not -- is "just use /dev/urandom" still, I believe).

I'm trying not to introduce dependencies on crypto libraries while remaining OS-agnostic. But your comment gave me the idea to allow the user to supply their own random number generator function.

You could also use an HMAC construction so that you retain a key (then, someone would need both your logs and the private key for the HMAC)

I suppose I could allow the user to supply their own obscuring function, and default to simple SHA256 if none is provided.

Although SHA256 hashing is certainly a well-optimized problem (thanks to bitcoin etc) it's still "hard" to reverse it (especially when the target is itself a random string).

But in this case, the session ID is a [1, 2^53] integer, where the average time to crack at 1 gigahashes/sec would be 52 days.

ecorm avatar Feb 07 '23 08:02 ecorm

Does its random generator need to be cryptographically secure?

not strictly I believe. eg WAMP-SCRAM does not use session ID as a nonce, but a "proper" random value via os.urandom

https://github.com/crossbario/crossbar-examples/blob/master/authentication/scram/static/client.py#L34

Session IDs might as well be cryptographically-random because those generators are fast now. (Common advice from cryptographers -- which I am not -- is "just use /dev/urandom" still, I believe).

yes, I agree!

thus, we could improve the spec text

https://wamp-proto.org/wamp_bp_latest_ietf.html#name-ids

Sessions (global scope) IDs in the global scope MUST be drawn randomly from a uniform distribution over the complete range [1, 2^53]

with

IDs in the global scope drawn randomly SHOULD use a cryptographically strong generator (eg /dev/urandom), but WAMP authentication methods MUST NOT rely on global IDs being cryptographically strong


Note that it's been too long since I looked in depth at all the uses of Session-IDs to know if there are other considerations around leaking them

yeah, same here. thus we could make it crystal clear:

IDs drawn randomly MUST NOT be used anywhere in WAMP relied to be drawn cryptographically strong .. terrible/wrong english/grammer I guess, but should be clear what's meant;)


we could add text to clarify those things in a new subsection "Security Considerations" within 2.1.2. IDs

oberstet avatar Feb 07 '23 10:02 oberstet

But in this case, the session ID is a [1, 2^53] integer, where the average time to crack at 1 gigahashes/sec would be 52 days.

Yes, true, you could go the other way around and simple hash every possible Session-ID with SHA256 (basically making a "rainbow table").

With an HMAC this isn't true (because you don't hash just the Session ID) or salt (which was invented to defeat rainbow tables, IIRC).

meejah avatar Feb 07 '23 15:02 meejah

What I'd like to know is what an attacker could possibly do with a leaked session ID. If there's no possible harm, then I would prefer to leave session IDs in plain text in the logs.

I guess no one is going proclaim there's no harm in leaking session IDs, only to be proven wrong later. :grin:

One scenario is where a bad actor somehow gains access to portions of the meta API and can then cause mischief if they know the session IDs of active sessions.

Another scenario might be with Subscriber White/Black-Listing where an attacker adds a subscriber that they were not supposed to know about.

Another scenario might be an app relying on session IDs being kept secret. The attack vectors could be directed towards the app and not the router directly.

ecorm avatar Feb 07 '23 18:02 ecorm

I guess no one is going proclaim there's no harm in leaking session IDs, only to be proven wrong later. grin

Yeah, definitely "caution" usually prevails in security discussions ;)

It's hard to imagine any attacks after the session is over, but there could indeed be various tricks to play if you get hold of a valid, ongoing session-id .. especially if you've also got valid access to the router somehow .. so "when" they leak is likely important.

If you care about anonymity / privacy, this could be even trickier to analyze: an attacker in this sense may merely wish to confirm the existence of a particular session (e.g. perhaps the attacker has seized one endpoint device, and wishes to understand to whom it was talking).

(And yeah, I like the thinking in your last example: have to consider the applications too, not just raw WAMP + router).

meejah avatar Feb 07 '23 18:02 meejah

I've decided to blank out session IDs completely in the logs by default, and the user can supply their own obfuscator function to scramble the session IDs in the logs if they wish.

thus, we could improve the spec text

We should also consider including a summary of which message options that ~leak~ expose the session ID, as well as authentication information.

ecorm avatar Feb 07 '23 18:02 ecorm

We should also consider including a summary of which message options that ~leak~ expose the session ID, as well as authentication information.

  • HELLO.Details: authid, authextra
  • WELCOME.Details: authid, authrole, authextra
  • PUBLISH.Options: exclude, exclude_authid, exclude_authrole, eligible, eligible_authid, eligible_authrole
  • EVENT.Details: publisher_id, publisher_authid, publisher_authrole
  • INVOCATION.Details: caller_id, caller_authid, caller_authrole
  • AUTHENTICATE.Extra: everything
  • CHALLENGE.Extra: everything

Also (per obserstet below):

  • ERROR.Details: forward_for
  • PUBLISH.Options: forward_for
  • PUBLISHED.Options: forward_for
  • SUBSCRIBE.Options: forward_for
  • UNSUBSCRIBE.Options: forward_for (note: Options dict appears to be non-standard extension)
  • EVENT.Details: forward_for
  • CALL.Options: forward_for
  • CANCEL.Options: forward_for
  • RESULT.Details: forward_for
  • REGISTER.Options: forward_for
  • UNREGISTER.Options: forward_for (note: Options dict appears to be non-standard extension)
  • INVOCATION.Details: forward_for
  • INTERRUPT.Options: forward_for
  • YIELD.Options: callee, callee_authid, callee_authrole, forward_for

ecorm avatar Feb 07 '23 23:02 ecorm

those messages contain authid etc, and if you deliberately print those messages to an attacker's screen, that's a problem, but not a leak in the traditional sense.

also, there are more messages that contain authid, eg Yield, and also, forward_for contains authids of the originator as well as all intermediary router nodes:

(cpy311_5) oberstet@intel-nuci7:~/scm/crossbario/autobahn-python$ python
Python 3.11.1 (main, Jan 15 2023, 12:28:34) [GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from autobahn.wamp.serializer import Serializer
>>> for klass in Serializer.MESSAGE_TYPE_MAP.values():
...     print(klass, klass.__slots__)
... 
<class 'autobahn.wamp.message.Hello'> ('realm', 'roles', 'authmethods', 'authid', 'authrole', 'authextra', 'resumable', 'resume_session', 'resume_token')
<class 'autobahn.wamp.message.Welcome'> ('session', 'roles', 'realm', 'authid', 'authrole', 'authmethod', 'authprovider', 'authextra', 'resumed', 'resumable', 'resume_token', 'custom')
<class 'autobahn.wamp.message.Abort'> ('reason', 'message')
<class 'autobahn.wamp.message.Challenge'> ('method', 'extra')
<class 'autobahn.wamp.message.Authenticate'> ('signature', 'extra')
<class 'autobahn.wamp.message.Goodbye'> ('reason', 'message', 'resumable')
<class 'autobahn.wamp.message.Error'> ('request_type', 'request', 'error', 'args', 'kwargs', 'payload', 'enc_algo', 'enc_key', 'enc_serializer', 'callee', 'callee_authid', 'callee_authrole', 'forward_for')
<class 'autobahn.wamp.message.Publish'> ('_request', '_topic', '_args', '_kwargs', '_payload', '_enc_algo', '_enc_serializer', '_enc_key', '_acknowledge', '_exclude_me', '_exclude', '_exclude_authid', '_exclude_authrole', '_eligible', '_eligible_authid', '_eligible_authrole', '_retain', '_transaction_hash', '_forward_for')
<class 'autobahn.wamp.message.Published'> ('request', 'publication')
<class 'autobahn.wamp.message.Subscribe'> ('request', 'topic', 'match', 'get_retained', 'forward_for')
<class 'autobahn.wamp.message.Subscribed'> ('request', 'subscription')
<class 'autobahn.wamp.message.Unsubscribe'> ('request', 'subscription', 'forward_for')
<class 'autobahn.wamp.message.Unsubscribed'> ('request', 'subscription', 'reason')
<class 'autobahn.wamp.message.Event'> ('_subscription', '_publication', '_args', '_kwargs', '_payload', '_enc_algo', '_enc_serializer', '_enc_key', '_publisher', '_publisher_authid', '_publisher_authrole', '_topic', '_retained', '_transaction_hash', '_x_acknowledged_delivery', '_forward_for')
<class 'autobahn.wamp.message.EventReceived'> ('publication',)
<class 'autobahn.wamp.message.Call'> ('request', 'procedure', 'args', 'kwargs', 'payload', 'timeout', 'receive_progress', 'transaction_hash', 'enc_algo', 'enc_key', 'enc_serializer', 'caller', 'caller_authid', 'caller_authrole', 'forward_for')
<class 'autobahn.wamp.message.Cancel'> ('request', 'mode', 'forward_for')
<class 'autobahn.wamp.message.Result'> ('request', 'args', 'kwargs', 'payload', 'progress', 'enc_algo', 'enc_key', 'enc_serializer', 'callee', 'callee_authid', 'callee_authrole', 'forward_for')
<class 'autobahn.wamp.message.Register'> ('request', 'procedure', 'match', 'invoke', 'concurrency', 'force_reregister', 'forward_for')
<class 'autobahn.wamp.message.Registered'> ('request', 'registration')
<class 'autobahn.wamp.message.Unregister'> ('request', 'registration', 'forward_for')
<class 'autobahn.wamp.message.Unregistered'> ('request', 'registration', 'reason')
<class 'autobahn.wamp.message.Invocation'> ('request', 'registration', 'args', 'kwargs', 'payload', 'timeout', 'receive_progress', 'caller', 'caller_authid', 'caller_authrole', 'procedure', 'transaction_hash', 'enc_algo', 'enc_key', 'enc_serializer', 'forward_for')
<class 'autobahn.wamp.message.Interrupt'> ('request', 'mode', 'reason', 'forward_for')
<class 'autobahn.wamp.message.Yield'> ('request', 'args', 'kwargs', 'payload', 'progress', 'enc_algo', 'enc_key', 'enc_serializer', 'callee', 'callee_authid', 'callee_authrole', 'forward_for')
>>> 

oberstet avatar Feb 07 '23 23:02 oberstet

those messages contain authid etc, and if you deliberately print those messages to an attacker's screen, that's a problem, but not a leak in the traditional sense.

Yes, sorry, I meant "expose", not "leak". If I don't filter them out of my logs, then they become leaks.

What is the WAMP feature that requires callee, callee_authid, callee_authrole in the options? Same question for forward_for. Is it router-to-router links?

ecorm avatar Feb 08 '23 00:02 ecorm

Yield.callee is for callee disclosure ... mmmh:

https://github.com/crossbario/crossbar/blob/706b2311bb4fa532ada81a357a888528ed493209/crossbar/router/dealer.py#L1254

disclosing the callee can make sense in at least these situations:

  • shared and sharded registrations
  • r2r links

forward_for is for router-to-router links, yes

oberstet avatar Feb 08 '23 00:02 oberstet

Yield.callee is for callee disclosure

I forgot about that one; it's not in the spec. Note to self: Callee Disclosure is discussed in issue 57.

ecorm avatar Feb 08 '23 00:02 ecorm

I take it enc_key should be kept out of logs as well, even if it's (presumably) a public key?

ecorm avatar Feb 08 '23 00:02 ecorm

yes, approximately. enc_key is only identifying the data encryption key used

https://github.com/crossbario/autobahn-python/blob/49c41440e16528ee6ab267df0f9989fb1ed81f7c/autobahn/wamp/message.py#L1478

not the data encryption key itself, which is transported via asymmetric encryption against the public key of the other peer

https://github.com/crossbario/autobahn-python/blob/master/autobahn/xbr/_seller.py#L125

oberstet avatar Feb 08 '23 01:02 oberstet