NAME
Apache2::API::Password - Create and verify HTTP Basic Auth password hashes (APR1/bcrypt/SHA-crypt)
SYNOPSIS
use Apache2::API::Password;
# Create a new hash from a cleartext password (random salt)
# MD5-crypt (APR1, "$apr1$") — default
my $ht = Apache2::API::Password->new( 'secret', create => 1 );
my $hash = $ht->hash; # "$apr1$abcd1234$...."
# Create APR1 with a provided salt (max 8 chars; [./0-9A-Za-z])
my $ht2 = Apache2::API::Password->new( 'secret', create => 1, salt => 'hfT7jp2q' );
say $ht2->hash;
# Wrap an existing APR1 ($apr1$) hash and verify user input
my $ht3 = Apache2::API::Password->new( '$apr1$hfT7jp2q$DcU1Hf5w2Q/9G8yqv1hbl.' );
my $ok = $ht3->matches( 'secret' );
# Bcrypt ($2y$), choose a cost (04..31); defaults to 12
my $b = Apache2::API::Password->new('s3cret', create => 1, algo => 'bcrypt', bcrypt_cost => 12);
say $b->hash; # "$2y$12$..."
# SHA-crypt ($5$ = SHA-256, $6$ = SHA-512), optionally set rounds
my $s6 = Apache2::API::Password->new('s3cret', create => 1, algo => 'sha512', sha_rounds => 150000);
say $s6->hash; # "$6$rounds=150000$..."
# Accessors
my $hahs_password = $ht->hash;
# parsed from the hash
my $salt = $ht3->salt;
VERSION
v0.1.0
DESCRIPTION
Apache2::API::Password
creates and verifies password hashes used by Apache HTTP Basic Authentication. It supports:
APR1 / MD5-crypt (
$apr1$
) — same ashtpasswd -m
This implements the full APR1 algorithm (password + magic + salt, alternate sum, bit-mixing, 1000 rounds, and the crypt-style 64-symbol encoding) and is fully compatible with Apache’s
htpasswd -m
and " apache_md5_crypt" in Crypt::PasswdMD5.bcrypt (
$2y$
) — same ashtpasswd -B
Generated via the system
crypt(3)
when available; otherwise falls back toAuthen::Passphrase::BlowfishCrypt
,Crypt::Bcrypt
, orCrypt::Eksblowfish::Bcrypt
.SHA-crypt (
$5$ = SHA-256, $6$ = SHA-512
) — same ashtpasswd -2
/-5
Generated via the system
crypt(3)
when available; otherwise falls back toCrypt::Passwd::XS
.
This class handles existing Apache password or create new ones, and makes it possible to retrieve the encoded password, or to test if a user-provided clear password matches.
When constructing from an existing hash, the scheme is auto-detected by prefix ($apr1$
, $2y$
, $5$
, $6$
) and matches
uses the appropriate verifier.
CONSTRUCTOR
new
my $ht = Apache2::API::Password->new( $clear, create => 1 );
my $ht = Apache2::API::Password->new( $clear, create => 1, salt => $salt );
my $ht = Apache2::API::Password->new( $apr1_hash );
# Multi-algorithm creation:
my $b = Apache2::API::Password->new( $clear, create => 1, algo => 'bcrypt', bcrypt_cost => 12 );
my $s5 = Apache2::API::Password->new( $clear, create => 1, algo => 'sha256', sha_rounds => 6000 );
my $s6 = Apache2::API::Password->new( $clear, create => 1, algo => 'sha512', sha_rounds => 150000 );
This creates an instance either from:
a cleartext password (
$clear
) withcreate => 1
Generates a new hash. If
salt
is provided:APR1: clamped to
[./0-9A-Za-z]
, truncated to 8 chars.bcrypt: 22 chars in
[./0-9A-Za-z]
(bcrypt base64).SHA-crypt: up to 16 chars in
[./0-9A-Za-z]
.
If omitted, a random salt is generated from a cryptographic RNG when available.
an existing modular-crypt hash string
E.g. the right-hand side of a
.htpasswd
line:$apr1$...
,$2y$...
,$5$...
, or$6$...
. The salt (and rounds/cost where applicable) are parsed.
Note that the Apache algorithm to generate md5 password is not the same as simply using Digest::MD5. Apache algorithm uses a more enhanced approach with a thousand iterations.
This constructor returns the newly instantiated object upon succes, or, upon error, returns undef
in scalar context, or an empty list in list context.
METHODS
algo
# or 'bcrypt', 'sha256', 'sha512'
$ht->algo( 'md5' );
my $which = $ht->algo;
Sets or gets the hashing algorithm used by "make" when create
is true: md5
, bcrypt
, sha256
, or sha512
. Default is md5
bcrypt_cost
# 04..31
$ht->bcrypt_cost(12);
my $c = $ht->bcrypt_cost;
Sets or gets the bcrypt cost factor (4–31). Default is 12. Higher values increase security but slow computation. Note: Apache's htpasswd -B
caps at 17; this module supports 4..31.
create
$ht->create(1);
my $bool = $ht->create;
Boolean flag indicating whether the constructor should create a new hash from the provided cleartext. Typically passed to new
.
hash
my $hash = $ht->hash;
# validate & set; also updates 'salt'
$ht->hash( $crypt_hash );
Gets or sets the stored hash (e.g.: $apr1$
). Setting validates format and extracts metadata, such as salt
.
salt
my $salt = $ht->salt;
$ht->salt( 'abcd1234' );
Gets or sets the salt (1–8 chars in [./0-9A-Za-z]
for MD5
, 22 chars for bcrypt
, 1–16 chars for SHA-256/512
, alphabet [./0-9A-Za-z]
).
If an hash is provided upon object construction, its salt
will be derived, and stored.
sha_rounds
$ht->sha_rounds(150000);
my $r = $ht->sha_rounds;
Sets or gets the number of rounds for SHA-256/512
(1000–999999999). Default is 5000.
make
my $hash = $ht->make( $clear_password );
my $hash = $ht->make( $clear_password, $salt );
Generates a hash using the selected algorithm. If $salt
is omitted, the value stored in "salt" is used or a random salt
is generated. The salt
is clamped to the valid alphabet and truncated to the appropriate number of characters for the algorithm.
Returns the generated hash on success, or, upon error, undef
in scalar context, or an empty list in list context.
make_md5
my $hash = $ht->make_md5( $clear_password, $salt );
Generates an APR1 MD5 hash ($apr1$<salt
$<hash>). Salt is 1–8 chars, default random.
make_bcrypt
my $hash = $ht->make_bcrypt( $clear_password, $salt );
Generates a bcrypt hash ($2y$<cost
$<salt-and-hash>). Salt is 22 chars, default random. Uses bcrypt_cost
.
make_sha256
my $hash = $ht->make_sha256( $clear_password, $salt );
Generates a SHA-256 crypt hash ($5$[rounds=<n
$]<salt>$<hash>). Salt is 1–16 chars, default random. Uses sha_rounds
.
make_sha512
my $hash = $ht->make_sha512( $clear_password, $salt );
Generates a SHA-512 crypt hash ($6$[rounds=<n
$]<salt>$<hash>). Salt is 1–16 chars, default random. Uses sha_rounds
.
matches
my $ok = $ht->matches( $user_input_password );
Compute a fresh hash using the instance salt and compare with the stored hash.
Returns true if the cleartext password matches.
EXAMPLES
Creating a .htpasswd
line
my $user = 'john';
my $ht = Apache2::API::Password->new( 's3cret', create => 1 );
say join( ':', $user, $ht->hash );
Verifying a login
my $stored = '$apr1$hfT7jp2q$DcU1Hf5w2Q/9G8yqv1hbl.';
my $ht = Apache2::API::Password->new( $stored );
if( $ht->matches( $input_password ) )
{
# ok
}
THREAD SAFETY
This module keeps per-object state only (algo
, salt
, bcrypt_cost
, sha_rounds
, hash
) and uses no mutable global variables except precompiled regex constants. As such, it is re-entrant and safe to use from multiple Perl ithreads as long as each thread has its own object instances.
For bcrypt/SHA-crypt, verification/generation prefers the system crypt(3)
; on modern libcs this is thread-safe. When the system lacks support, fallbacks are used:
Authen::Passphrase::BlowfishCrypt, Crypt::Bcrypt, Crypt::Eksblowfish::Bcrypt (bcrypt)
Crypt::Passwd::XS (bcrypt and SHA-crypt)
These libraries are widely used and do not expose shared mutable globals for the operations performed here; there are no known thread-safety issues in typical Perl ithread usage. If your deployment uses fork
rather than ithreads, objects are independent per process as usual.
COMPATIBILITY
APR1 output is identical to
htpasswd -m
and "apache_md5_crypt" in Crypt::PasswdMD5.bcrypt output is compatible with
htpasswd -B
($2y$...
).SHA-crypt output is compatible with
htpasswd -2
($5$
) and-5
($6$
).
All 64 encoding symbols (including trailing .
or /
) are valid.
SECURITY NOTES
Empty passwords
All algorithms accept empty strings. An empty password will verify successfully if you store its hash. Avoid this in production.
Legacy algorithm
APR1/MD5-crypt is legacy and weak by modern standards. Prefer
bcrypt
(with a cost appropriate to your CPU budget) orsha512
(SHA-crypt) where bcrypt is not available; retainAPR1
only for Apache compatibility. For bcrypt, remember the 72-byte input limit—longer passphrases are truncated by the algorithm.
AUTHOR
Jacques Deguest <jack@deguest.jp>
SEE ALSO
Crypt::PasswdMD5, Apache::Htpasswd, Authen::Htpasswd, Authen::Passphrase::BlowfishCrypt, Crypt::Bcrypt, Crypt::Eksblowfish::Bcrypt, Crypt::Passwd::XS, Apache htpasswd(1)
, Digest::MD5
COPYRIGHT & LICENSE
Copyright (c) 2023 DEGUEST Pte. Ltd.
You can use, copy, modify and redistribute this package and associated files under the same terms as Perl itself.