ibanix has asked for the wisdom of the Perl Monks concerning the following question:
Hi monks,
I've got the need to generate a psuedo-random 8-character string. This is what I coded up:
my $string;
for (0..7) { $string .= chr( int(rand(25) + 65) ); }
print "$string\n";
This works, but I suspect it isn't the best way to do it. As you can tell, it only returns uppercase letters ("A" .. "Z"). Is there a better way to go about this?
Many thanks,
ibanix
$ echo '$0 & $0 &' > foo; chmod a+x foo; foo;
Re: Random string generator
by Paladin (Vicar) on Feb 06, 2003 at 01:53 UTC
|
my @chars = ("A".."Z", "a".."z");
my $string;
$string .= $chars[rand @chars] for 1..8;
Fill @chars with what ever characters you want to be valid in your string. | [reply] [d/l] [select] |
|
This is really nifty. Paladin++
I have to spell this out; perhaps other monks will appreciate the explination. Please feel free to correct me.
rand @chars takes @chars in scalar context, that is, the length of the array holding our characterset. So rand returns a value somewhere in the array, and that becomes the index for the array lookup.
I keep forgetting you can do cool things like this by implied scalar or array context.
Thanks,
ibanix
$ echo '$0 & $0 &' > foo; chmod a+x foo; foo;
| [reply] [d/l] [select] |
|
$chars[int rand scalar @chars]
which does exactly the same thing as
$chars[rand @chars]
The net effect is that all array indexes have the same change of being chosen. | [reply] [d/l] [select] |
Re: Random string generator
by BrowserUk (Patriarch) on Feb 06, 2003 at 01:55 UTC
|
sub rndStr{ join'', @_[ map{ rand @_ } 1 .. shift ] }
print rndStr 8, 'A'..'Z';
VUXGFLAV
print rndStr 8, 'a'..'z';
jplojhxe
print rndStr 8, 'a'..'z', 0..9;
recv1wym
print rndStr 8, 'a'..'z', 0..9;
434d0wwo
Examine what is said, not who speaks.
The 7th Rule of perl club is -- pearl clubs are easily damaged. Use a diamond club instead. | [reply] [d/l] |
|
I like your function. But I'd call it differently...
print rndStr 12, 'A'..'Z', 0..9, 'a'..'z', '-', '_', '.'; print "\n";
People who don't memorise passwords would think that
punctuation marks would make a password harder to
remember, but they don't. kmJPJ-wLKA is no worse
to remember than QDBLhUCcjH, and provided the
punctuation is included at random (rather than
forced to be there), security is enhanced.
Now, the other poster's alternating-consonant-vowel
passwords are even easier, but those are less secure.
There are ways to fix that up a little, though, if
you're willing to sacrifice uniform length, without
a horrible impact on ease of remembering... I'll
post that separately, though.
--jonadab
| [reply] [d/l] [select] |
|
It's a good point.
Though I have to say that I wasn't responding to a "passwords generator" question, but a random string generator question. I tend to use it for generating test data and the like.
I never used a random password generator, I (like many people) have my methods of arriving at passwords. I won't describe it, but essentially there is a pattern to them. Hopefully, so long as I keep the pattern to myself, it shouldn't compromise me too much.
Examine what is said, not who speaks.
The 7th Rule of perl club is -- pearl clubs are easily damaged. Use a diamond club instead.
| [reply] [d/l] |
|
|
Re: Random string generator
by tachyon (Chancellor) on Feb 06, 2003 at 01:58 UTC
|
You might like to look at
The Sort-of-pronounceable-password generator which is a Perl script that generates easy-to-remember passwords. The following variables can be specified: the number of passwords, the length of the passwords, the dictionary file, the length of the chunks of words that are used to construct the password, and the character to separate the passwords in the output
cheers
tachyon
s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print
| [reply] |
|
Ah, but I'm not generating passwords. Thank you, nonetheless!
$ echo '$0 & $0 &' > foo; chmod a+x foo; foo;
| [reply] [d/l] |
Re: Random string generator
by tachyon (Chancellor) on Feb 06, 2003 at 02:02 UTC
|
Something like this produces pseudo-random vaguely memorable char sequences. For default passwords I just tend to pick one that is easy to remember from a dozen options:
#!/usr/bin/perl;
srand( time() ^ ($$ + ($$ << 15)) );
my @v = qw ( a e i o u y );
my @c = qw ( b c d f g h j k l m n p q r s t v w x z );
for (1..12) {
my ($flip, $str) = (0,'');
$str .= ($flip++ % 2) ? $v[rand(6)] : $c[rand(20)] for 1 .. 9;
$str =~ s/(....)/$1 . int rand(10)/e;
$str = ucfirst $str if rand() > 0.5;
print "$str\n";
}
__DATA__
Fuco1wipon
name5jeweb
Vute2tecis
Paco8podec
wopu3wasig
lune2fosuk
Same2jamek
Qeqy0nebit
rizi5muwuj
Qone7tatat
Daho7cejab
fesi2jideq
This algorithm produces 2*20*6*20*6*10*20*6*20*6*20 or around 82 billion permutations so it is quite feasible to brute force it though quite time consuming. 26**8 is around 208 billion so there is similar (order of magnitude) complexity to a random 8 char same case alphabetic string but far more memorability.
cheers
tachyon
s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print
| [reply] [d/l] |
|
This algorithm produces 2*20*6*20*6*10*20*6*20*6*20 or around 82 billion permutations so it is quite feasible to brute force it though quite time consuming.
If all passwords don't have to be exactly the same
length (just within a range), it's possible to modify
your algorithm slightly, keeping the same principle,
by allowing a vowel to be a dipthong and/or allowing
a consonant to be a blend, at random. This will
throw off the odd-even pattern, while still leaving
something more likely to be pronounceable than an
entirely random string.
srand( time() ^ ($$ + ($$ << 15)) );
my @v = ( 'a', 'e', 'i', 'o', 'u', 'y');
#, 'ai', 'ou', 'oo', 'ee', 'oi');
my @c = ( 'b', 'c', 'd', 'f', 'g', 'h', 'j',
'k', 'l', 'm', 'n', 'p', 'q', 'r',
's', 't', 'v', 'w', 'x', 'z');
$nc = @c; # Number of consonants excluding blends.
# (Some of the blends are not good endings.)
@c = (@c, 'bl', 'br', 'tr', 'st', 'dr',
'th', 'ch', 'sh');
my @d = (0..9, '_', '-', '.');
my $length = 10; # This is a minimum.
my ($flip, $str) = (0,'');
for ( 1 .. ($length - 1) ) {
$flip++;
if ($flip%2) {
if ($flip==$length-1) {
$str .= $c[rand($nc)];
} else {
$str .= $c[rand(@c)];
}
} else {
$str .= $v[rand(@v)];
}
}
my $re;
my $digitpos = rand(5)+3; foreach (1..$digitpos) {
$re .= ".";
}
$str =~ s/($re)/$1 . $d[rand(@d)]/e;
$str = ucfirst $str;
print "$str\n";
Results...
- Vedos5titem
- Rif7yfadrom
- Cajy1dugec
- Kunory7trir
- Zih4ychusyv
- Meb9rishystil
- Brucuce4chyv
- Nob0achuchew
- Buq6ehiqor
- Lybru-bexuk
So the question is, how secure are these passwords
with that modification? Well, if the person doing the
brute-forcing knows exactly how you generate them, or
has seen a good number of them, they're not much better,
because there's not enough variation. Adding in a bunch
of different blends and dipthongs would probably help a
bit. What would help more would be using several different
algorithms and alternating between them at random -- so
that one time you might get one like the above, and another
time you might get "ad-hoc(Variable)17" or
"e.g.{Sputnik}82" and yet another time you
might get "f0rtu1t10us_gr33nh0us3" or
"m4rg1n_bl4sph3my". (Update: those patterns
are not as hard to brute-force as the one above, but
they were intended only as quick examples.)
Any one of those patterns could
be brute-forced, but trying to code a general case that
takes in all of them could be just about as bad as doing
each one of them in turn; with a dozen different such
patterns, that could get prohibitive. If you want to
be sure that they have to do them in turn (rather than
coding a general case of some kind), throw in one
pattern that generates fairly lengthy stuff
like "running-implicit-tomorrow-wet-Howard"
and "chortle-wax-Susan-dromedary-green".
That makes for a very nice pessimal case when the
algorithm trying to break it also has to deal with
the possibilities inherent in the other patterns.
Oh, and make sure your wordlist is large and
undisclosed. (The wordlist can be disclosed if it
is seriously large, e.g., if you scan in the OED.)
Of course, if they can get what they want by
compromising only any one password, then you have to
make sure each and every one of the patterns has a
certain minimum of resistance to brute-forcing. Your
exact threshhold will depend on your circumstances.
--jonadab | [reply] [d/l] [select] |
|
You can do the maths easy enough. Say you have a dictionary of 10**5 words and use dict_word[0..9] as the pattern you get 10**5*10 == 10**6 == not enough permutations that should be memorable. If you use dict_word[0..9]dict_word instead you then get a respectable 10**11 or 100 billion permutations which is the same conmplexity as what was presented. The rationale for \w{4}\d\w{5} was to make two reasonably easy to remember strings separated by an easy to remeber digit. By adding [-_.] to the mix you only up the permutations to 106 billion (81*10**9*13/10) from 81 == small change. If you add the digit in position 5 or 6 randomly you up the permutations from 82 to 164 billion which is still a relatively small change. Using dipthongs instead of single consonants still only adds ((consonants+dipthongs)/consonants))**5 units of complexity.
Extra length is a good way to increase conplexity as each extra alphanumeric adds roughly one order of magnitude of complexity. Exactly one order of magnitude for digits, a little more for alphabetics where the set > 10.
By far the best protection from brute forcing is to protect the pwd database with as little as a 1 second retry timout as 82*10**9 seconds is roughly 2100 years so your attacker should be dead long before they crack a pwd.
Like all security it is simply a matter of how high you want to raise the bar, the idea being for it not to be worth the time expended for the result obtained.
cheers
tachyon
s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print
| [reply] [d/l] [select] |
|
Re: Random string generator
by Mr_Person (Hermit) on Feb 06, 2003 at 02:47 UTC
|
| [reply] |
|
Since people might confuse 'l' and '1' (did you ;-) ?), O and 0, when jotting passwords down, i use the following char set for string::random :
use String::Random;
my $pattern = new String::Random;
my $size = 8;
# don't use i,I,1,l,L,0,O, etc..
$pattern->{'A'} = [ 'A'..'H', 'J', 'K', 'M', 'N', 'P'..'Z', 'a'..'h',
+'j', 'k', 'm', 'n', 'p'..'z', '2'..'9' ];
print $pattern->randpattern('A' x $size);
| [reply] [d/l] |
Re: Random string generator
by Coruscate (Sexton) on Feb 06, 2003 at 02:03 UTC
|
# @chars contains characters to get random string from
my @chars = (a..z, A..Z, 0..9);
my $string = join '', map { @chars[rand @chars] } 1 .. 8;
print $string;
If the above content is missing any vital points or you feel that any of the information is misleading, incorrect or irrelevant, please feel free to downvote the post. At the same time, reply to this node or /msg me to tell me what is wrong with the post, so that I may update the node to the best of my ability. If you do not inform me as to why the post deserved a downvote, your vote does not have any significance and will be disregarded.
| [reply] [d/l] |
Re: Random string generator
by t'mo (Pilgrim) on Feb 06, 2003 at 15:58 UTC
|
perl -le 'print map{(a..z)[rand 26]} 0..rand 10'
which I have extended thusly:
perl -le 'print map{(a..z,A..Z,0..9)[rand 62]} 0..7'
and which you could extend as needed.
| [reply] [d/l] [select] |
|
For use in a function which is much easier to remember.
print randStr(8)."\n";
sub randStr {
return join('', map{('a'..'z','A'..'Z',0..9)[rand 62]} 0..shift);
}
| [reply] [d/l] |
Re: Random string generator
by Anonymous Monk on Feb 06, 2003 at 23:12 UTC
|
Try This (String::Random):
I found this module on CPAN that looks to be quite useful
and simple to use.
#!/usr/bin/perl -w
use strict;
use String::Random;
my $string = new String::Random;
print $string->randregex('\d\d\d\d\d'); # Prints 5 random digits
print "\n"; ##just to separate the 2 output lines
print $string->randpattern("....."); # Prints 5 random printable char
+acters
20030208 Edit by Corion: Removed red, added code tags.
| [reply] [d/l] |
|
|