Last active
March 12, 2018 09:37
-
-
Save mz0/274ac003028899ca3dfe3cc8c779447d to your computer and use it in GitHub Desktop.
a PHP function to check user credentials against .htpasswd file with apr1 (MD5) hashes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* loosely based on | |
https://elonen.iki.fi/code/misc-notes/htpasswd-php | |
Use | |
=== | |
require('htpasswd.inc'); | |
if ( passOK($user,$pass) ) print "Access granted."; | |
*/ | |
/** | |
* @param $plaintext | |
* @param $salt | |
* @return string - as found in .htpasswd for given $plaintext password and $salt | |
Copied (+fix initialization warning) from | |
www.virendrachandak.com/techtalk/using-php-create-passwords-for-htpasswd-file | |
originally from | |
stackoverflow.com/questions/2994637/how-to-edit-htpasswd-using-php/8786956#8786956 | |
*/ | |
function apr1hash($plaintext,$salt) { | |
$len = strlen($plaintext); | |
$txt = $plaintext.'$apr1$'.$salt; | |
$bin = pack("H32", md5($plaintext.$salt.$plaintext)); | |
for($i = $len; $i > 0; $i -= 16) { $txt .= substr($bin, 0, min(16, $i)); } | |
for($i = $len; $i > 0; $i >>= 1) { $txt .= ($i & 1) ? chr(0) : $plaintext{0}; } | |
$bin = pack("H32", md5($txt)); | |
for($i = 0; $i < 1000; $i++) { | |
$new = ($i & 1) ? $plaintext : $bin; | |
if ($i % 3) $new .= $salt; | |
if ($i % 7) $new .= $plaintext; | |
$new .= ($i & 1) ? $bin : $plaintext; | |
$bin = pack("H32", md5($new)); | |
} | |
$tmp=''; | |
for ($i = 0; $i < 5; $i++) { | |
$k = $i + 6; | |
$j = $i + 12; | |
if ($j == 16) $j = 5; | |
$tmp = $bin[$i].$bin[$k].$bin[$j].$tmp; | |
} | |
$tmp = chr(0).chr(0).$bin[11].$tmp; | |
$tmp = strtr(strrev(substr(base64_encode($tmp), 2)), | |
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", | |
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); | |
return '$apr1$'.$salt.'$'.$tmp; | |
} | |
// Loads htpasswd file into an array of form | |
// Array( username => crypted_pass, ... ) | |
function load_htpasswd($fileloc='.htpasswd') { | |
$res = Array(); | |
if ( file_exists($fileloc) ) { | |
foreach(file($fileloc) as $l) { | |
$array = explode(':',$l); | |
$user = $array[0]; | |
$pass = chop($array[1]); | |
$res[$user] = $pass; | |
} | |
} | |
return $res; | |
} | |
/* Where's salt? | |
apr_md5_encode(const char *pw, const char *salt, char *result, apr_size_t nbytes) | |
http://svn.apache.org/viewvc/apr/apr/trunk/crypto/apr_md5.c?revision=1460244&view=markup#l535 | |
// It stops at the first '$' or 8 chars, whichever comes first | |
for (ep = sp; (*ep != '\0') && (*ep != '$') && (ep < (sp + 8)); ep++) { | |
continue; | |
} | |
// Get the length of the true salt | |
sl = ep - sp; | |
see .htpasswd, salt is always 8 chars long, ends with '$': | |
3oVLW6C6$m2gqFRWaYmZURzERixkKd1 | |
bdlItA56$sYBF3T/7vzNbkC/3mPse01 | |
gHB5qipH$9xQSr6o6BOoAUO34MM.8y1 | |
0uvNx1Oj$wobMgp7H6Wxbi5M0fjsYK/ | |
eI7jaDcY$kjSqQ7DId/Gh04WvWMkO/0 | |
*/ | |
/** | |
* @param string $user | |
* @param string $pass | |
* @param string $fileloc | |
* @return bool true if $user exists in $fileloc (.htpasswd is the default) and $pass matches | |
* | |
*/ | |
function passOK( $user, $pass, $fileloc='.htpasswd' ) { | |
$pass_array = load_htpasswd($fileloc); | |
if ( !isset($pass_array[$user])) { | |
print "No record found for $user"; | |
return False; | |
} | |
$rec = $pass_array[$user]; | |
/* See .htpasswd record parsing reference implementation in | |
apr_password_validate(const char *passwd, const char *hash) | |
definition in | |
[apr_password.c](http://svn.apache.org/viewvc/apr/apr/trunk/crypto/apr_passwd.c?view=markup#l108) | |
*/ | |
if (substr($rec, 0, 6) == '$apr1$' ) { | |
$salt = substr($rec,6,8); | |
return $rec == apr1hash($pass,$salt); | |
} else { | |
// we do not deal with y2 (blowfish) and others | |
print "No idea what's going on here!"; | |
return false; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment