NIST-compliant password validation and strength analysis.
Validates passwords against configurable policy rules and provides strength assessment for UI feedback. Follows NIST SP 800-63B guidelines:
- Minimum 8 characters (configurable)
- Maximum 72 bytes (bcrypt compatibility)
- Common password rejection via embedded 10k list
- Optional composition rules (uppercase, digit, special char)
- Optional HIBP breached password check
Usage with Ecto Changesets
changeset
|> Sigra.PasswordPolicy.validate()
changeset
|> Sigra.PasswordPolicy.validate(min_length: 12, require_uppercase: true)Strength Assessment
Sigra.PasswordPolicy.check_strength("correcthorsebatterystaple")
#=> {:strong, []}
Sigra.PasswordPolicy.check_strength("abc")
#=> {:weak, ["Use a longer password (at least 8 characters)"]}
Summary
Functions
Checks if a password has been found in data breaches via the HIBP API.
Assesses password strength for UI feedback.
Validates a password field on an Ecto changeset against the configured policy.
Functions
@spec check_breached(String.t()) :: {:ok, non_neg_integer()} | {:error, term()}
Checks if a password has been found in data breaches via the HIBP API.
Uses the k-Anonymity model: only the first 5 characters of the SHA-1 hash are sent to the API, so the full password hash is never transmitted.
Returns {:ok, count} where count is the number of times the password
appeared in breaches, or {:error, reason} on failure.
Off by default -- must be explicitly enabled.
Examples
Sigra.PasswordPolicy.check_breached("password")
#=> {:ok, 3861493}
Sigra.PasswordPolicy.check_breached("xK9#mP2$vL5")
#=> {:ok, 0}
Assesses password strength for UI feedback.
Returns {strength, suggestions} where strength is :weak, :fair,
or :strong, and suggestions is a list of improvement hints.
Scoring
- Length: 0-4 chars = 0pts, 5-7 = 1pt, 8-11 = 2pts, 12-15 = 3pts, 16+ = 4pts
- +1 for mixed case
- +1 for digits
- +1 for special characters
- -2 for common password
- -1 for >3 repeated consecutive characters
- -1 for >3 sequential characters
Thresholds: 0-2 = :weak, 3-4 = :fair, 5+ = :strong
Examples
iex> Sigra.PasswordPolicy.check_strength("correcthorsebatterystaple")
{:strong, []}
@spec validate( Ecto.Changeset.t(), keyword() ) :: Ecto.Changeset.t()
Validates a password field on an Ecto changeset against the configured policy.
If the changeset has no :password change, returns the changeset unchanged.
Options
All options from @default_opts can be overridden:
:min_length- Minimum password length (default: 8):max_bytes- Maximum password byte size (default: 72):require_uppercase- Require at least one uppercase letter (default: false):require_digit- Require at least one digit (default: false):require_special- Require at least one special character (default: false):check_common- Check against common passwords list (default: true):check_breached- Check against HIBP API (default: false)
Examples
changeset |> Sigra.PasswordPolicy.validate()
changeset |> Sigra.PasswordPolicy.validate(min_length: 12)