macOS Accounts, sudo and Least Privilege: Hardening Guide
Run macOS as a standard user, harden sudoers, enable Touch ID for sudo safely, disable guest and auto-login, and enforce password and screen lock policy.
Most macOS compromises need the user to do something: open a file, approve a prompt, type a password. How much damage follows depends a lot on how much privilege that user has. Least privilege on macOS means separating everyday work from administration, making sudo harder to abuse, closing unauthenticated entry points such as the guest account and automatic login, and enforcing password and screen lock policy. This guide covers each of these for macOS 15 Sequoia and macOS 26 Tahoe.
Standard vs admin accounts
macOS has two main local account types that matter for hardening:
| Capability | Standard user | Administrator |
|---|---|---|
Run and install apps into ~/Applications | Yes | Yes |
Install apps into /Applications | Only if the folder permissions allow it | Yes |
| Change most System Settings panes | No, prompts for admin credentials | Yes |
Use sudo | No (not in sudoers by default) | Yes |
| Approve system extensions and many privacy prompts | No | Yes |
| Install configuration profiles manually | No | Yes |
Admins are members of the local admin group. Check membership:
# Which groups is the current user in?
id -Gn
# Is a specific user an admin?
dseditgroup -o checkmember -m alice admin
# List members of the admin group
dscl . -read /Groups/admin GroupMembership
Daily driver as a standard user
Use a standard account for daily work. When an admin task comes up, macOS asks for an administrator's name and password and elevates only that action. Malware running in the user session then has no admin rights to lean on, and an accidental sudo rm is impossible.
To demote an existing user, first make sure another admin account exists and works, then:
sudo dseditgroup -o edit -d alice -t user admin
On Apple silicon, keep in mind the volume owner concept. Some operations, such as installing macOS updates or changing startup security, require a user who holds a Secure Token and is a volume owner. Check before demoting anyone:
sysadminctl -secureTokenStatus alice
sudo fdesetup list
The FileVault guide explains how Secure Token, volume ownership and FileVault unlock relate.
Just-in-time admin and Platform SSO
In managed fleets, a permanent second admin account per user is hard to control. Two common patterns:
- Just-in-time elevation: a tool temporarily adds the user to the
admingroup for a set time, logs the reason, and removes the membership afterwards. The open-source "Privileges" app from SAP is a well-known example. Several commercial endpoint management products offer the same idea. - Platform SSO: introduced in macOS 13 and extended in later releases. It links the local account to an identity provider through an SSO extension. Depending on the IdP and MDM, it can create local accounts at login and set whether they are standard or admin based on directory group membership. Apple's Platform Deployment documentation describes what your macOS version and IdP support.
Whichever you choose, the goal is the same: admin rights should be short-lived, attributable and logged.
Hardening sudo
Edit sudoers safely
Never edit /etc/sudoers with a plain editor. visudo locks the file and checks syntax before saving. A broken sudoers file can lock every admin out of sudo. Put your changes in a drop-in file under /etc/sudoers.d/, which the macOS sudoers already includes:
sudo visudo -f /etc/sudoers.d/10-hardening
# Always require the password (no cached credentials)
Defaults timestamp_timeout=0
# If caching is allowed, scope it to the terminal session
Defaults timestamp_type=tty
Notes:
timestamp_timeout=0makes everysudocall ask again. A small value, in minutes, is a common compromise.timestamp_type=ttykeeps an authentication in one Terminal tab from carrying over to another.- Drop-in files must be owned by root and not writable by others. sudo ignores file names that contain a
.or end in~.
Validate the whole configuration after any change:
sudo visudo -c
Review who can do what
# What may the current user run?
sudo -l
# Look for NOPASSWD or broad grants in drop-ins
sudo grep -R "NOPASSWD" /etc/sudoers /etc/sudoers.d/
Management agents and developer tooling sometimes install NOPASSWD rules. Each one should be justified and scoped to a specific command, never ALL.
Touch ID for sudo
Since macOS 14 Sonoma, Apple ships /etc/pam.d/sudo_local.template. A sudo_local file created from it survives macOS updates, unlike changes made directly to /etc/pam.d/sudo:
sed "s/^#auth/auth/" /etc/pam.d/sudo_local.template | sudo tee /etc/pam.d/sudo_local
The resulting active line is:
auth sufficient pam_tid.so
Things to know:
- It is a convenience feature. It makes frequent re-authentication, such as
timestamp_timeout=0, less painful, which may be its best security argument. - It does not work over SSH, and it does not work with the lid closed unless a Touch ID keyboard is attached. The password prompt remains as the fallback.
- Terminal multiplexers such as tmux may need extra helpers to reach Touch ID. Don't weaken PAM configuration to work around that.
Close unauthenticated entry points
Guest account
The guest account allows a passwordless login. It should be off:
sudo sysadminctl -guestAccount status
sudo sysadminctl -guestAccount off
With MDM, enforce it through the com.apple.MCX payload with DisableGuestAccount set to true and EnableGuestAccount set to false.
Automatic login
Automatic login skips the login window at boot. It is not possible while FileVault is on, but it can be set on Macs without FileVault, and it should never be. Check:
defaults read /Library/Preferences/com.apple.loginwindow autoLoginUser
An error saying the key does not exist means auto-login is off. With MDM, set com.apple.login.mcx.DisableAutoLoginClient to true in a com.apple.loginwindow payload.
Root account
The root user is disabled by default on macOS, and it should stay that way. If someone enabled it through Directory Utility or dsenableroot, disable it:
dsenableroot -d
You can inspect the root record with dscl . -read /Users/root AuthenticationAuthority. A ShadowHash entry there suggests a password has been set for root.
Password and screen lock policy
Password policy
Local password policy can be read with pwpolicy:
sudo pwpolicy -getaccountpolicies
The output is an XML property list of policy dictionaries. For fleets, set policy with a configuration profile rather than per-machine pwpolicy edits. The passcode payload (com.apple.mobiledevice.passwordpolicy) supports keys such as:
<dict>
<key>PayloadType</key>
<string>com.apple.mobiledevice.passwordpolicy</string>
<key>PayloadIdentifier</key>
<string>com.example.baseline.passcode</string>
<key>PayloadUUID</key>
<string>REPLACE-WITH-UUID</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>minLength</key>
<integer>14</integer>
<key>requireAlphanumeric</key>
<true/>
<key>maxFailedAttempts</key>
<integer>10</integer>
<key>pinHistory</key>
<integer>5</integer>
</dict>
Pick values from your own policy or benchmark. The benchmarks guide shows how mSCP generates these profiles from a baseline. Current guidance generally favors long passphrases over forced periodic rotation. Consider this before setting an expiry.
Screen lock
An unlocked, unattended Mac bypasses every other control. Require a password right after the screen saver or sleep starts, and set an idle timeout:
| Setting | Profile domain / key | Typical value |
|---|---|---|
| Require password after screen saver | com.apple.screensaver / askForPassword | true |
| Grace period before password is required | com.apple.screensaver / askForPasswordDelay | 0 seconds |
| Screen saver idle time | com.apple.screensaver / idleTime | per policy, e.g. 600 or less |
Teach users to lock manually with Control-Command-Q.
Verify it
id -Gn # daily user should not list "admin"
dscl . -read /Groups/admin GroupMembership # only expected admins
sudo visudo -c # sudoers parses cleanly
sudo cat /etc/sudoers.d/10-hardening # timestamp settings present
cat /etc/pam.d/sudo_local # pam_tid.so line if Touch ID sudo is used
sudo sysadminctl -guestAccount status # guest disabled
defaults read /Library/Preferences/com.apple.loginwindow autoLoginUser # should not exist
sudo pwpolicy -getaccountpolicies # policy present
sudo profiles show # passcode / loginwindow / screensaver payloads installed
Checklist
- Daily account is a standard user. Admin rights are separate or granted just in time.
- At least one known-good admin with a Secure Token exists before anyone is demoted.
- sudoers changes live in
/etc/sudoers.d/, made withvisudo, and passvisudo -c. - No unjustified
NOPASSWDrules. - Touch ID for sudo, if used, is configured through
sudo_local, not by editing/etc/pam.d/sudo. - Guest account and automatic login are off and enforced by profile.
- Root account is disabled.
- Password and screen lock policy are delivered by profile.
- FileVault is on, and privacy permissions are reviewed per the TCC guide.
Common pitfalls
- Demoting the only admin. Always confirm another working admin exists first, ideally one with a Secure Token.
- Editing
/etc/pam.d/sudodirectly. macOS updates can overwrite it. Usesudo_local. - Long sudo timestamps plus admin daily use. Together they let any process in the session run
sudosilently for minutes. - Profiles that conflict. Two passcode payloads from different sources can merge in unexpected ways. Keep one source of truth in your MDM.
- Ignoring the logs.
sudouse is visible in the unified log. The logging guide shows how to watch for it.