Description
pam_pwquality (package libpam-pwquality) checks every new password when it is set with
passwd. Its settings live in /etc/security/pwquality.conf and /etc/security/pwquality.conf.d/:
minlen: minimum length;minclass: minimum number of character classes (lower, upper, digit, other);difok: characters that must differ from the old password;maxrepeat/maxsequence: longest run of the same or consecutive characters;dictcheck: reject words from the cracklib dictionary;enforce_for_root: apply the rules when root sets a password too.
Without libpam-pwquality, Debian only applies the weak built-in obscure check of pam_unix.
Installing the package enables the module in /etc/pam.d/common-password through pam-auth-update.
Rationale
Length is what makes a password resist guessing. The other rules reject the patterns attackers try first (repeated characters, sequences, dictionary words, a one-character change).
Impact
Users must pick passwords that follow the rules when they next change them. Existing passwords are not checked.
Setting
| Location | /etc/security/pwquality.conf.d/60-cirsec.conf |
| Default | Module not installed |
| Recommended | minlen >= {{ minlen }}, minclass >= {{ minclass }}, difok >= 2, maxrepeat <= 3, maxsequence <= 3, dictcheck, enforce_for_root |
Parameters
| Parameter | Core | Server | Server hardened | Desktop | Desktop hardened | Bounds |
|---|---|---|---|---|---|---|
minleninteger. Minimum password length. | 14 | 14 | 16 | 14 | 16 | 8 to 64 |
minclassinteger. Minimum number of character classes. | 3 | 3 | 3 | 3 | 3 | 0 to 4 |
The value used is your override (tailoring file), else the profile's, else the one of the profile it extends, else the default. Bounds limit what an override may set.
Audit
dpkg -s libpam-pwqualitymust show the package installed./etc/pam.d/common-passwordmust contain apam_pwquality.soline.- The last value of each setting in
/etc/security/pwquality.confthen/etc/security/pwquality.conf.d/*.confmust be:minlen>= {{ minlen }},minclass>= {{ minclass }},difok>= 2,maxrepeatandmaxsequencebetween 1 and 3,dictchecknot 0, andenforce_for_rootpresent.
Automated: every check must pass. Scanners report each check by its id.
package
name: libpam-pwquality
state: installed
module
pam_pwquality is in the password stack.
path: /etc/pam.d/common-password
pattern: ^\s*password\s+.*pam_pwquality\.so
settings
Effective pwquality settings.
script: |
files="/etc/security/pwquality.conf $(ls /etc/security/pwquality.conf.d/*.conf 2>/dev/null)"
get() { cat $files 2>/dev/null | sed -n "s/^[[:space:]]*$1[[:space:]]*=[[:space:]]*\([-0-9]*\).*/\1/p" | tail -n 1; }
minlen=$(get minlen) minclass=$(get minclass) difok=$(get difok)
maxrepeat=$(get maxrepeat) maxsequence=$(get maxsequence) dictcheck=$(get dictcheck)
root=$(cat $files 2>/dev/null | grep -c '^[[:space:]]*enforce_for_root')
echo "minlen=${minlen:-9} minclass=${minclass:-0} difok=${difok:-1} maxrepeat=${maxrepeat:-0} maxsequence=${maxsequence:-0} dictcheck=${dictcheck:-1} enforce_for_root=$root"
[ "${minlen:-9}" -ge "$CB_MINLEN" ] && [ "${minclass:-0}" -ge "$CB_MINCLASS" ] && [ "${difok:-1}" -ge 2 ] \
&& [ "${maxrepeat:-0}" -ge 1 ] && [ "${maxrepeat:-0}" -le 3 ] \
&& [ "${maxsequence:-0}" -ge 1 ] && [ "${maxsequence:-0}" -le 3 ] \
&& [ "${dictcheck:-1}" != 0 ] && [ "$root" -gt 0 ]
Remediation
- Run
apt install libpam-pwquality. - Create
/etc/security/pwquality.conf.d/60-cirsec.conf:minlen = {{ minlen }} minclass = {{ minclass }} difok = 2 maxrepeat = 3 maxsequence = 3 dictcheck = 1 enforce_for_root
| Restart | none |
| Disruption | low |
Rollback
Delete /etc/security/pwquality.conf.d/60-cirsec.conf. Remove the package to drop the module.
package
Generated from check package: the engine writes the fix for every output, it sets the expected value and the check runs again after.
settings
f=/etc/security/pwquality.conf.d/60-cirsec.conf
cb_backup "$f"
mkdir -p /etc/security/pwquality.conf.d
printf '%s\n' '# Written by CIRSEC Baselines CB-DEB-AUTH-010' "minlen = $CB_MINLEN" "minclass = $CB_MINCLASS" \
'difok = 2' 'maxrepeat = 3' 'maxsequence = 3' 'dictcheck = 1' 'enforce_for_root' >"$f"
chmod 644 "$f"
Handwritten code reads parameters from CB_<NAME> environment variables. Each release turns these fixes into remediate.sh and an Ansible role per profile: dry run by default, a fix runs only when its check fails.
References
- pam_pwquality(8), Debian manual pages Debian, vendor-doc, accessed 2026-10-18
- pwquality.conf(5), Debian manual pages Debian, vendor-doc, accessed 2026-10-18
- NIST SP 800-63B - Digital Identity Guidelines, Authentication NIST, standard, accessed 2026-10-18
- ANSSI-BP-028 v2.0 - Recommandations de configuration d'un système GNU/Linux (R31) ANSSI, guide, accessed 2026-10-18
Mappings
| Framework | ID | Relation |
|---|---|---|
| ANSSI-BP-028 - Recommandations de configuration d'un système GNU/Linux2.0 | R31 | equivalent |
| NIST SP 800-53 Rev. 55.1.1 | IA-5(1) | partial |
How this control relates to the other framework's: equivalent, stricter, looser, partial or related.
Threats mitigated
| Taxonomy | ID |
|---|---|
| MITRE ATT&CK | T1110 |
People and lifecycle
| Authors |
|
| Verified | 2026-10-18 by Sn0wAlice on 12, Proxmox VM, Debian 12 genericcloud image, kernel 6.1.0-54-cloud-amd64. Weak password rejected and strong one accepted with pamtester; minlen 14 core, 16 hardened. |
| Verified | 2026-10-18 by Sn0wAlice on 13, Proxmox VM, Debian 13 genericcloud image, kernel 6.12.113+deb13-cloud-amd64. Weak password rejected and strong one accepted with pamtester; minlen 14 core, 16 hardened. |
| Introduced in | v0.1.0 |
| Created, updated | 2026-10-18, 2026-10-18 |
| Translations | fr |