Skip to content

Password Quality and Hashing

Linux checks a new password's strength with the pam_pwquality module, configured in /etc/security/pwquality.conf, and hashes it with whatever algorithm the pam_unix line in /etc/pam.d/common-password names. Both need to be right, and an image commonly has the module missing entirely.

Install the module

dpkg -l libpam-pwquality | grep ^ii || sudo apt install -y libpam-pwquality cracklib-runtime
grep pam_pwquality /etc/pam.d/common-password

Installing the package (CIS 5.2.1.3; cracklib-runtime supplies the dictionary the dictcheck setting uses, Debian CIS 5.3.1.4) adds a pam_pwquality.so line to common-password automatically through pam-auth-update (5.2.2.3). If the grep prints nothing after the install, the profile was disabled: sudo pam-auth-update --enable pwquality. If that says there's no such profile, create it and enable it:

sudo tee /usr/share/pam-configs/pwquality > /dev/null <<'EOF'
Name: Pwquality password strength checking
Default: yes
Priority: 1024
Conflicts: cracklib
Password-Type: Primary
Password:
        requisite                       pam_pwquality.so retry=3
EOF
sudo pam-auth-update --enable pwquality

On Mint and Debian, the /etc/pam.d/common-* files are generated from the profiles in /usr/share/pam-configs/ by pam-auth-update. A line you add to common-password by hand works until something runs pam-auth-update again (a package upgrade can), which is why the profile is the durable way.

The quality settings

/etc/security/pwquality.conf. Every line ships commented out; uncomment and set. The format is name = value with spaces around the equals sign.

Setting Value Meaning
minlen 8 to 16 (use 14; CIS 5.2.3.2.2 asks for 14 or more, STIG UBTU-22-611035 for 15) Minimum length.
difok 2 or more (CIS 5.2.3.2.1); 8 for a STIG baseline (UBTU-22-611040) Characters that must differ from the old password
dcredit -1 At least one digit (negative numbers mean "required", positive mean "bonus")
ucredit -1 At least one uppercase letter
lcredit -1 At least one lowercase letter
ocredit -1 At least one symbol
minclass 3 or 4 Alternative to the four credit lines: how many character classes must appear
maxrepeat 3 (CIS 5.2.3.2.4) No more than 3 of the same character in a row
maxsequence 3 (CIS 5.2.3.2.5) No abcd or 1234 runs longer than 3
dictcheck 1 (CIS 5.2.3.2.6) Reject dictionary words
enforcing 1, or absent (never enforcing = 0; CIS 5.2.3.2.7, STIG UBTU-22-611045) 0 would turn every check into a warning

The four credit lines and dictcheck are STIG CAT II items too (UBTU-22-611010 to 611030). | usercheck | 1 | Reject passwords containing the username | | enforce_for_root | (present; CIS 5.2.3.2.8) | Root's own password changes obey the rules too |

Complexity through minclass or the credit lines is CIS 5.2.3.2.3 (Level 2); the rest are Level 1.

sudo nano /etc/security/pwquality.conf

The file ships with every setting commented out and explained, which makes it a good one to read top to bottom once. For each row of the table, find the line (Ctrl+W), delete the #, and set the value:

minlen = 14
dcredit = -1

The last occurrence of a setting wins, so don't leave a second uncommented copy further down.

The hashing algorithm

grep pam_unix.so /etc/pam.d/common-password

The line should include yescrypt (the default on Mint 21 and Debian 12) or sha512 (CIS 5.2.3.4.3; the STIG accepts only sha512 because it's the FIPS-approved one, UBTU-22-611070), and use_authtok (5.2.3.4.4) so it hashes the password that pwquality and pwhistory already checked rather than asking again. It must not include nullok (5.2.3.4.1) or remember= (5.2.3.4.2; history belongs on the pwhistory line, next page). If it says md5, sha256, or has no algorithm, an attacker who copies /etc/shadow cracks it in minutes. Edit the word in place:

sudo sed -i '/pam_unix.so/ s/\b\(md5\|sha256\|des\)\b/yescrypt/' /etc/pam.d/common-password

Passwords already hashed with the weak algorithm stay weak until they're changed; the Passwords and Shells page finds them.

Verify

grep pam_pwquality /etc/pam.d/common-password
grep -E '^(minlen|dcredit|ucredit|lcredit|ocredit|minclass)' /etc/security/pwquality.conf
grep pam_unix.so /etc/pam.d/common-password
passwd     # try setting your own password to "password1" and read the refusal

Example

grep pam_pwquality /etc/pam.d/common-password prints nothing and dpkg -l libpam-pwquality shows it isn't installed. Install it, then set minlen = 14 and the four credit lines. grep pam_unix.so /etc/pam.d/common-password ends in md5; change it to yescrypt. Now sudo awk -F: '$2 ~ /^\$1\$/' /etc/shadow lists three users with MD5 hashes; reset those three passwords.

Try it

  1. Set minlen = 14 and try to give a test user a 10-character password. Read the message.
  2. Set dictcheck = 1 and try password123.

Build it

A pwquality.sh that uncomments and sets each row of the table with sed, then prints the uncommented lines. Test it on a fresh copy of the file first.

Next

Password History