On 16.05.2016 16:38, Vincent Veyron wrote:
Hi,
I use the code shown here :
http://pastebin.com/6YL9FWGX
in a mod_perl handler.
At line 57, $args->{_token_id} ||= ... generates a random token that is used to
uniquely identify the request (users may reload the URL in two different windows),
and added to the request's arguments.
Out of five different servers, the code works fine on four machines, and a
different token is generated every time the page is loaded or re-loaded. On one
server however, a previous token is being re-used. The servers are not
identical, but all run the same up-to-date Debian Jessie, using the same
distribution packages of Apache and mod_perl
Below is what is printed in the error log of the faulty system by Data::Dumper;
as you can see, on the third iteration, the first token is reused.
Before : $VAR1 = {
'id_entry' => '16007',
'mois' => 0,
'open_journal' => 'Fournisseurs'
};
After : $VAR1 = {
'_token_id' => 'Gh63Y2J9YYaNuReIsI8JAEj9oCY39oiy',
<------------------------------------- token 1
'id_entry' => '16007',
'mois' => 0,
'open_journal' => 'Fournisseurs'
};
Before : $VAR1 = {
'id_entry' => '16007',
'open_journal' => 'Fournisseurs',
'mois' => 0
};
After : $VAR1 = {
'open_journal' => 'Fournisseurs',
'mois' => 0,
'id_entry' => '16007',
'_token_id' => 'EHL2mm1LvmFIgPeYEFYO8dKt71lWiwkP'
<------------------------------------- token 2
};
Before : $VAR1 = {
'mois' => 0,
'open_journal' => 'Fournisseurs',
'id_entry' => '16257'
};
After : $VAR1 = {
'open_journal' => 'Fournisseurs',
'mois' => 0,
'id_entry' => '16257',
'_token_id' => 'Gh63Y2J9YYaNuReIsI8JAEj9oCY39oiy'
<------------------------------------- token 1 again
};
What can I do to find the cause of this misbehaviour?
Hi.
I have not really analysed the instruction by which you create the token, to check if
there was not some flaw there which would lead to generate a less-than-random token.
But I have a suspicion and a theory nevertheless :
1) What is the Apache MPM used on the 5 machines (prefork, worker, etc.) ?
You can find out by entering "apache2ctl -l" on each machine.
2) the rand() function, when called repeatedly, provides a *pseudo*-random series of
return values.
Example : run this little program, *repeatedly*, on any one of the servers :
#!/usr/bin/perl
# test_rand.pl : simple test for rand()
use strict;
use warnings;
srand(12345);
for my $it (1..10) {
print "it # $it : " . sprintf("%05d",int(rand(99999))) . "\n";
}
3) what does this show ?
That when you start with the same "seed" (srand(12345)), even in totally independent
processes running one after the other, you obtain exactly the same sequence of
pseudo-random numbers when you then call rand() repeatedly.
4) you are running Apache + mod_perl. If this is a "prefork" configuration, it means that
there will be a "main Apache process" (which actually receives the browser requests) , and
a number of Apache "children" started in parallel, each with its own persistent perl
interpreter. When a request comes into the main Apache, it looks in some table, to find a
"child" process which isn't currently doing anything, and if found, it passes the request
to that one for processing.
This is largely unpredictable : depending on how many requests come in over a period of
time, and the number of started Apache children processes, and the time it takes to
process one request, one or the other child process can be free or not, to handle the
latest request.
It is thus possible that the same child process will handle a series of requests in a row,
or that each child will be called in turn, or whatever combination.
(And also, according to how I understand your application, Apache will receive a
succession of requests, some with already a token, and some not. So rand() will be called
or not in any child, depending on how requests exactly come in.
5) now look at the description of rand() and srand() here :
http://perldoc.perl.org/functions/rand.html
It says that the first time rand() is called by any process, if srand() has not been
called before, it is called automatically wihout argument, to return some seed for the
first rand() call.
I do not know exactly what srand() returns in such a case, but if it happened in each of
these "Apache children processes + perl", to return initially the same seed, then each of
these Apache processes, subsequently, would return the same series of successive "random"
numbers when rand() is called.
So now put all of that together, and I believe that it is almost guaranteed that you will
regularly get the same keys, on all your servers, not just the one you cite as different.
Also, if I may say so, the function that you are using to generate your "random" unique
session-id :
join "", map +(0..9,"a".."z","A".."Z")[rand(10+26*2)], 1..32 ;
looks at first sight to me like quite inefficient and probably likely to generate the same
string regularly, even if it does not look that way.
(The only variable there is rand(), and it can only return values between 0 and
62).
I haven't done the math, but it just looks like an awful lot of function calls, int/string
conversions, array contruction and retrievals etc., to get a 32-char string that is
finally not so random.
Would it not be better to call rand() only a few times, with a much larger argument, so
that it returns a much wider range of values, and then map these values to letters for
example using a hex sprintf, and then concatenate these hex strings ?
(Or better, use one of the CPAN modules specially designed to do similar things,
efficiently and really randomly)