Suppose we’re using the Netscape
Directory Server. It stores passwords in one of three ways: as
cleartext, encrypted with the Unix crypt( )
function, or encrypted using S
ecure Hash Algorithm
(SHA). Here’s how the server represents an
SHA-encrypted password as an attribute of a user object:
{SHA}W8r/fyL/UzygmbNAjq2HbA67qac=
The gobbledygook after {SHA}
is another
Base64-encoded value. It decodes to a 20-byte SHA hash. We can
extract that value in hexadecimal form like this:
use MIME::Base64; print unpack('H*', decode_base64('W8r/fyL/UzygmbNAjq2HbA67qac='));
The result is `5bcaff7f22ff533ca099b3408ead876c0ebba9a7',
which is the hexadecimal representation of the SHA hash value of the
password open sesame
.
Like Unix’s crypt( )
, SHA is a one-way
hashing function. When you assign a password in Directory Server, it
applies the one-way hash to the cleartext password to produce an
encrypted password, which it stores in a user object in the
directory. When a browser, mailreader, or newsreader tries to
authenticate to a Directory Server-based application—such as
Netscape’s web, mail or news server—the sequence of
events is as follows:
We’ll call this provider the application server, to distinguish it from the directory server. This client-to-application-server connection can be a cleartext channel or it can be SSL-encrypted. In either case the application server ultimately gets hold of a cleartext password.
Again this channel may be cleartext or SSL-encrypted.
It applies SHA to the cleartext password received from the user to produce an encrypted password. Then it compares that encrypted password with the one retrieved from Directory Server. If the two match, the application server accepts the credentials as authentic.
Although the hash algorithm itself is thought to be irreversible, which means that there is no way to recover a cleartext password from its hashed counterpart, all the usual caveats apply. We like to say that this kind of scheme authenticates a user, but really it just authenticates a set of credentials that may or may not have issued from that user. If I leave my browser logged in to a protected site and you walk up to my PC and start fetching pages, the cached credentials will still look just fine to the application server. Of course, the jig is up if I allow my cleartext password to travel over an unencrypted connection and you capture it with a sniffer. The jig may or may not be up if you gain access to my directory server and capture encrypted passwords. The passwords, even though encrypted, are vulnerable to brute-force dictionary attacks. For this reason the cryptographically ideal password is a long string of gobbledygook. Unfortunately that kind of password is worse than useless to the poor human who is supposed to use it, who can’t memorize a long string of gobbledygook and who will have to write it down on a scrap of paper and stick it to a keyboard or monitor. This dilemma is the Scylla and Charybdis of computer security, and there’s no simple way to navigate through it.
Bearing these caveats in mind, we can write a script that uses Directory Server in the same way that Netscape’s SuiteSpot servers do. We’ve already seen how to fetch attributes of a user object by using Group::LdapGroup. Here’s a fragment that fetches Aladdin’s SHA-encrypted password:
use Group::LdapGroup; my $g = Group::LdapGroup->new("ldap.roninhouse.com",389, "o=RoninHouse","subscribers","uid=admin,o=RoninHouse", "admin_password"); print $g->getProperty("Aladdin","userpassword");
If we’ve received a password from a user who purports to be Aladdin, we SHA-encrypt the cleartext password and compare it to the encrypted one:
use MIME::Base64; use SHA; my $sha = new SHA; $sha->add("open sesame"); my $digest = $sha->digest(); # compute digest my $password = # look up password in directory $g->getProperty("Aladdin","userpassword"); $password =~ s/(SHA{([^}]+)/; # isolate password in $1 if ( $1 eq encode_base64($digest) ) # compare { print "Authenticated"; } else { print "Not authenticated"; }
3.129.22.135