Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Password Generation and Module Multiplication

by Orsmo (Beadle)
on Nov 22, 2010 at 18:53 UTC ( [id://873020]=perlmeditation: print w/replies, xml ) Need Help??

I found myself recently writing a script to do user account management tasks in an environment with multiple NIS, LDAP, and Active Directory directories. Along the way, I found myself in need of a way to generate a password that was reasonably secure by fitting the requirements of our password policy. The policy we have is fairly common, since it basically is what Active Directory does when the password complexity rules are turned on.

"Great," I thought to myself. "I can just use a module from CPAN!"

There turned out to be a few, so off I went to install them and give them a try. The first thing I noticed is that they all seemed to work fairly well at doing a good bit more than I really needed. The problem however was that none of them seemed to be configurable to exactly match our password complexity requirements.

"Well, crap! I guess I'll have to write my own."

So off I went to do just that. What astonished me, was just how simple it was to write something that managed it.

use strict; use warnings; use List::Util qw(shuffle); sub gen_password { my $password; my $minlength = 8; my $maxlength = 12; my $minclasses = 3; my $other = rand($maxlength-$minlength) + $minlength - $mincl +asses; my @classes = ( ['a' .. 'z'], ['A' .. 'Z'], [0 .. 9], [qw( . ! / \ - : ; " ' ? $ ^ & @ [ ] = + | < > % * ~ ) + , qw/ ( ) /, ',', '#'], ); my @required = shuffle(0 .. $#classes); my @indexes = shuffle(@required[ map { rand @required } (1 .. +$other) ], @required[0 .. ($minclasses - 1)]); foreach my $i (@indexes) { $password .= @{$classes[$i]}[rand @{$classes[$i]}]; } return $password; }

Now, I'm not saying this is ideal code. It doesn't completely suck though, and it is pretty easily extended to cover a few things I didn't bother with. About the only significant bit I couldn't add unless I were to expend a fair amount of effort would be a bit that checked the password to see if it matched a certain number of consecutive characters of the user's name or userid. (not that such a check would be difficult, just that it would require more than a simple line of code.)

But this left me faced with a new problem. How do I address the fact that my few lines of code represent something none of the other modules do? Do I try to incorporate my code into one of the existing modules and contribute a patch to the module maintainer? Do I flesh mine out into a full module and add to the number of modules out there that meet an individual's needs but don't quite cover the bases for a majority? Do I write a module that's backward compatible with the other modules but includes the solution for my own needs in the hope that I can reduce the number of modules that have multiplied out there so far?

In the end, I've opted to take the lazy way out and post a snippet here, knowing that it will like only very rarely be found by anyone that might need it, but justifying it to myself in that it is such a short snippet that anyone could have come up with it on their own if they needed it.

Does CPAN module multiplication induce apathy and denial in anyone else, or is it just me?

Replies are listed 'Best First'.
Re: Password Generation and Module Multiplication
by merlyn (Sage) on Nov 22, 2010 at 20:59 UTC
    Just an aside... this seems overly complex:
    [qw( . ! / \ - : ; " ' ? $ ^ & @ [ ] = + | < > % * ~ ) , qw/ ( ) /, ', +', '#'],
    why didn't you just write:
    [qw( . ! / \ - : ; " ' ? $ ^ & @ [ ] = + | < > % * ~ , # ( ) )],
    since qw's can nest for those parens, or even something like:
    [map chr, 32..47, 58..63, 123..126]
    to really get all of the "other" characters.

    -- Randal L. Schwartz, Perl hacker

    The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

      Or just use {} as delimiters, as they aren't allowed chars. However, I'd write it as:
      [split //, q{.!/\-:;"'?$^&@[]=+|>?%*~.#()}]
      which doesn't trigger silly warnings because of the use of ',' and '#' inside qw. Besides, qw to get individual characters looks so awkward.

      Fair enough on the complexity of the "other" characters line. It is spelled out with explicit characters specifically to make it easier to add or remove given characters from the list. At the time, I think I had some warning that came up with the nested parens. Unfortunately my memory is a bit vague on that point. Those may simply have due to been a typo at the time. I will say though that with the use of "use strict; use warnings;" I do get at least the following warning:

      Possible attempt to separate words with commas at test line X.

      I'm sure there are more elegant ways to write the list though, including your fine use of map chr.

        At the time, I think I had some warning that came up with the nested parens.
        Which is why, in general, I don't add "use warnings" to the code I write. Sometimes, it finds useful things, but the only time it triggers for me now, it's simply annoying.

        If you find yourself changing the code you write to be more obscure or hard to maintain because warnings are warning you, warnings have failed.

        -- Randal L. Schwartz, Perl hacker

        The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

Re: Password Generation and Module Multiplication
by jwkrahn (Abbot) on Nov 22, 2010 at 22:38 UTC
    $password .= @{$classes[$i]}[rand @{$classes[$i]}];

    That should really be:

    $password .= $classes[$i][rand @{$classes[$i]}];

    You don't really want a list on the right hand side of the assignment.

Re: Password Generation and Module Multiplication
by toolic (Bishop) on Nov 22, 2010 at 21:05 UTC
    Your code is not self-contained. Are you using List::Util::shuffle?
    Do I try to incorporate my code into one of the existing modules and contribute a patch to the module maintainer?
    Yes, especially if you use a similar coding style to that used by the current author and you supply tests to validate your new functionality.
    Do I flesh mine out into a full module and add to the number of modules out there that meet an individual's needs but don't quite cover the bases for a majority?
    This is another good choice if you feel your code is better in some way than existing modules (faster, easier to use, easier to install, etc.). You should explicitly describe in the module's POD what differentiates your module from others. If you go this route, try to select a module name which is similar to other CPAN modules which perform a similar function. This of course requires more of a commitment because you will be responsible for supporting and maintaining the module.
    Do I write a module that's backward compatible with the other modules but includes the solution for my own needs in the hope that I can reduce the number of modules that have multiplied out there so far?
    Not necessarily. How do you think you will be able to reduce the number of CPAN modules?
    Does CPAN module multiplication induce apathy and denial in anyone else, or is it just me?
    The number of CPAN modules has thus far not affected me in that way.
      Sorry, the code was not meant to be self contained, but merely illustrative. I'll add the use statement in the original post so as to make it more clear.
Re: Password Generation and Module Multiplication
by sundialsvc4 (Abbot) on Nov 22, 2010 at 19:30 UTC

    You might have accomplished your purpose by posting this code-block here.   Showing someone who will follow you, what you did and how you did it.   And, soliciting public response.

    If folks ask, then a new CPAN module might be underway.

    But, either way, they will kibbutz kibbitz unmercifully.   ;-)

    (Even when you mispel yiddish ...)

      Minor nit, and off-topic. My apologies...

      A kibbutz is an agrarian commune.

      To kibbitz is to offer unwanted advice.

      The first is Hebrew, the second Yiddish. (Well, this *is* a language forum...)

Re: Password Generation and Module Multiplication
by CountZero (Bishop) on Nov 23, 2010 at 16:45 UTC
    What all these password complexity rules do is to actually reduce the set of possible passwords by several orders of magnitude and thereby make brute force attacks many times easier.

    The only way to avoid users using easy to guess passwords (their user name, or date of birth, or ...) is to not allow them to choose their own password but provide them with a random password they have to use. For real security, you cannot trust the user to come up with a strong password.

    As far as a computer security is concerned "ADAM" is as good a password as "uhulhbjGKVOILHS885AS72JGHS65G33".

    Just by spelling out the complexity rules of the password,you have made it hackers soo much easier. The only good rule as far as security is concerned is "there are no rules, other than 'throw some random characters together'.

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

      Not to mention you can't remember strong passwords so you tend to write them down. I once had to generate a password for some military app that was so bad, it had to be some 20+ characters without any common words, nor could you use common numbers and symbols inplace of letters, so a password like, "P@$$W0rd" wouldn't work. I ended up writing it down somewhere... in my own special code I made up so as to confuse anyone who saw it (mostly inspectors who will write you up if they find passwords written on papers glued to monitors).
        Yes, that is absolutely the downside of strong random passwords: nobody can remember them, therefore writes them up on a piece of paper and then tapes that to his/her monitor!

        As soon as you write something fool-proof, along comes a better fool.

        CountZero

        A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

      Brute force attacks against a single password, granted.

      But without complexity rules, a letter-only brute force attack or rainbow table attack against a list of hashed passwords will too easily pick off the lazy users. I'd assume the complexity rules are really designed to protect against this case.

      --
      A math joke: r = | |csc(θ)|+|sec(θ)|-||csc(θ)|-|sec(θ)|| |
      Online Fortune Cookie Search
      Office Space merchandise

        That, basically, is the case.   The number-one most often used password is:   password.   Close behind them are:   enter, secret.

        Nevertheless, it doesn’t work.   Enforcing complex password-rules simply causes more passwords to be written down.   When a system is broken into, it usually is not from “forcing” a password.   There are too many “other” ways into a complex system.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others wandering the Monastery: (3)
As of 2024-03-29 02:28 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found