[Back to main] [Printable version] [Search] [Leave a comment] Comments

Functions for using .htpasswd in PHP [Leave a comment]

This script (htpasswd.inc) allows you to

  • read .htpasswd file in PHP
  • test passwords against it (unix crypt or SHA1)
  • add new user/password pairs into it
  • write .htpasswd file back to disk

The script is copyright (C) 2004,2005 by Jarno Elonen and it's licensed under the Modified BSD license (see the script comments for details).

Thanks to J├╝rgen Galupki for a CRYPT_SALT_LENGTH patch and Jonas Wagner for SHA1 support.

Usage example

Here's how to emulate the basic .htaccess method to password protect your web site. Obviously, it's not the most useful way to employ this script, but a good example anyway.


<?php
  header
("Expires: 0");
  
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
  
header("Cache-Control: public");

  require_once(
'htpasswd.inc');
  
$pass_array load_htpasswd();

  if (isset(
$_SERVER['PHP_AUTH_USER']) && test_htpasswd$pass_array,  $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] ))
  {
    print(
"Welcome!");
    
// Do stuff
  
}
  else
  {
    
header('WWW-Authenticate: Basic realm="Restricted area"');
    
header('HTTP/1.0 401 Unauthorized');
    echo 
'Access denied. Please enter correct credentials.';
    exit;
  }
?>

The code


<?php

// .htpasswd file functions
// Copyright (C) 2004,2005 Jarno Elonen <elonen@iki.fi>
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
//   list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
// * The name of the author may not be used to endorse or promote products derived
//   from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Usage
// =====
//   require_once('htpasswd.inc');
//   $pass_array = load_htpasswd();
//
//   if ( test_htpasswd( $pass_array,  $user, $pass ))
//       print "Access granted."
//
//   $pass_array[$new_user] = rand_salt_crypt($new_pass);
//   save_htpasswd($pass_array);
//
//   $pass_array[$new_user2] = rand_salt_sha1($new_pass2);
//   save_htpasswd($pass_array);
//
// Thanks to Jonas Wagner for SHA1 support.

define("HTPASSWDFILE"".htpasswd");

// Loads htpasswd file into an array of form
// Array( username => crypted_pass, ... )
function load_htpasswd()
{
  if ( !
file_exists(HTPASSWDFILE))
      return Array();

  
$res = Array();
  foreach(
file(HTPASSWDFILE) as $l)
  {
    
$array explode(':',$l);
    
$user $array[0];
    
$pass chop($array[1]);
    
$res[$user] = $pass;
  }
  return 
$res;
}

// Saves the array given by load_htpasswd
// Returns true on success, false on failure
function save_htpasswd$pass_array )
{
  
$result true;

  
ignore_user_abort(true);
  
$fp fopen(HTPASSWDFILE"w+");
  if (
flock($fpLOCK_EX))
  {
    while( list(
$u,$p) = each($pass_array))
      
fputs($fp"$u:$p\n");
    
flock($fpLOCK_UN); // release the lock
  
}
  else
  {
    
trigger_error("Could not save (lock) .htpasswd"E_USER_WARNING);
    
$result false;
  }
  
fclose($fp);
  
ignore_user_abort(false);
  return 
$result;
}

// Generates a htpasswd compatible crypted password string.
function rand_salt_crypt$pass )
{
  
$salt "";
  
mt_srand((double)microtime()*1000000);
  for (
$i=0$i<CRYPT_SALT_LENGTH$i++)
    
$salt .= substr("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"mt_rand() & 631);
  return 
crypt($pass$salt);
}

// Generates a htpasswd compatible sha1 password hash
function rand_salt_sha1$pass )
{
  
mt_srand((double)microtime()*1000000);
  
$salt pack("CCCC"mt_rand(), mt_rand(), mt_rand(), mt_rand());
  return 
"{SSHA}" base64_encode(pack("H*"sha1($pass $salt)) . $salt);
}

// Generate a SHA1 password hash *without* salt
function non_salted_sha1$pass )
{
  return 
"{SHA}" base64_encode(pack("H*"sha1($pass)));
}

// Returns true if the user exists and the password matches, false otherwise
function test_htpasswd$pass_array$user$pass )
{
  if ( !isset(
$pass_array[$user]))
      return 
False;
  
$crypted $pass_array[$user];

  
// Determine the password type
  // TODO: Support for MD5 Passwords
  
if ( substr($crypted06) == "{SSHA}" )
  {
    
$ohash base64_decode(substr($crypted6));
    return 
substr($ohash020) == pack("H*"sha1($pass substr($ohash20)));
  }
  else if ( 
substr($crypted05) == "{SHA}" )
    return (
non_salted_sha1($pass) == $crypted);
  else
    return 
crypt$passsubstr($crypted,0,CRYPT_SALT_LENGTH) ) == $crypted;
}

// Internal test
function internal_unit_test()
{
  
$pwds = Array( "Test" => rand_salt_crypt("testSecret!"),
                 
"fish" => rand_salt_crypt("sest Ticret"),
                 
"Generated" => "/uieo1ANOvsdA",
                 
"Generated2" => "Q3cbHUBgm7aYk");

  
asserttest_htpasswd$pwds"Test""testSecret!" ));
  
assert( !test_htpasswd$pwds"Test""wrong pass" ));
  
asserttest_htpasswd$pwds"fish""sest Ticret" ));
  
assert( !test_htpasswd$pwds"fish""wrong pass" ));
  
asserttest_htpasswd$pwds"Generated""withHtppasswdCmd" ));
  
assert( !test_htpasswd$pwds"Generated""" ));
  
asserttest_htpasswd$pwds"Generated2""" ));
  
assert( !test_htpasswd$pwds"Generated2""this is wrong too" ));
}

?>

blog comments powered by Disqus