The .htpasswd file can store passwords using several different hash algorithms, with the most common being:
- Traditional Unix
crypt()
(DES-based, limited to 8 characters) - Apache's extended
crypt()
with MD5 ($apr1$
prefix) - SHA-1 (
{SHA}
prefix) - BCrypt (
$2y$
or similar prefix)
The hash axF3s9cdEnsNP
appears to be using the traditional Unix crypt()
function with DES encryption. This format has several characteristics:
- Exactly 13 characters long
- Only uses characters from the base64 alphabet (./0-9A-Za-z)
- First 2 characters are the salt
Here's how to generate these hashes in different programming languages:
Python Implementation
import crypt
password = "mypassword"
salt = "ax" # First 2 chars of your example
hashed = crypt.crypt(password, salt)
print(hashed) # Output: axF3s9cdEnsNP (if password matches)
PHP Implementation
$password = "mypassword";
$salt = "ax";
$hashed = crypt($password, $salt);
echo $hashed; // Output: axF3s9cdEnsNP
C Implementation (Unix/Linux)
#include
#include
int main() {
char *password = "mypassword";
char *salt = "ax";
char *hashed = crypt(password, salt);
printf("%s\n", hashed);
return 0;
}
While the traditional crypt()
method is still supported for backward compatibility, it's considered insecure because:
- Limited to 8 significant characters
- Uses fast DES encryption which is vulnerable to brute force
- Small salt space (only 4096 possible variants)
For new implementations, consider using more secure alternatives:
# Example of generating more secure hashes in Python
import bcrypt
password = "mypassword".encode('utf-8')
hashed = bcrypt.hashpw(password, bcrypt.gensalt())
print(hashed.decode('utf-8'))
To verify if a password matches an existing .htpasswd entry:
import crypt
def verify_password(stored_hash, password):
salt = stored_hash[:2]
return crypt.crypt(password, salt) == stored_hash
# Usage:
print(verify_password("axF3s9cdEnsNP", "mypassword"))
When working with .htpasswd files, you'll encounter various hash formats like:
axF3s9cdEnsNP # Crypt (traditional UNIX)
$apr1$salt$hash # Apache MD5 variant
$2y$... # BCrypt
$5$... # SHA-256
$6$... # SHA-512
The example hash axF3s9cdEnsNP
appears to be using the traditional UNIX crypt() algorithm with DES encryption. This was the default in older Apache versions.
Key characteristics:
- 13 characters long
- First 2 characters are the salt
- Followed by 11 character hash
Here's how to generate compatible hashes in different languages:
Python Implementation
import crypt
password = "secret"
salt = "ax" # First 2 chars of existing hash
hash = crypt.crypt(password, salt)
print(hash) # Output: axF3s9cdEnsNP
PHP Implementation
$password = "secret";
$salt = "ax";
$hash = crypt($password, $salt);
echo $hash; // Output: axF3s9cdEnsNP
Perl Implementation
use Crypt::PasswdMD5;
my $password = "secret";
my $salt = "ax";
my $hash = unix_md5_crypt($password, $salt);
print $hash; # Output: axF3s9cdEnsNP
While the crypt/DES method still works, consider more secure alternatives:
# Using Python's passlib for better security
from passlib.apache import HtpasswdFile
ht = HtpasswdFile(".htpasswd")
ht.set_password("username", "password") # Uses SHA-256 by default
ht.save()
To test if your generated hash works:
# Python verification
import crypt
stored_hash = "axF3s9cdEnsNP"
input_pass = "secret"
salt = stored_hash[:2]
print(crypt.crypt(input_pass, salt) == stored_hash) # True if match