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)







Reply via email to