Skip to content

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.

Published on 6 min read

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:

CapabilityStandard userAdministrator
Run and install apps into ~/ApplicationsYesYes
Install apps into /ApplicationsOnly if the folder permissions allow itYes
Change most System Settings panesNo, prompts for admin credentialsYes
Use sudoNo (not in sudoers by default)Yes
Approve system extensions and many privacy promptsNoYes
Install configuration profiles manuallyNoYes

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 admin group 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=0 makes every sudo call ask again. A small value, in minutes, is a common compromise.
  • timestamp_type=tty keeps 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:

SettingProfile domain / keyTypical value
Require password after screen savercom.apple.screensaver / askForPasswordtrue
Grace period before password is requiredcom.apple.screensaver / askForPasswordDelay0 seconds
Screen saver idle timecom.apple.screensaver / idleTimeper 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 with visudo, and pass visudo -c.
  • No unjustified NOPASSWD rules.
  • 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/sudo directly. macOS updates can overwrite it. Use sudo_local.
  • Long sudo timestamps plus admin daily use. Together they let any process in the session run sudo silently 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. sudo use is visible in the unified log. The logging guide shows how to watch for it.