Description
pam_pwhistory (in libpam-modules) keeps the hashes of each user's last passwords in
/etc/security/opasswd and refuses a new password that matches one of the last remember.
Debian ships the module but does not enable it; a pam-auth-update profile adds it to the
password stack, after the quality check and before pam_unix.
Rationale
When a password is changed after a suspected compromise, going back to the previous one undoes the change. Remembering recent passwords also stops users from cycling between two values.
Impact
Users cannot reuse their last {{ remember }} passwords. The history file grows by one hash per user and change.
Setting
| Location | /usr/share/pam-configs/cirsec_pwhistory |
| Default | Not enabled |
| Recommended | remember={{ remember }}, enforce_for_root, use_authtok |
Parameters
| Parameter | Core | Server | Server hardened | Desktop | Desktop hardened | Bounds |
|---|---|---|---|---|---|---|
rememberinteger. Number of previous passwords that cannot be reused. | 24 | 24 | 24 | 24 | 24 | 5 to 400 |
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
/etc/pam.d/common-password must contain a pam_pwhistory.so line with remember= of at
least {{ remember }}, enforce_for_root and use_authtok, placed before pam_unix.so.
Automated: every check must pass. Scanners report each check by its id.
module
pam_pwhistory line and its options in common-password.
script: |
l=$(grep -E '^[[:space:]]*password[[:space:]].*pam_pwhistory\.so' /etc/pam.d/common-password) \
|| { echo "pam_pwhistory not enabled"; exit 1; }
echo "$l"
r=$(printf '%s\n' "$l" | sed -n 's/.*remember=\([0-9]*\).*/\1/p')
[ "${r:-0}" -ge "$CB_REMEMBER" ] && printf '%s\n' "$l" | grep -q enforce_for_root && printf '%s\n' "$l" | grep -q use_authtok
Remediation
- Create
/usr/share/pam-configs/cirsec_pwhistory:Name: CIRSEC - password history (pam_pwhistory) Default: yes Priority: 1000 Password-Type: Primary Password: requisite pam_pwhistory.so remember={{ remember }} enforce_for_root try_first_pass use_authtok - Run
pam-auth-update --enable cirsec_pwhistory.
| Restart | none |
| Disruption | low |
Rollback
Run pam-auth-update --remove cirsec_pwhistory and delete the profile file.
configure
p=/usr/share/pam-configs/cirsec_pwhistory
printf '%s\n' 'Name: CIRSEC - password history (pam_pwhistory)' 'Default: yes' 'Priority: 1000' \
'Password-Type: Primary' 'Password:' \
" requisite pam_pwhistory.so remember=$CB_REMEMBER enforce_for_root try_first_pass use_authtok" >"$p"
cb_backup /etc/pam.d/common-password
DEBIAN_FRONTEND=noninteractive pam-auth-update --enable cirsec_pwhistory
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_pwhistory(8), Debian manual pages Debian, vendor-doc, accessed 2026-10-18
- pam-auth-update(8), Debian manual pages Debian, vendor-doc, accessed 2026-10-18
Mappings
| Framework | ID | Relation |
|---|---|---|
| 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 |
Related controls
- related
CB-DEB-AUTH-010Enforce password quality with pam_pwquality
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. Reuse of the last password rejected with pamtester. |
| Verified | 2026-10-18 by Sn0wAlice on 13, Proxmox VM, Debian 13 genericcloud image, kernel 6.12.113+deb13-cloud-amd64. Reuse of the last password rejected with pamtester. |
| Introduced in | v0.1.0 |
| Created, updated | 2026-10-18, 2026-10-18 |
| Translations | fr |