Dallaylaen has asked for the wisdom of the Perl Monks concerning the following question:

So here we are again. After examining several posts about session generation, including Dancer's, Catalyst's and PHP's, I decided to roll my own.

As I understood, there are generally 3 requirements:

Now the following code snippet, as I believe, satisfies these criteria:

=head2 get_session_id( [$user_salt] ) Generate a new, shiny, unique, unpredictable session id. Id is base64-encoded. The default is using two rounds of md5 with time, process id, hostname +, and random salt. Should be unique and reasonably hard to guess. If argument is given, it's also added to the mix. Set $MVC::Neaf::X::Session::Hash to other function (e.g. Digest::SHA:: +sha224) if md5 is not secure enough. Set $MVC::Neaf::X::Session::Host to something unique if you know bette +r. Default is hostname. Set $MVC::Neaf::X::Session::Truncate to the desired length (e.g. if length constraint in database). Default (0) means return however many chars are generated by hash+base +64. =cut use Digest::MD5; use Time::HiRes qw(gettimeofday); use Sys::Hostname qw(hostname); use MIME::Base64 qw(encode_base64); # Premature optimisation at its best. # Should be more or less secure and unique though. my $max = 2*1024*1024*1024; my $count = 0; my $old_rand = 0; my $old_mix = ''; our $Host = hostname() || ''; our $Hash = \&Digest::MD5::md5_base64; our $Truncate; sub get_session_id { my ($self, $salt) = @_; $count = $max unless $count--; my $rand = int ( rand() * $max ); my ($time, $ms) = gettimeofday(); $salt = '' unless defined $salt; # using old entropy means attacker will have to guess ALL previous + sessions $old_mix = $Hash->(pack "LaaaaLLLLaL" , $rand, $old_mix, "#" , $Host, '#', $$, $time, $ms, $count , $salt, $old_rand); # salt before second round of hashing # public data (session_id) should NOT be used for generation $old_rand = int (rand() * $max ); my $ret = encode_base64( $Hash->( pack "aL", $old_mix, $old_rand ) + ); $ret = substr( $ret, 0, $Truncate ) if $Truncate and $Truncate < length $ret; return $ret; }; # finally, bootstrap the session generator at startap get_session_id();

But I may well be missing something. Am I?

Replies are listed 'Best First'.
Re: Session id generation with Perl once more
by Anonymous Monk on Oct 06, 2016 at 22:49 UTC

    So here we are again. After examining several posts about session generation, including Dancer's, Catalyst's and PHP's, I decided to roll my own. As I understood, there are generally 3 requirements: ... But I may well be missing something. Am I?

    Well, you're rolling your own, thats rule 1, don't roll your own, unless you know what you're doing ...

    That you're not comparing to the existing session id generators, no benchmarks or randomness testing , no algorithm reference ...

    it hints that you don't know what you're doing

    but then I don't really know much to say one way or another ... except i've seen some hints ... Re^4: Debugging cgi-bin script

      thats rule 1, don't roll your own

      Which is why I'm posting it here.

      no benchmarks

      You've got me on this, here's a benchmark. My proposed function is available here. (The repo is outdated, will need to check out devel branch).

      #!/usr/bin/env perl use strict; use warnings; use Benchmark qw(cmpthese); use Dancer::Session::Abstract; use Apache::Session::Generate::MD5; use Catalyst::Plugin::Session; use MVC::Neaf::X::Session; $SIG{__DIE__} = \&Carp::confess; my $cat = Catalyst::Plugin::Session->new; cmpthese( -1, { dancer => \&Dancer::Session::Abstract::build_id, apache => \&Apache::Session::Generate::MD5::generate, catalyst => sub { $cat->generate_session_id }, neaf => \&MVC::Neaf::X::Session::get_session_id, });

      Results of its execution on a 0.8 GHz intel laptop:

      bash$ perl -Ilib bench.pl Rate dancer catalyst apache neaf dancer 4886/s -- -62% -92% -93% catalyst 12922/s 164% -- -79% -81% apache 61265/s 1154% 374% -- -12% neaf 69591/s 1324% 439% 14% --

      no randomness testing

      Agreed, will search for a reference for doing that.

Re: Session id generation with Perl once more
by coicles (Sexton) on Oct 12, 2016 at 00:15 UTC

    I noticed some possible issues with this code

    Only the first character of strings will take part in your entropy pool:

    $old_mix = $Hash->(pack "LaaaaLLLLaL" , $rand, $old_mix, "#" , $Host, '#', $$, $time, $ms, $count , $salt, $old_rand);

    The "a" format specifier alone will only copy over the first character of the source string. To copy over the whole source string, use "a*".

    my $ret = encode_base64( $Hash->( pack "aL", $old_mix, $old_rand ) );

    Note that this "pack" also suffers the same problem, so only the first character from $old_mix gets used for hashing to a result. This entropy loss is made even more stunning given:

    our $Hash = \&Digest::MD5::md5_base64;

    So $old_mix is the result of a base64 encoding, thus its first character retains only a fraction of a byte of entropy.

    Also, $ret is being assigned a doubly-base64-encoded hashed value, so it will be longer than necessary, and its entropy density will be less than expected. This problem is benign from a security standpoint, unless $Truncate gets used:

    $ret = substr( $ret, 0, $Truncate )

    So truncation will cause a greater than expected loss of entropy in the result.

    As for using MD5, in this context I don't think its weaknesses are a liability -- it is being used for mixing a secret key, so even if an attacker can concoct a collision for this secret, this pseudo-secret key will be useless. However, I would personally choose a newer hashing algorithm, due to unjustified paranoia.

      unjustified paranoia

      With crypto I think paranoia is the right attitude and justified. The attacker might have tactics unknown to the dev.

      Thanks for pointing out my mistakes. Should've checked what I'm actually hashing...

      The fixed version is slower than Apache's now.

Re: Session id generation with Perl once more
by Anonymous Monk on Oct 06, 2016 at 17:17 UTC
    Yes, how will the session be stored? If it's a database, let it choose the ID.

      Database-chosen IDs (that is, sequences/integer primary key not null) are highly predictable and thus shouldn't be passed to the outside world.