|
Random and Recursive Crypting using Salt on Unix and Win32
You are here: irt.org | Articles | CGI & Perl | Random and Recursive Crypting using Salt on Unix and Win32
Published on: Monday 31st May 1999 By: Ha Quach
What for Encryption?
You don't need to know anything about encryption to use some of the
scrambling algorithms out there to generate messy passwords. And when
do you need messy passwords? When you want to protect your content, be
it a piece of email attachment, a Web directory, or just stuff you pass
out to your special friends. OK, since this article is for the Perl/CGI
section, I assume you're reading this because you want to know how
Perl's built-in crypt() function can help you protect Web pages or
otherwise authenticate users before letting them go after your goodies.
Ever seen this?
Well, you can do the same for your own site. Some time soon in another
article, I'll talk about protected directories under the Apache Web
server. But let's not get ahead of ourselves. Before you jump into
Apache, you need to know how to generate the encrypted passwords in the
first place (sorry, Apache doesn't do it *automagically* for you). And
I'll show you just that if you're running a UNIX system or a Win32
system so that no matter what port of Perl or Apache you have, by the
time I start talking about Apache (please remind me to write that
article), you'll be wearing the white hat and ready to ride the bronco.
Perl's Built-in crypt() Function
Perl's crypt() works similar to UNIX crypt(). To answer you question:
No, there is no decrypt() function, although if you were trying to break
it by brute force, you shouldn't be reading this article anyway. To
answer your next question: Since crypt() is a one-way function, you
create encrypted passwords (or any string) AND check that string against
a user's plaintext password by running both strings through your
crypting routine, which you'll roll your own. If the user ever loses
his/her password, run a new password through your crypting routine and
voila!, a new mess of mumbled text. You, the Web guru, will never know
the user's password. Isn't that what security is all about? Now, on with
the rolling!
Perl's crypt() takes two arguments, the unencrypted string and the salt.
The final encrypted string is the first two characters of the salt,
which is the key for the system to figure out the rest of the mumble
jumble after the salt. That's all you need to know about the function.
So, if you have a salt like 'salt', the substring 'sa' is the first two
characters. Here's our username and salt in the literal:
# prints sa./eocij6UyY
print crypt('username','salt');
|
And here's something slightly more real-world:
# prints toFP3Ud0a1vGA
print crypt('john','tomorrow');
|
Random Salts for UNIX
Of course, the whole idea behind the salt is to make it random enough so
that two users named 'john' would not result in the same encrypted
string Same salt + same string = same encrypted string. Since you can't
make the password random, your best bet for a random encrypted string is
a random salt. Also, you want a random salt for better security so that
not even you, the maintainer, can duplicate the key off the top of your
head.
This routine gets us a random salt and, by effect, a more random
encrypted string:
sub encrypt_password
{
my $unencrypted_string = shift @_;
my @salt_chars = ('a'..'z','A'..'Z','0'..'9');
my $salt = $salt_chars[rand(63)] . $salt_chars[rand(63)];
return crypt($unencrypted_string, $salt);
}
|
Here's how you talk to the &encrypt_password() routine:
my $encrypted_password = &encrypt_password($unencrypted_password);
|
Let's look at each line of the routine.
my $unencrypted_string = shift @_;
|
takes the $unecrypted_password you passed into the routine and assigns
it to $unencrypted_string.
my @salt_chars = ('a'..'z','A'..'Z',0..9);
|
is an array of characters from lowercase a through z, uppercase A
through Z, and the integers zero through nine. The array has 62 members,
from which we will pick two members to generate the first and second
characters of our salt. Of course, you can find more creative ways to
generate your array, such as using HTML colors in hexadecimal, dates,
the moves of a classic Fischer-Spasky chess game, the notes to your
favorite song from your favorite band, anything that can be translated
into characters.
The next line
my $salt = $salt_chars[rand(63)] . $salt_chars[rand(63)];
|
uses Perl's rand() function to get a random fractional number (something
with a decimal in it). The behavior of rand() is in your Perlfunc
documentation. Even though the index of an array is an integer and your
rand() returns a fraction, Perl is smart enough to automatically--er,
invisibly--treat your fraction as an integer. This makes sense because
C treats a float like an integer if you're trying to pass it into an
integer context, but I digress.
my @stuff = qw(cat dog moose mouse goose);
print $stuff[3.1415927]; # prints mouse
|
works as if it were
print $stuff[int(3.1415927)]; # same as $stuff[3];
|
If you don't know the number of members in your array, use:
my $salt = $salt_chars[rand(scalar @salt_chars)] . $salt_chars[rand(scalar @salt_chars)];
|
The whole idea is to make sure that our index number is inclusively
within the total number of members in our array, so you want to control
the return value of rand() a bit.
Finally, the &encrypt_string routine returns what you want it to with
the line
return crypt($unencrypted_string, $salt);
|
which takes for granted that Perl will evaluate crypt() before doing the
return.
Recursive crypt() for Win32
On a Win32 system, you can't use a random salt. You'll never be
able to repeat your &encrypt_string process to authenticate a user
because Win32 doesn't know what to do with a salt (it kinda loses it
somewhere that we don't know about). A workaround is to recursively
encrypt a static salt and use that as the new salt for a modified
version of your &encrypt_string routine. This is slightly better
obfiscation than the static salt string itself, say 'tomorrow', but it'll
have to do.
sub encrypt_string_on_Win32
{
my $unencrypted_string = shift @_;
my $unencrypted_salt = 'tomorrow';
my $first_crypt = crypt($unencrypted_string,$unencrypted_salt);
my $encrypted_salt = substr($first_crypt,3,1).substr($first_crypt,7,1);
my $encrypted_string = crypt($unencrypted_string,$encrypted_salt);
return crypt($encrypted_string,$encrypted_salt);
}
|
Here, we take the static $unencyrypted_salt 'tomorrow' and apply
crypt() to it to get our first-time-around $first_crypt. Then we
take the third and seventh characters of $first_crypt as our new
salt called $encrypted_salt and concatenate the two with a dot.
Choosing the third and seventh characters is rather arbitrary. On the
second application of crypt(), we used the $encrypted_salt to get the
$encrypted_string and return the whole thing with a third application
of crypt().
That's just one way of obfiscating things. You could get really extreme
and call &encrypt_string_on_Win32 many, many times like so:
my $password = 'john';
for(1..13) {
$password = &encrypt_string_on_Win32($password);
}
print "3 times inside and 13 times outside yields $password\n";
|
Of course, you get the same result every time, which is not as powerful
as a random salt on UNIX, but you can have a lot of fun with this. Play
around with different ways to generate an array of salts, or to derive a
salt from wacky sources such as the longitute and latitude of the place
where you were born, or how much recursion you want or even the number
of recursions as a derivative of something crypted and cryptic.
Final Caution for WIN32 Users
Remember, your encrypted string is only as secure as the algorithm you
use when you're playing with static data. If someone figures out "Oh, yes,
I see that the person is using the last prime factor of the
double-integral of the first bridge in his sister's favorite Beatles tune
translated to an atonal scale," then there's nothing you can do. The
chances of someone figuring that out and reverse-engineering your
algorithm is much smaller than their being able to get to the source code
by some other hackery.
Demo: &encrypt_password and &encrypt_password_Win32
Why not try out the working example?
The source code for the working example is available to download as
a zip file.
Please send suggestions and corrections to Ha Quach.
Next Article: Password-Protected Directories for Apache
Feedback on 'Random and Recursive Crypting using Salt on Unix and Win32'
View the profile on Ha Quach and the list of other Articles by Ha Quach.
|
|