Skip to content

Use Crypt::SysRandom to generate session ids #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

robrwo
Copy link

@robrwo robrwo commented Jun 25, 2025

This replaces convoluted code for generating session ids with a simple call to retrieve random bytes from the system source of randomness.

Besides simplifying the code, this also improves security by using high quality randomness to generate a sessionid rather than a hash of a lot of data, much of which is predictable.

It uses Crypt::SysRandom, which is a lighter-weight alternative to Crypt::URandom, though either is fine.

This replaces convoluted code for generating session ids with a simple
call to retrieve random bytes from the system source of randomness.
@robrwo robrwo force-pushed the rrwo/improve-sessionid-generation branch from 7b8c9ab to 6a259ce Compare June 25, 2025 17:52

return join( "", ++$counter, time, rand, $$, {}, overload::StrVal($c), );
}
sub session_hash_seed { }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A subclass could be using this, so it should still return something useful.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would be useful? An empty string? Some random data?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, we remove the method, document it as such in the changes. Any subclass that relies on this value will need to be updated.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd probably not be in favor of removing a method from this, at this point this module is very hard to change given the size of the deployment footprint.

Copy link
Member

@haarg haarg Jun 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Providing a reasonable seed seems like a good idea to me:

sub session_hash_seed { Crypt::SysRandom::random_bytes(80) }

Or whatever length seems appropriate. The old string was near 80, although it was of course providing much less entropy.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

32 bytes (256 bits) is plenty.

@robrwo
Copy link
Author

robrwo commented Jun 26, 2025

Perhaps instead this should just use UUID to generate identifiers?

@jjn1056
Copy link
Member

jjn1056 commented Jun 26, 2025

I think we need to be careful to change the dependencies of this 20+ year old module that is used in nearly every Catalyst application I've consulted on.

I think the main thing we are trying to say here is the old code could be a security problem. If that is completely true, like there are known exploits, that's good reason to change.

Crypt::SysRandom seems to have a pretty ok compatibility profile, but it's also fairly new. I don't personally know enough about this security topic to be sure it's an actual improvement.

I'd not be excited to change this unless we are sure of all the above.

As a total aside, I've never been happy with the design of the session plugin as it gloms too much into the application class directly. If the existing one is problematic, could be a good excuse to rethink.

@robrwo
Copy link
Author

robrwo commented Jun 26, 2025

I think we need to be careful to change the dependencies of this 20+ year old module that is used in nearly every Catalyst application I've consulted on.

In your consulting, have you ever run across users that this change might break?

I imagine most customisations would be to use some variant of /dev/random for the seed or generated id anyway.

I think the main thing we are trying to say here is the old code could be a security problem. If that is completely true, like there are known exploits, that's good reason to change.

This is a potential security problem. Session ids are often used to grant access to resources. (Note that securing sessions from cookie stealing attacks or forgery is another issue.)

What seemed secure in 2005 is now practical to attack with today's hardware. Digest algorithms like MD5 and SHA are from the early 1990s and not considered secure, and there are data used to seed the hash is not very secret.

Crypt::SysRandom seems to have a pretty ok compatibility profile, but it's also fairly new. I don't personally know enough about this security topic to be sure it's an actual improvement.

The "standard" is Crypt::URandom. That's been around for a long time. But I think Crypt::SysRandom is lighter weight, with XS handled as a separate module that users can install. We can use either though.

Unless someone is running Catalyst on a very old system without /dev/urandom (before mid-1990s), I don't see it as being a problem.

One alternative is to

  • modify the session_hash_seed method to return data from one of the randomness modules,
  • add an option to use the session_hash_seed as the id, and
  • modify the generator to check the option, and use the hash seed if true, otherwise default to use a simple hash of the random data (I think in this case MD5 is good enough, and it's already used by Object::Signature anyway, but we can also make the hash configurable.

If users are customising the session_hash_seed, then it should work.

If they are customising the generate_id to use session_hash_seed, then again it should work.

Otherwise they can update their config to just use the random data from the seed.

As a total aside, I've never been happy with the design of the session plugin as it gloms too much into the application class directly. If the existing one is problematic, could be a good excuse to rethink.

I remember being told in 2013 when I was submitting patches to improve this that it was "deprecated" and that I should consider moving to "Plack::Middleware::Session". I have been tempted to write a wrapper that just uses the Plack middleware.

The session_hash_seed method returns raw random byes from the random
source.  The number of bytes can be configured using the
"hash_seed_size" option.

The generate_session_id method will return a hex string of those raw
bytes if the "use_raw_hash_seed" option is true.  Othersie (default), it
will return a SHA-1 hash of the session_hash_seed.

The algorithm can be changed using the "digest" option.
@robrwo
Copy link
Author

robrwo commented Jun 26, 2025

I have made some changes in d10be86 that I think will be safer for backwards compatability:

  • session_hash_seed returns data from the random device
  • generate_session_id will return a SHA-1 hash of that data, unless configured to return the raw data as a hex string
  • the hash digest algorithm can be configured
  • the size of the hash data can be configured

@robrwo robrwo requested review from haarg and jjn1056 June 26, 2025 17:40
@robrwo
Copy link
Author

robrwo commented Jul 2, 2025

It might be worth updating the documentation to say that the session_hash_seed method is deprecated and that a future release may not use it at all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants