http://www.perlmonks.org?node_id=296799

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

There's been a discussion in my group about encrypting the passwords of people who log into the site. Similar to the one way encryption of Linux passwords.

Right now, the site stores the passwords in a seperate directory from the perl script to avoid the chance that someone may figure out how to read the directory contents. The password directory isn't readable by anyone except the running script.

We plan on shifting the site from a flat file storage scheme to a MySQL DB and will likely store the passwords in this database. At that point should we even store the passwords in this DB and if so, how do I encrypt it via Perl? I've thought about using MD5 or CRC32, but the passwords are likely to be very short, anywhere from 8 to perhaps 24 or so characters and I don't know if those two checks would be fine or not.

I did a search for password hashes, but naturally, hash has a different meaning in Perl and the results were not what I needed :( What should I be looking for out there? Is it fair to stick a link to my site here?

Thanks for you patience.

Replies are listed 'Best First'.
Re: Ecrypting passwords
by tilly (Archbishop) on Oct 05, 2003 at 22:16 UTC
    You are on the right path. For a basic one-way encryption you can take the password, append to it some standard text that is part of your algorithm, append some salt that varies per user, and then take an MD5 hash of that. Store the MD5 hash.

    Given a password, if the same procedure yields the signature that you stored, then the passwords matched.

    There probably is something more secure than this, but this is enough that your remaining security problems are more likely to be elsewhere - possibly plaintext passwords sent over the wire, people with bad passwords, people reusing passwords, people who can be social engineered...

      There probably is something more secure than this
      Not as far as I know. That's how /etc/passwd works, how MySQL hashes passwords, and seems to be the widely accepted way to solve this particular problem..
Re: Ecrypting passwords
by tachyon (Chancellor) on Oct 05, 2003 at 23:10 UTC

    crypt() is very similar to Linux passwords. I would use MD5 (better hash) or Crypt::Blowfish (reversible if you want to be able to email password reminders) but this is very simple to implement:

    my $salt = 'Na'; print crypt( 'password', $salt ), $/; print "Valid" if crypt( 'password', $salt ) eq 'Na3eD25AE5/zI';

    cheers

    tachyon

    s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

      If you want to send password "reminders", then because the user has forgotten their password you don't really need to be able to retrive their old password and send it to them. Just generate a new password for them.
        True... but there is still stuff to think about.
        • How do you verify that the person is indeed the person you think you're sending the password to?
        • Do you change the password immediatly as someone made the request, or do you wait to verify that the request was valid by verifiying the user through some other means... (If the person was on your site as the password was reset, this could be a bad thing...)
        Anyways, just a "thought exercise" first thing in the morning...
        Cheers.


        ----
        Zak
        undef$/;$mmm="J\nutsu\nutss\nuts\nutst\nuts A\nutsn\nutso\nutst\nutsh\ +nutse\nutsr\nuts P\nutse\nutsr\nutsl\nuts H\nutsa\nutsc\nutsk\nutse\n +utsr\nuts";open($DOH,"<",\$mmm);$_=$forbbiden=<$DOH>;s/\nuts//g;print +;
Re: Ecrypting passwords
by skx (Parson) on Oct 06, 2003 at 00:02 UTC

     As has already been mentioned you can use MD5 for turning your passwords into unrecoverable strings inside your database.

     If you do this you read the password from the user, apply the MD5 function and compare the result with that in your database, this is fast, easy and very standard.

     However it suffers from two "drawbacks":

    • Your users are still sending their passwords in plaintext, unless you're using a secure server and HTTPS.
    • You don't have the plaintext password in your database, so you cannot implement a "remind me of my password" function.

     The first point may not be a big deal to you, I guess it depends upon the nature of your site.

     The second point might be an issue if you have lots of forgetful users. In practise if you include a function for "resetting" your users passwords and mailing them a new one that will probably suffice.

    Steve
    ---
    steve.org.uk
      Your users are still sending their passwords in plaintext, unless you're using a secure server and HTTPS.

      Not necessarily true. See Digest Authentication, which there is a useful introduction to here.

        Digest Auth still effectivly transfers the password in plaintext no? sure the client hashes to md5 or whatever but someone listning on the line can use that hash to auth just as easily as the plaintext version. I have not read the full RFC but that is my understanding of it.


        -Waswas
      You don't have the plaintext password in your database, so you cannot implement a "remind me of my password" function.

      An even better way is to simply reset the password, sending them the new password and an urgent request to change it ASAP. No worry about needing to keep a plaintext copy around... so you can implement the one way md5 checksum as tilly suggested.


      cp
      ----
      "Never be afraid to try something new. Remember, amateurs built the ark. Professionals built the Titanic."
        That is only better if management agrees to that solution. Otherwise..well remember that part of what you are being paid for is to do things that you wouldn't choose to do if you weren't being paid..
Re: Ecrypting passwords
by fokat (Deacon) on Oct 06, 2003 at 02:37 UTC

    Take a look at Crypt::PasswdMD5, as it can do exactly what you want in all-Perl.

    Best regards

    -lem, but some call me fokat

Re: Ecrypting passwords
by hardburn (Abbot) on Oct 06, 2003 at 13:55 UTC

    Avoid MD5 for anything new. Its made people in the cryptographic community so nervous that they actually suggest using something from the NSA (namely, SHA1) instead of MD5. Its not outright broken, but it doesn't look too good.

    If you can, use SHA1 instead. You'll need to grab Digest::SHA1 off CPAN.

    The way I normally do it is to have a table containing usernames and a seperate related table (1-to-1 relationship) containing the encrypted password for that user. This is because it is more difficult (though not impossible) to carry out a cross-table SQL attack. The schema looks something like this:

    CREATE TABLE secure_fields ( id INTEGER UNSIGNED PRIMARY KEY AUTO_INCREMENT NOT NULL, salt INTEGER UNSIGNED NOT NULL, passwd CHAR(40) NOT NULL );

    When encoded in hex, SHA1 produces a string 40 characters long. You can change the size of the passwd field for other encodings if you want.

    Encoding the password looks something like this (in psudo-code):

    plaintex_passwd = The plaintext password from the user hex() = Hex encoding function SHA1() = SHA1 digest function salt = Random 32-bit value (doesn't need to be a cryptographically s +ecure random num) + = String concat hex( SHA1( salt + SHA1( salt + plaintext_passwd ) ) )

    IIRC, MySQL's PASSWORD() function uses MD5, so avoid it.

    IMHO, an often overlooked part of cryptography is that you need to code your application so that you can change crypto algorithms easily. In this case, that means you need to code so that you can replace SHA1 with something else if tommarow it turns out that SHA1 is totally broken. The Digest module is good for this purpose.

    Update: Fixed typo.

    ----
    I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
    -- Schemer

    Note: All code is untested, unless otherwise stated

      Something I don't understand. Perhaps someone could enlighten me?

      If we encrypt the password via SHA1 and store it in database how will we compare it at login? Doesn't SHA1 encrypt the same string into a random hash each time? So if we encrypt the user given password and compare it to the stored password aren't the hashes going to be different?

      If we somehow unencrypt the hashes how is that done? I don't see that mentioned in the SHA1 docs.

      Sorry for being dense.

      Neil Watson
      watson-wilson.ca

        If you put the exact same data into SHA1 (or any other reasonable hashing algorithm), the exact same hash value will come out. It's only when you have different data (even as small as one bit) that a cryptographic hash will give a different output. Non-cryptographic hashing algorithms (such as the one used by Perl's hash data structure) may produce collisions for different data, which usually need to be handled by the program in question.

        ----
        I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
        -- Schemer

        : () { :|:& };:

        Note: All code is untested, unless otherwise stated

Re: Ecrypting passwords
by tachyon (Chancellor) on Oct 06, 2003 at 09:32 UTC

    The password directory isn't readable by anyone except the running script.

    Actually the script will likely be running as the webserver default user so anyone that hacks the server via a badly coded CGI will have access to that dir.

    cheers

    tachyon

    s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

Re: Ecrypting passwords
by kjd (Acolyte) on Oct 07, 2003 at 01:11 UTC
    Here's two programs I had from a project using salted SHA1 passwords as above in hardburn's post. One hashes a given password, and the other compares a hashed password to plaintext, returning the result:
    % ./sha1pass test {SSHA}06mQAx78jfQWriWZRKeC8r3UCcpF7QtdsJ967w== % ./cmpsha1pass {SSHA}06mQAx78jfQWriWZRKeC8r3UCcpF7QtdsJ967w== test debug: got b64salt: Re0LXbCfeu8= Original: {SSHA}06mQAx78jfQWriWZRKeC8r3UCcpF7QtdsJ967w== Computed: {SSHA}06mQAx78jfQWriWZRKeC8r3UCcpF7QtdsJ967w== Passwords match. % ./cmpsha1pass {SSHA}06mQAx78jfQWriWZRKeC8r3UCcpF7QtdsJ967w== badpass debug: got b64salt: Re0LXbCfeu8= Original: {SSHA}06mQAx78jfQWriWZRKeC8r3UCcpF7QtdsJ967w== Computed: {SSHA}feht7DIITMD3PyNONqnXnFAnNVJF7QtdsJ967w== Passwords do not match!
Re: Ecrypting passwords
by SavannahLion (Pilgrim) on Oct 07, 2003 at 06:24 UTC
    I really appreciate all the advice I've been given. Not sure what the proper way is to respond to so many people all at once so I'll just try this. If it doesn't fly, I'd like a pointer on the right way ;)



    I temporarily installed and ran PerlDiver to see what modules I have available to me. I have MD5 but not SHA1. I guess I'll have to dig in and get that installed. Currently, there's a bug (not written by me :p) that actually allows registered users no password for their login! Needless to say, I was kind of surprised at this design choice, but oh well. There's a second ID hash in the cookie that ties the user in with the ID, but it isn't actually used to validate users the same way a login/password does. That's going to get fixed ASAP. At this moment, I don't really care if I implement MD5, install SHA1 then switch over. Registration is going to require a valid eMail and new passwords can easily be sent out. Which brings me to my next point, the thought of decrypting passwords and sending them out if people forget never actually occurred to me. But as I think about it, it's probably best to go with one way hashes and send out new passwords to valid eMail accounts any ways.
    Unfortunately, I still haven't figured out how to create a secured SSL connection to the site. But I figure it's only a matter of time, a lot of searches, and reading before I come across the answer. If only I can stay focused and not be distracted so easily. Hey, look what I found on eBay!



    Actually the script will likely be running as the web server default user so anyone that hacks the server via a badly coded CGI will have access to that dir.
    Sadly, I don't have any books on that topic. Or rather, they cover CGI security rather vaguely. What constitutes a badly written CGI and how might I find out? I've usually written things that are used internally where the source code is open (i.e. not mine) and never released to the public. So software security was a topic that never really came up. Is there a decent site or book that you recommend I can study up on this? I'll do a search on Perl Monks to see what bubbles up. Thanks for bringing that to my mind, I would've forgotten during the data conversion. Actually, to tell the truth, I did forget. :-\



    Thanks for all the suggestions and heads up. It seems I forgot more than a thing or two about things I should've been thinking about. Well... that's what's so great about Perl Monks. That and all those brains I can poke at.

    Is it fair to stick a link to my site here?

    Thanks for you patience.

      A nice place to look for web application security is the Open Web Application Security Project which as well as having a nice guide as to what the major flaws in many such systems are also have a program (In Java) called WebGoat which pretty much takes you through common exploits as you try them on a test server. In my opinion this helps you understand the issues on an intuitive level and to avoid allowing such holes into your own code.

      Another useful resource with some Perl-specific hints is the W3 Security FAQ, which when used in conjunction with the above and a good knowledge of generally reliable coding strategies should stand you in good stead.