Ubuntu · Accounts & authentication

Prevent reuse of recent passwords

When it fails: Users can set a password they used before. Users forced to change a password can switch back to the old one, which may already be known to an attacker.

CB-UBU-AUTH-012lowactiveautomatedautomated fixprofile core

As released in v0.1.2 (beta). Current state.

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. Ubuntu 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.

The options are set on the pam_pwhistory.so line, which works on every Ubuntu release. /etc/security/pwhistory.conf only exists from Ubuntu 24.04 (PAM 1.5); options on the module line take precedence over it. The history must be kept by pam_pwhistory only: the older remember= option of pam_unix stores it in a different way and must not be used.

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
DefaultNot enabled
Recommendedremember={{ remember }}, enforce_for_root, use_authtok

Parameters

ParameterCoreServerServer hardenedDesktopDesktop hardenedBounds
rememberinteger. Number of previous passwords that cannot be reused.24242424245 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. No pam_unix.so line of /etc/pam.d/common-* has a remember= option.

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

no-unix-remember

pam_unix does not keep its own password history.

path: /etc/pam.d/common-*
pattern: ^[^#]*pam_unix\.so.*\sremember=
expect: no_match

Remediation

  1. 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
    
  2. Run pam-auth-update --enable cirsec_pwhistory.
  3. If a pam_unix.so line has remember=, remove it from the pam-auth-update profile that adds it (or from the file edited by hand), then run pam-auth-update again.
Restartnone
Disruptionlow

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

Mappings

FrameworkIDRelation
NIST SP 800-53 Rev. 55.1.1IA-5(1)partial

How this control relates to the other framework's: equivalent, stricter, looser, partial or related.

Threats mitigated

People and lifecycle

Authors
Verified2026-10-19 by Sn0wAlice on 20.04, Proxmox VM, Ubuntu 20.04 cloud image, kernel 5.4.0-216-generic. Reuse of the previous password rejected with pamtester; no remember on pam_unix; kept after reboot.
Verified2026-10-19 by Sn0wAlice on 22.04, Proxmox VM, Ubuntu 22.04 cloud image, kernel 5.15.0-198-generic. Reuse of the previous password rejected with pamtester; no remember on pam_unix; kept after reboot.
Verified2026-10-19 by Sn0wAlice on 24.04, Proxmox VM, Ubuntu 24.04 cloud image, kernel 6.8.0-147-generic. Reuse of the previous password rejected with pamtester; no remember on pam_unix; kept after reboot.
Verified2026-10-19 by Sn0wAlice on 26.04, Proxmox VM, Ubuntu 26.04 cloud image, kernel 7.0.0-38-generic. Reuse of the previous password rejected with pamtester; no remember on pam_unix; kept after reboot.
Introduced inv0.1.0
Created, updated2026-10-19, 2026-10-19
Translationsfr