Mac OS X,  Mac OS X Server,  Mac Security

Secure Keyboard Entry on macOS

The Secure Keyboard Entry option has been in Terminal going back almost a decade. Secure Keyboard Entry was added as EnableSecureEventInput way back in Mac OS X 10.3 and was developed to protect the more sensitive inputs people provided, so also made into a public API. It was meant to protect the more sensitive types of data so if we had a login screen with a password field or something else, we’d protect those with it. The purpose was to lock what other processes could use the GetKeys function (once used to write keystroke loggers), tap the IOHIDDeviceInterace and IOHIDOptionsTypeSeizeDevice processes or tap any events that involved any HID system drivers or processes that could access keyboard events. There were valid reasons to do many of those tasks but those have largely fallen away with the advent of System Integrity Protection (SIP) and hardened runtimes.

With SIP, we can safely assume that provided that SIP is enabled a process cannot invade another space because they cannot escalate their privileges to root (yes, I’m aware PIDs aren’t supposed to be actors in a sentence, but bear with me here and don’t be so pedantic about it or I’ll add spelling errors in the future, just for fun). The rootless feature of SIP is important to these kinds of conversations as a root login allows any processes to see, change, and hook into any other process, including the kernel, where it can stream raw data from a keyboard (after all, the kernel has to watch the keyboard or these words I’m typing wouldn’t show up on my screen). By default, the kernel forces process separation when routing events. EnableSecureEventInput is a user process and not in the kernel, so these events can be allowed per app.

Most valid uses to interact with the keyboard processes are now for Accessibility features. There are cases where instrumentation frameworks get exceptions or entitlements, where a process can run with enough privileges (given the right extension/entitlement), and of course SIP can get disabled. Further, each terminal session is a unique teletype instance, going back to the good old days of timesharing. We can then inspect events from ttys001 to ttywf. We can cat (e.g. cat /dev/ttys003 for an open session with this option disabled to see the keystrokes on the other teletype session or invoke screen and > the output in one to a file to cat it later) those or even cat them and write them back into the original shell so the user doesn’t notice that they are being intercepted (except that the shell becomes less responsive). This is blocked with the Secure Keyboard Entry option as well as the much lower level techniques discussed earlier.

Many advocate to still force this option to enabled, especially given that the secrets we put into the Terminal app include administrative passwords, keys, etc. The Secure Keyboard Entry should be on by default for most machines, although I’ve now seen a lot of computers that have been upgraded for a decade and never had it toggled on. It’s simple enough to check or enable manually, just open Terminal and look in the Terminal menu.

It’s also simple to enable with a script. The defaults domain for com.apple.terminal has a SecureKeyboardEntry.

defaults write com.apple.terminal SecureKeyboardEntry -bool true