{
    "href": "/post/2005/04/13/password-problems-with-crypt-and-htpasswd-files/",
    "relId": "2005/04/13/password-problems-with-crypt-and-htpasswd-files",
    "title": "Password problems with crypt() and htpasswd files",
    "author": "pmjones",
    "markup": "html",
    "tags": [
        {
            "href": "/tag/php/",
            "relId": "php",
            "title": "PHP",
            "author": null,
            "created": null,
            "updated": [],
            "markup": "markdown"
        }
    ],
    "created": "2005-04-13 18:01:36 UTC",
    "updated": [
        "2005-04-13 18:01:36 UTC"
    ],
    "html": "<p>In working with <a href=\"http://solarphp.com\">Solar</a> today, I discovered an issue related to the <a href=\"http://php.net/crypt\">crypt()</a> function and password files generated by Apache htpasswd.  Technically, it's not a security issue with either of those fine programs, because they do work as documented and intended.  However, due to my own ignorance of the limitations of crypt(), I created a security issue of my own; perhaps this post will help others avoid it.</p>\n<p>The Solar_User_Auth class is very much like the PEAR Auth class, in that it lets you pick different container or storage types for your username/password authentication.  You can use a database table, LDAP, POP or IMAP email account, a .ini file, and more.  Similar to LiveUser, Solar_User_Auth also comes with a \"Multi\" container that lets you specify multiple authentication sources, so you can fall back from one to another automatically.</p>\n<p>My problem today was with the \"Htpasswd\" container for Solar_User_Auth.  Apache comes with a utility called \"htpasswd\" which lets you create a file of usernames and encrypted passwords.  The file format is pretty straightforward; each line consists of \"username:cryptedpass\".  To create a htpasswd file and insert the first username/password combination, you would issue \"htpasswd -c /where/you/want/htpasswd.data -a someuser\"; it will create the htpasswd.data file and prompt you for the password for \"someuser\", encrypting the password with the system crypt() function.</p>\n<p>Now here's the thing about crypt() ... effectively, it only looks at the first 8 characters of the password to generate the encrypted hash.  (Yes, there are ways to make crypt() use a longer salt, but that's not pertinent to this particular discussion, as we are only concerned with the way Apache htpasswd uses the crypt() function.)</p>\n<p>Thus, if you have a password *longer* than 8 characters, as long as the first 8 characters match, crypt() will call it a valid match.  For example, if your password is \"password\" and is stored as a crypted hash in a htpasswd file, checking against \"passwordX\" will be exactly the same as checking against \"password\", \"password123\", and so on; that is, it will be returned as a valid check because the first 8 characters match properly.  Similarly, if your password is \"longpassword\" and you check against only \"longpass\", that will be returned as valid, too.</p>\n<p>Obviously this is a problem.  The solution, at least for Solar_User_Auth_Htpasswd, is that from now on, password checks longer than 8 characters will be rejected automatically as invalid.  This sidesteps the problem entirely, even though it does limit users who want to use the Htpasswd driver to passwords of 8 characters or less.  The next release of Solar will have this patch in place, and it has already been committed to the Subversion repository for Solar.</p>\n<p>Have I missed anything important here?  Has anyone else out there run into anything like this?  If so, what was your solution?</p>\n<p><strong>Update:</strong> (2005-04-14) Although I'm retaining the 8-character limit under default DES encryption, <a href=\"http://paul-m-jones.com/blog/?p=135\">I've added SHA1 and APR1-MD5 support</a>, which should help a great deal.</p>\n"
}
