There is an ongoing outage on the primary CPAN mirror. It is possible to work around the issue by using MetaCPAN as a mirror.

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 as htpasswd -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 as htpasswd -B

    Generated via the system crypt(3) when available; otherwise falls back to Authen::Passphrase::BlowfishCrypt, Crypt::Bcrypt, or Crypt::Eksblowfish::Bcrypt.

  • SHA-crypt ($5$ = SHA-256, $6$ = SHA-512) — same as htpasswd -2 / -5

    Generated via the system crypt(3) when available; otherwise falls back to Crypt::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) with create => 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:

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) or sha512 (SHA-crypt) where bcrypt is not available; retain APR1 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.