Debian GNU/Linux · Accounts & authentication

Refuse empty passwords and hash passwords with a strong algorithm in pam_unix

When it fails: pam_unix accepts accounts with an empty password. An account whose password field is empty can be logged into without any password.

CB-DEB-AUTH-013highactiveautomatedautomated fix, lockout riskprofile core

Description

pam_unix checks local passwords against /etc/shadow. Two options matter here:

  • nullok lets accounts with an empty password field authenticate with no password. Debian sets it in the default unix profile;
  • the hashing method used when a password is changed (yescrypt on Debian 12 and 13, or sha512). Older methods (md5, bigcrypt, DES) are fast to crack.

The packaged unix profile is replaced by a copy without nullok, so package upgrades do not bring the option back. Passwords keep their current hash until they are changed.

Rationale

An empty password field, created by mistake or by a tool, becomes an open account as long as nullok is present. A weak hashing method lets an attacker who copies /etc/shadow recover passwords quickly.

Impact

Accounts with an empty password can no longer log in with a password. Give them a password, or lock them with passwd -l.

Setting

Location/usr/share/pam-configs/cirsec_unix
Defaultnullok, yescrypt
RecommendedNo nullok, yescrypt or sha512

Audit

  1. No pam_unix.so line in /etc/pam.d/common-auth or /etc/pam.d/common-password contains nullok.
  2. The pam_unix.so line of /etc/pam.d/common-password contains yescrypt or sha512.

Automated: every check must pass. Scanners report each check by its id.

no-nullok-auth

path: /etc/pam.d/common-auth
pattern: pam_unix\.so.*\bnullok\b
expect: no_match

no-nullok-password

path: /etc/pam.d/common-password
pattern: pam_unix\.so.*\bnullok\b
expect: no_match

strong-hash

path: /etc/pam.d/common-password
pattern: ^\s*password\s+.*pam_unix\.so.*\b(yescrypt|sha512)\b

Remediation

  1. Lock or set a password for every account with an empty field: awk -F: '$2==""' /etc/shadow.
  2. Copy /usr/share/pam-configs/unix to /usr/share/pam-configs/cirsec_unix, remove every nullok, change Name: and make sure the Password: lines carry yescrypt.
  3. Run pam-auth-update --enable cirsec_unix --disable unix.
  4. Check that a new login still works before closing your session.
Restartnone
Disruptionlow
Lockout riskyes: can cut off administrative access. Off by default in generated scripts and playbooks, opt in through a tailoring file.

Rollback

Run pam-auth-update --enable unix --remove cirsec_unix and delete the profile file.

profile

p=/usr/share/pam-configs/cirsec_unix
sed -e 's/^Name: .*/Name: CIRSEC - Unix authentication without nullok/' \
    -e 's/[[:space:]]nullok\b//g' /usr/share/pam-configs/unix >"$p"
grep -q yescrypt "$p" || grep -q sha512 "$p" || sed -i 's/\(pam_unix\.so obscure\)/\1 yescrypt/' "$p"
for f in /etc/pam.d/common-auth /etc/pam.d/common-password /etc/pam.d/common-account /etc/pam.d/common-session; do cb_backup "$f"; done
DEBIAN_FRONTEND=noninteractive pam-auth-update --enable cirsec_unix --disable unix

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

FrameworkIDRelationNote
ANSSI-BP-028 - Recommandations de configuration d'un système GNU/Linux2.0R68partialR68 is about protecting stored passwords; this control covers the hashing method.
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-18 by Sn0wAlice on 12, Proxmox VM, Debian 12 genericcloud image, kernel 6.1.0-54-cloud-amd64. nullok removed through the cirsec_unix profile; login with password still works (pamtester).
Verified2026-10-18 by Sn0wAlice on 13, Proxmox VM, Debian 13 genericcloud image, kernel 6.12.113+deb13-cloud-amd64. nullok removed through the cirsec_unix profile; login with password still works (pamtester).
Introduced inv0.1.0
Created, updated2026-10-18, 2026-10-18
Translationsfr