dave_pl has asked for the wisdom of the Perl Monks concerning the following question:

Hi i am seeking some knowlege and Wisdom from the monks:)
I have a program that gets a password from a user and then
stores this crypted password in a flat text file for later
reading now it works great except that it doesn`t pack(?)
the passwords correctly what i meen is UserA enters the
password: test12345 but when i do a bolean check the pasword test12345 matches
but so does the password test1234
and even test123 ??
my $pass = <STDIN>; my $user = <STDIN>; my @salt = ('a'..'z', 'A'..'Z'); my $passc = crypt($pass, @salt[int(rand(@salt))] . @salt[int(rand(@sal +t))]); open my $fh, '>pass.pwd' or die $!; print $fh "$userñ$pass"; close($fh);

then i read it and check it like this:
my $user = <STDIN>; my $pass = <STDIN>; my $bolead = 0; open my $fh, '<pass.pwd' or die $!; while(<$fh>){ my ($one, $two) = split(/\ñ/, $_); if($one eq $user){ $bolead = crypt($pass, $two) eq $two; } } if($bolead == 1){ print "MATCH"; }else{ print "NO MATCH"; }

ANY help would be great thanks!

Replies are listed 'Best First'.
Re: crypt help
by Random_Walk (Prior) on Apr 05, 2005 at 08:27 UTC

    The Crypt docco says:

    Traditionally the result is a string of 13 bytes: two first bytes of the salt, followed by 11 bytes from the set ./0-9A-Za-z, and only the first eight bytes of the encrypted string mattered, but alternative hashing schemes (like MD5), higher level security schemes (like C2), and implementations on non-UNIX platforms may produce different strings.

    So I would expect test1234 to crypt to the same as test12345 but not test123. Here is some test code showing just that behaviour:

    nph>perl -ne'BEGIN{print "Pass: "}; $S="ab"; print crypt($_, $S), "\nP +ass: "' Pass: test abpNYMT5RGuX6 Pass: test1 abW8wbI6pkG9g Pass: test12 abP627jOqenSg Pass: test123 abpwNzBDeyy9Y Pass: test1234 abP/cckPJaUqA Pass: test12345 abP/cckPJaUqA Pass: test123456 abP/cckPJaUqA Pass:
    I used a constant salt just to keep things simple. Are you using Unicode so that 8 bytes is less characters ? I see enyas (sp?) in your code like the one here print $fh "$userñ$pass";

    Cheers,
    R.

    Pereant, qui ante nos nostra dixerunt!
      Random_Walk

      Thanks for the quick reply! i see what you mean with only
      the first byte matters! that explains a lot! who is enyas?
      thanks for the asnwer, is there a way that you know of that
      you can have more than 8 bits?

      Zaxo, Yes sorry about the way i wrote the syntax
      i quickly typed it up since the code i am using
      is in cgi and is really a mess...

      Thank You

        Thats 8 bytes not bits !

        If you want to use more than the first 8 bytes you will have to use another one way hash or cludge a crypto sub that splits your password into 8 byte chunks and crypts each seperately (Bad Idea). Perhaps MD5 hash would be nice, remember to add some salt to make dictionary attacks more expensive and if you store an MD5 you will have to store the salt as well (MD5 does not put it at the fromt like crypt does). Suppose you could use the MD5 of the user name as a salt, concatenate it with the password then store the MD5 of the result.

        Here is your code fixed up a bit and altered to use md5 digest with MD5 of user name used as salt.

        #!/usr/bin/perl use strict; use warnings; use Digest::MD5 ('md5_base64'); sub my_crypt { my ($pass, $user)=@_; my $salt = md5_base64($user); return md5_base64($salt.$pass); } print "user> "; chomp (my $user = <STDIN>); print "pass> "; chomp (my $pass = <STDIN>); my @salt = ('a'..'z', 'A'..'Z'); my $passc = my_crypt($pass, $user); open my $fh, '>>pass.pwd' or die $!; print $fh "$user\t$passc\n"; close($fh); # then i read it and check it like this: print "user> "; chomp ($user = <STDIN>); print "pass> "; chomp ($pass = <STDIN>); open $fh, '<pass.pwd' or die $!; my $matched; while(<$fh>){ next unless /^$user/; ($user, $passc) = split /\s+/, $_; if (my_crypt($pass, $user) eq $passc){ print "Matched\n"; $matched++; last; } } print "No Match\n" unless $matched; __END__ >./passtest user> random pass> test12345678 user> random pass> test12345678 Matched >rm pass.pwd >./passtest user> random pass> test12345678 user> random pass> test1234567 No Match >cat pass.pwd random 6EtC2Kp14XloIR7y4KdOQw

        WARNING

        Crypto is famous for subtle bugs. I give no promise that this algorithm is in any way secure but having said that I can see no obvious problems with it.

        Cheers,
        R.

        Pereant, qui ante nos nostra dixerunt!
Re: crypt help
by Zaxo (Archbishop) on Apr 05, 2005 at 08:34 UTC

    It's not clear to me how you're getting the results you describe. There are several problems with your code, however.

    The principal one is that you never use your crypted $passc. What you're saving is the clear text. The file you save it to should not be opened with '>' mode, but '>>', or, better, '+<' so you can read the file and check for duplicated user names. File locking would also be wise.

    You can call next in the read loop if the $user doesn't match, and last out of the loop once you find the entry for $user and compare crypts. That will let you avoid some unnecessary work.

    chomp before split and after reading from STDIN to remove line ends which will wreck comparisons.

    After Compline,
    Zaxo

Re: crypt help
by TedPride (Priest) on Apr 05, 2005 at 09:24 UTC
    Given a pass that's larger than 8 characters, use the last 8 characters in your crypt instead of the first 8. People are far less likely to get the password "right" when it's still incorrect.

      Interesting idea. From the info per character perspective it may be adding the same security as one more character for the most naive user but can add nothing for a user choosing true random passwords.

      In English prose there are around 1.6 bits of entropy per character. This can be calculated from its compressibility. The best arithmetic compression algorithms (e.g. PPM) will compress a large body of English text down to about 20% of its original size. 8 * 0.2 = 1.6, which is why we frequently see the "1.5 bits per character" entropy number for English.

      For good random pecking at the keyboard there are 95 printable characters, 95 ~= 2 ** 6.75 bit of entropy per key.

      For our user password we have between 12.2 and 52.5 bits of entropy. Using the last 8 characters for an English word then a small amount of entropy is added, being the number of bit needed to encode the difference between password length and 8. If the users evenly distribute password lengths between 8 and 15 we have to add the entropy needed to store a value between 0 and 7 so 3 more bits of entropy free to the naive user. The completely random password is unaffected keeping 52.5 bits.

      Few users will use passwords much longer than 8 characters so if we take all password lengths and compress that list I think we will find the entropy provided by the length data to be less than 1.6 bits per user, so about the same as making the step to 9 character english language passwords.

      You can force users to use some numbers and some punctuation, you reduce the total password space but you do probably up the average users entropy.

      See further: the entropy of english

      Cheers,
      R.

      Pereant, qui ante nos nostra dixerunt!