On 16.05.2016 20:03, Bruce Johnson wrote:
On May 16, 2016, at 10:15 AM, André Warnier (tomcat) <a...@ice-sa.com> wrote:
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).
The function is meant to map a random element from the 62-element-long array
(0..9,"a".."z","A".."Z”) (hence a rand() call to generate a number from 0 and
62), 32 times, and join them into a string.
Although I think that should really be rand(9+26*2) to properly generate array
indices for the entire array and no more. With a number between 0 and 62 (63
numbers) and a 62-element array, you’ll be retrieving nulls from the array 1/62
calls, but all that means is that the string is one char shorter for each time
'62’ comes up...
So long as rand is properly seeded, you should not get repeats, at least not
frequently enough to ever notice, I’d think.
This is textbook Perl, as in I’m pretty sure it’s out of one of Larry Wall’s
books; I use it to generate random strings for cookies.
Maybe the textbook example was meant to show the power of the syntax, but not necessarily
the efficiency of it.
I interpret this as doing the following (if we ignore any optimisation which the perl
compiler may make by itself) :
quote perldoc
map EXPR,LIST
Evaluates the BLOCK or EXPR for each element of LIST (locally setting $_ to each element)
and returns the list value composed of the results of each such evaluation. In scalar
context, returns the total number of elements so generated. Evaluates BLOCK or EXPR in
list context, so each element of LIST may produce zero, one, or more elements in the
returned value.
unquote
LIST in this case is 1..32 = 32 elements.
EXPR is (0..9,"a".."z","A".."Z") which is another list (say LIST-2), of 62
elements.
Thus, we are 32 times evaluating (building) a list LIST-2 of 62 elements.
(Always the same one, but does perl know this and optimise it away ?)
Out of which LIST-2 then (at each of the 32 iterations) we extract just 1 "random"
element, by virtue of the [rand(62)] index into the list LIST-2.
I am not sure immediately of the effect of the leading +(LIST-2), but it may have the
effect of forcing some internal conversion of the elements of (LIST-2).
At the end of the 32 iterations of
map +(0..9,"a".."z","A".."Z")[rand(10+26*2)], 1..32
we have an array of 32 single-character elements.
Which we then join into a 32-character string by means of the join().
I may be wrong, but at least intuitively, this does not seem to be an optimal way to
obtain a 32-char long random key.
Mathematically, I think such a string provides for 62 ** 32 combinations, which
is ..
2,0859248397665137523388883849312 e+93
according to my pocket calculator.
That may be more than the total number of atoms in the known Universe.
Which I have also intuitively a tendency to consider a bit excessive, for generating a
simple time-limited session key for a webapp.
Note also that the fact that there are theoretically that many possible combinations, does
not mean that all of them are equally likely.
For that to be the case, rand() would have to return a series of really random responses,
which we know it doesn't.
If it’s properly seeded in the original code, it should either work or not work
on all five servers. Not working on one out of the five makes me think maybe
there’s some sort of weird caching issue.
I agree, that the seed is the main issue.
The code should insure that for each server process (+ perl interpreter), srand() is
called once, whence that process starts (or the first time the webapp is run), and with a
different seed for each process.
(A BEGIN block with a seed based on the time (hi-res) may be a solution.)
But the fact that it does not evidently work on one of the servers only, may be due to
many factors, such as the rate at which already-keyed and not-already-keyed requests are
hitting each server, their order, how many server processes there are to respond to the
requests, and how long it takes to respond to one request.
So it may also be that "it does not work" on all servers, but that it has only been so far
noticed on the one.
If all server processes on one server happen to use the same seed, then it
depends
on which server process handles the next unkeyed request. If it happens to be, for that
server process, the n-th unkeyed request that it handles (and other server processes have
already handled more than n requests before), then the key generated by this server
process will duplicate one which has already been issued before, no matter how many
different keys are theoretically possible.
And whether this causes problems or not (or gets noticed), may only depend on whether the
session which previously used the same key, has already concluded or not.