bronto has asked for the wisdom of the Perl Monks concerning the following question:
I'd like to sum up what has been discussed on my question about basic checks on password; What follows could be further discussed and improved and then, if you like, go to code snippets or categorized q/a.
First of all, the question was:
What I need is to do just a minimal check, in particular:
- password is at least 5 characters long;
- password contains alphabetic characters and digits
- password isn't a repetition of a few characters or patterns (e.g.: "pippo12": too many p's; "cacca11": only c's and a's, and widely repeated; "32ratatata": the pattern "ta" appears 3 times and covers 60% of the password...)
While the first two points are really easy to code:
. . .
I'm not sure how to manage the third point in a clear and efficient manner . . .
An old ugly programming language, one could say :-)
After reading monks' comments I went back to my Essential System Administration book (2nd edition, unfortunately :-). At page 210, speaking about Password Triviality Checks, it broadly says that the passwd command (she's speaking about UNIX passwords) may reject passwords that are:
Speaking about the SCO(TM) UNIX'goodpw program, she then shows some examples of passwords that would be rejected for the user chavez:
There have been interesting considerations about repeated characters or sequences. I share the opinion expressed by an Anonymous Monk, even if agree that 'ppppp' isn't more difficult to crack than 'abcde' or 'qwerty'.
Now, the requirement in the original question was to have something clear and efficient; in particular, waiting for more that a second to have the password check is not acceptable.
I ran john the ripper against my password, with a dictionary of nearly 187000 words it took 44 seconds: too much; we have to drop some requirements to have a real basic check:
Therefore, here is what we'll consider "a minimal check for passwords":
Personally? This is my first attempt:
#!/usr/bin/perl # # call this file passcheck.pl in order to test it with the # test script use strict ; use warnings ; sub passcheck { my ($username,$password,$name,$surname,$city) = @_ ; my ($minlen,$maxlen,$maxfreq) = (5,8,.5) ; my $plen = length $password ; my $pclean = lc $password ; $pclean =~ s/[^a-z]//g ; my %prots = map {$_,qr/$_/} ($pclean,leftrotations($pclean)) ; # Check length { return "password is too short" if $plen < $minlen ; return "password is too long" if $plen > $maxlen ; } # Check repetitions { my @chars = split //,$password ; my %unique ; foreach my $char (@chars) { $unique{$char}++ } ; while (my ($char,$count) = each %unique) { return "Too many repetions of char $char" if $count/$plen > $maxfreq ; } } # Check password against username, name, surname and city # All but username could be composed, like "Alan Louis", or "Di Cioc +cio" # or "Los Angeles", so we have to treat each chunk separately. { my %chunks = map { ($_,qr/$_/) } split(/\s+/,lc(join(" ",$name,$surname,$city))) ; # Add username to %chunks # You can compact the code below in one line, but why? :-) { my $lcuser = lc $username ; $chunks{$lcuser} = qr/$lcuser/ ; } foreach my $chunk (keys %chunks) { foreach my $rot (keys %prots) { return "password matches personal data after some left rotatio +n" if $rot =~ $chunks{$chunk} or $chunk =~ $prots{$rot} ; } } } # Left rotations of the password don't match it { foreach my $rot (leftrotations($password)) { return "Password matches itself after some left rotation" if $rot eq $password ; } } # Password contains alphas, digits and non-alpha-digits { local $_ = $password ; return "Password must contain alphanumeric characters, digits and +symbols" unless /[a-z]/i and /\d/ and /[^a-z0-9]/i ; } return "password ok" ; } sub leftrotations { my $string = shift ; my $n = length $string ; my @result ; # note: $i < $n, since the n-th permutation is the password again for (my $i = 1 ; $i < $n ; $i++) { $string =~ s/^(.)(.*)$/$2$1/ ; push @result,$string ; } return @result ; } 1 ;
I tested it with this small test script:
#!/usr/bin/perl use Test::More qw(no_plan) ; my ($username,@userinfo) = qw(bronto Marco Marongiu Capoterra) ; my $good = 'c0m&c@z%' ; my @passwords = qw(shrt waytoolong manyyyyy nto12bro% comar1$ marmaron poterr@1 t1c&t1c& pitbull) ; my $ok = "password ok" ; require './passcheck.pl' ; is($ok,passcheck($username,$good,@userinfo),"$good is good") ; isnt($ok,passcheck($username,$_,@userinfo),"$_ is bad") foreach @passw +ords ;
Would you like to improve those subs with me?
Ciao!
--bronto
The very nature of Perl to be like natural language--inconsistant and full of dwim and special cases--makes it impossible to know it all without simply memorizing the documentation (which is not complete or totally correct anyway).
--John M. Dlugosz
edited: Tue Jul 29 14:01:03 2003 by jeffa - readmore tag
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Minimal password checking: a summary
by particle (Vicar) on Jul 29, 2003 at 13:04 UTC | |
|
Re: Minimal password checking: a summary
by kutsu (Priest) on Jul 29, 2003 at 14:33 UTC | |
by bronto (Priest) on Jul 29, 2003 at 14:58 UTC | |
|
Re: Minimal password checking: a summary
by dbwiz (Curate) on Jul 29, 2003 at 13:18 UTC | |
by bronto (Priest) on Jul 30, 2003 at 08:57 UTC | |
by dbwiz (Curate) on Jul 30, 2003 at 13:07 UTC | |
by bronto (Priest) on Aug 08, 2003 at 13:41 UTC | |
by larsen (Parson) on Aug 08, 2003 at 13:45 UTC | |
|
Re: Minimal password checking: a summary
by duelafn (Parson) on Jul 29, 2003 at 20:17 UTC | |
by bronto (Priest) on Jul 30, 2003 at 08:59 UTC |