Advances in macOS Security

Session 701 WWDC 2019

We are on a journey to continuously improve macOS security, with a particular focus on preventing malware and protecting user data. Join us on the next step and learn more about what’s new in Gatekeeper—for keeping malware out of macOS—as well as new protections that help keep users’ data and activity under their control.

[ Music ]

Good morning, everyone.

And thanks for joining us today.

My name is Garrett, and I work on the security engineering and architecture team here at Apple.

And today, we're here to talk about some of the advances in security coming in macOS Catalina.

Here's a quick agenda for the talk.

We're going to talk a little bit about the security principle of defense in depth and how it's applied in macOS.

Then we'll dig into two very different parts of the macOS security model, Gatekeeper and user privacy protection.

So let's get started with Defense in Depth.

Now, with a product as complex and with as many use cases as macOS, it's there is no single technology or feature that can deliver perfect security alone.

So instead, macOS is designed with many different layers of security.

Each designed with a specific purpose or goal.

And every year, we improve the technologies and policies at each of these layers to help keep you safe.

And this is an application of the principle of Defense in Depth.

And the goal is to ensure that one layer of security failing doesn't defeat the entire security model of the system.

Instead, we rely on multiple layers of protection with different properties.

Some layers are designed to delay the advance of an attacker, others to reduce the attack surface of a component, and some to create choke points that make it easier to defend a specific asset.

And today, we're here to talk about two very different layers of security in macOS.

First, we'll talk a little bit about Gatekeeper, which is an important outer layer of security, designed to prevent malicious software from ever running on your system in the first place.

Then, we'll talk about user privacy protection, which is designed to ensure that even if malicious software is able to get around Gatekeeper, it doesn't necessarily have access to your most sensitive data and resources.

So let's talk about Gatekeeper.

When Gatekeeper was originally introduced, along with the Developer ID program, its goal was to prevent the widespread prevent the outbreak, a widespread outbreak of malicious software.

But over the years, its goals have broadened a bit.

And now, it's designed to protect users from running malicious software while leaving them in control of what they run on their Mac.

Now, exactly how it does this has changed over the years too.

And while there's a lot of nuances, Gatekeeper's intent can generally be boiled down to a few simple questions.

So let's talk about how Gatekeeper achieves this goal today.

There's four things that we think of as part of a Gatekeeper scan.

The first is a malicious content scan to ensure that there's nothing known malicious in the app it's about to run.

Second, there's a signature validation, to ensure that the app hasn't been tampered with since it was signed by the developer.

Third, there's an identity validation.

And this is what we use to enforce the computer's local security policy.

If the user has selected that they only want to run software from the App Store or identify developers, then we don't want to allow something that's been signed by anyone else, or that doesn't have a signature at all.

And finally, there's a first launch prompt, which is meant to ensure that the user actually wants to run this application.

Now, we can't talk about what Gatekeeper checks without talking about when Gatekeeper checks it.

And in macOS Mojave, Gatekeeper runs its scans on the first launch of quarantined software launched via LaunchServices.

Now, to understand that a little better, we need to dig in on what quarantine means and what it means to be launched by LaunchServices.

So let's jump into that.

Quarantine is a technology built into macOS to mark files that have arrived from somewhere else off of the device.

When you download a file in a web browser or when someone sends you something via iMessages, or if you get an AirDrop, those files all get quarantined.

Additionally, macOS will add metadata to that file about where it came from.

So when we present a first launch prompt, we can give you more contexts about where that file came from.

Now, quarantine is an opt-in model, which means that apps have to opt in to quarantining the files that they place on disk.

And that generally means that when an app downloads files in the background as part of, for example, a self-update, those files generally never get quarantined.

The exception there is if the app is App Sandboxed.

Because quarantining files is the default for Sandboxed applications.

So that helps you understand a little bit more about what files on a system will get quarantined.

Now, let's talk about Launch Services.

Launch Services is a framework for finding and launching applications.

And it's generally responsible for most of the ways that you think of launching applications on your Mac.

For example, when you open an app in Finder or the Dock that will use Launch Services.

If you use the NSWorkspace APIs, those generally go through Launch Services also.

And when apps get open via document handlers or directly through URLs, that all goes through Launch Services.

Now, for the rest of the presentation, I'll use this Finder icon when I'm specifically talking about something that goes through the Launch Services path.

But there are also many ways of loading code that don't go through Launch Services.

Some examples that are not exhaustive, include using NSTask to start a process, or the exec and posix spawn's calls or loading a library into your process using the NSBundle APIs for loading or dlopen.

And for the rest of the presentation, I'll use this terminal icon when I want to refer to loading code off of the Launch Services path.

Now, let's quickly summarize everything that we've learned about Gatekeeper in a format that will make it easier to talk about what's changed this year.

So here, we can see Gatekeeper's behavior and macOS Mojave that on the first launch of quarantine software on the Launch Services path, Gatekeeper will perform a malicious content scan and a signature check to ensure that there's no known malicious content and that the application hasn't been tampered with.

Then it will perform a local policy check where the default is that it must be signed with the Developer ID certificate or by the App Store.

And finally, it will present the first launch prompt to the user, so that the user must approve the application.

Now, starting with the latest release of macOS Mojave 10.14.5, the default policy was changed slightly such that new Mac developer certificates required their software to be notarized to pass Gatekeeper, which brings us to the first improvement in macOS Catalina.

We're expanding on this policy such that all new software requires notarization.

And in this case, new means that the software was signed or built after June 1st, 2019.

This means that all existing software will continue to pass through Gatekeeper as is, with just a Developer ID certificate signature.

But that all new software must be notarized to pass Gatekeeper.

Now, the next improvement that we've made in macOS Catalina is that Gatekeeper is going to be expanding to enforce the same policy on all quarantine software.

And that means that regardless of how the software is loaded, if it's quarantined, it must contain no known malicious content, it must not be tampered with, new software will require notarization.

And the first launch policy will be slightly different, where we won't be requiring first launch user approval for standalone executables or libraries, but all bundled software will show first launch prompt.

So now, Gatekeeper has been expanded to scan and enforce policy on all quarantine software.

And that brings me to the final improvement in macOS Catalina, Gatekeeper will be further protecting you by ensuring that all software gets a malicious content scan.

And that means that regardless of whether software is quarantined, and regardless of how that code is loaded, if any known malicious content is found, it will be blocked and the user will be alerted.

So this is a summary of all the ways that Gatekeeper has been expanded to help protect you in macOS Catalina.

But it's important to keep one thing in mind.

Our goal is to make every Mac user safe by default, but not to prevent you from running the software that you want to run on your Mac.

And that means that there will always be a way to run a specific piece of software that you want to run on your system.

Now, I talked a little bit earlier about how every year we're continuously improving the technologies and policies that we have at every layer.

So I'd also like to take a minute to talk a little bit about what's next for Gatekeeper.

Now, we have a really big goal on the security engineering team.

We want to make macOS just as secure as iOS, while still maintaining all the flexibility that you've come to expect out of your Mac.

And that presents some really interesting challenges.

But one thing has become really clear, the security of the platform has become increasingly reliant on the validity of code signatures.

And this means that if an app has no signature, it's impossible to detect tampering.

And further, if a bundle signature has become broken at runtime, it's very difficult to differentiate malicious tampering from mundane tampering when in out modifies itself at runtime.

And in a future version of macOS, unsigned code will not run by default anymore.

And to get there, there's a couple of things that you can do to help improve the security of the platform.

First, sign and notarize all the software that you distribute, even if it doesn't get quarantined today.

Second, don't break application or bundle signatures at runtime.

If you do need to update an application, make sure that it results in an application on disk that is properly signed and notarized still.

And finally, remember that loading code can fail.

If you try to load a quarantine library or process and the user chooses not to run it, that will fail and ensure that your apps gracefully handle these failures.

So these are all the ways that Gatekeeper has been expanded to try to prevent malicious software from ever running on your Mac.

Now, let's bring up Kelly to talk about advancements in user privacy protection.

Kelly.

[ Applause ]

Well, thank you, Garrett.

Good morning, everyone.

My name is Kelly Yancey.

I work with Garrett in the security engineering and architecture team at Apple.

And last year at WWDC 2018, it was my privilege to introduce the new privacy protections in macOS Mojave, broadly characterized sorry.

Let's get to that.

These protections were designed to improve user's transparency over their how their data was being accessed and give them control over those accesses.

And I'm really excited to be back here today to talk about the improvements that we've made in macOS Catalina.

Now, broadly characterized, their privacy protections require user consent to access hardware that could record the users, such as the camera or microphone, or to access the user's privacy sensitive files or folders, such as photos, mail or messages.

In addition, they also protect the ability to automate other apps, so that the user is in control over how their data is being shared between apps.

I like to start today by looking at the recording capabilities.

Beginning in macOS Mojave, users have to consent before an app can access the camera or microphone.

And then macOS Catalina further requires consent to record the contents of your screen or the keys that you type on your keyboard.

And this is important because just like we don't want people shoulder surfing, looking over our shoulder to see what we're doing or are typing, we don't want apps to eavesdrop on our contact information, our bank details or passwords and so on, whether that's intentionally or accidentally.

So how do we do that?

Let's start by looking at screen recording.

Here's a simple example of using CGDisplayStream to record the contents of a display in real time.

On macOS Catalina, the first time this app runs and this call to create the CGDisplayStream is made that'll return nil, and a dialogue is displayed directing the user to the security and privacy preference pane, where the user can approve the app to record the screen if that's what they want to do.

The same is true when reading the contents of other apps windows.

For example, here's a function that saves the contents of a window to an image on disk.

Now, notably, the call to CGWindowListCreateImage can return nil, if passed the window ID that does not belong to the calling app, and doesn't belong to the desktop background image or to the menu bar.

And I like to emphasize, this is the background image, it doesn't include the icons or names of any files on the desktop.

Again, an authorization dialog, directing the user to approve the app for screen recording, may be displayed.

And I say may, because the dialog is only displayed the first time CGWindowListCreateImage or CGDisplayStream fails due to a lack of approval for screen recording.

Another topic I'd like to cover that's peripherally related to screen recording is window metadata.

Apps can query metadata about the windows that are on or off screen using the core graphics CGWindowListCopyWindowInfo function.

The metadata returned includes the size and position of the window and the unique window identifier, as well as the name and process of identifier of the app that owns the window.

However, the window name and sharing state are not available, unless the user has preapproved the app for screen recording.

And this is because some apps put sensitive data such as account names or more likely web page URLs in the window's name.

And CGWindowListCopyWindowInfo never triggers an authorization prompt, instead it filters the set of metadata that it returns to the caller.

So, if in your app you depend on getting the window name, for example, and you find that the metadata that's being returned doesn't include the window name, you may want to alert the user and direct them to the privacy security and privacy preference pane.

So, here's an example of a function that gets the unique window identifiers for the desktop background image for each display.

And once again, the background image doesn't include the icons on the desktop.

This function first gets a list of all of the windows on the screen, using the CGWindowListCopyWindowInfo function.

But then, it gets the window level or Z order that core graphics uses for the desktop background image window.

Then it filters that whole window list to just those windows at the desktop background window level.

So if you look on the internet, you'll find a lot of code samples filter by the kCG window name which, since the window names may contain privacy sensitive information, would require the app to be preapproved for screen recording.

However, by identifying the desktop background windows by their window level rather than by the window name, this works regardless of whether the user has preapproved the app for screen recording.

And this is an example of how a small change in your the design of your app can make a big difference in the user experience.

So that's how macOS Catalina protects the contents of your screen from being recorded without your permission.

Apps can freely record the contents of their own windows, the menu bar and the desktop background image.

But the user must use the security and privacy preference pane to preapprove apps to record the entire screen or the contents of windows other than their own.

Now, I'd like to present the other recording capability now protected in macOS Catalina, your keyboard.

Most users have an expectation that their keystrokes are only going to the app that they're interacting with, the front most app, and most apps only require a keyboard input when the user is actively using them.

And in fact, if your app uses the standard UI components, they handle those keyboard events that are being delivered to your app automatically.

Now, some apps want to be able to intercept those events as they're being delivered to their app.

And that's fine, they can subclass NSApplication and override the sendEvent method, or as shown here, you can use NSEvent's addLocalMonitorForEvents function.

Monitoring all keyboard events, including those for other apps, however, requires user approval.

And here you can see an example of using CGEventTapCreate to invoke a callback for key press and release events.

Now, the first time this code runs, this call, the CGEventTapCreate will fail and return nil.

Meanwhile, a dialog is displayed directing the user to the security and privacy preference pane, where the user can approve your app to monitor keyboard events in the background, if they so desire.

Now, apps may check the authorization status without triggering the approval prompt, using the IOHIDCheckAccess function with the kIOHIDRequestTypeListenEvent parameter.

And apps can request an approval dialog to be displayed without creating an event tab or trying to post an event by using the IOHIDRequestAccess function, again with the same parameter.

So in summary, macOS Catalina now requires user consent for apps to record the contents of your screen or the keys that you type on your keyboard.

Now, I'd like to turn your attention to how macOS protects access to your privacy sensitive files.

So continuing the approach in Mojave, macOS Catalina offers two broad levels of privacy protection for the user's files and folders.

First, user data that apps may generally access such as contacts or photos, and for these, macOS will confirm that the user consents before sharing that data with apps.

But second, there's also user data which for which the file system is really just an implementation detail, it's not part of their API.

And these are things like mail or messages and Safari browsing history.

And these have a high barrier to access because the files are typically only accessed by specialized apps such as disk management or backup utilities.

But first, let's talk about those files and folders that require user consent to access.

Now, macOS Mojave introduced user consent requirements to access your contacts, calendars, reminders, or your photos library via the file system.

And what that looks like is a prompt like this, when an app tries to access files in one of these categories.

Now, this is different than the authorization dialogs that we just saw for screen recording and keyboard event recording, rather than denying the access and displaying an alert to the user, the calling thread is actually blocked, waiting for the user to approve or deny the app access to files in the category.

In macOS Catalina, we complemented these categories with these additional categories for data where the file system is API.

These represent various locations where the users store their documents.

They double click on them in Finder.

They select them via Open or Save panels, and so on.

The user's desktop and document folders were prime candidates for protection since that's the default location where many users store their files.

And downloads because some apps, including Safari, save downloaded files there by default, also protecting documents stored in iCloud Drive or third-party cloud storage providers or on removable volumes and if you've been around as long as I have, you may be thinking floppy disks.

But here I mean any storage that can be physically removed, including USB thumb drives or external disks.

And as I'm sure you photographers out there can attest, some people have their entire lives chronicled on external disks, or on network-attached storage.

So, macOS Catalina is now protecting many of the most common locations where we store our files.

Now, user consent is not required for an app to create new documents in any of these protected locations, just to read existing contents, the contents of files that are already there.

So, for example, a file transfer app can continue to save new files to the user's downloads folder without triggering a consent prompt.

And user privacy protections in macOS Catalina now support the notion of user intent, when which is inferred when double clicking on files in Finder, when dragging and dropping from another application or when selecting files in an Open or Save panel.

And when the user performs any of these actions, the file performing any of these actions on a file protected location, your app gets access to the file or files that the user selected without the need for a consent prompt.

So let's see how Catalina's inference of user intent compares with user consent.

Sorry. So, first of all, user consent is reactive.

Access may be granted only after your app tries to read or write a file, whereas user intent is proactive.

Access is granted before the app, even tries to read or write the file.

And user consent prompts to kind of interrupt the user's workflow, whereas user intent is inferred from standard UI interactions.

In order to minimize those interruptions, user consent applies to an entire class of data, for example, all files on your desktop.

Whereas user intent is inferred for just the file or files that the user is interacting with.

Now, that said, the two are not mutually exclusive.

So long as your app is accessing files it created or which the user selected, no consent prompts are necessary.

But if your app accesses files in a privacy protected location, other than ones that it created itself or ones that the user has selected, the user will need to approve that access via consent prompt.

Now, one common scenario where an app might need to access a file other than one that the user selected are sidecar files.

For example, to automatically open a subtitle file that's with of the same name but next to a movie file.

So using the related items support in NSFileCoordinator, it's possible for the inferred permissions for one file to be extended to other files.

To use NSFileCoordinator to open a sidecar file, you need to first declare the extension of the sidecar file in your apps, CFBundleDocumentTypes Info.plist key.

And, set the NSIsRelatedItemType to Boolean true.

Then in your code, you subclass NSFilePresenter, set the primaryPresentedItemURL to the file that the user selected.

So that's the one that your app already has access to.

And the PresentedItemURL to the sidecar file.

That's the one that you want to access to.

And note that the sidecar file may have a different extension as the user selected file, but all other path components must be identical.

Finally, you create an NSFileCoordinator referencing the NSFilePresenter instance.

And when you call the NSFileCoordinator's coordinate method, your app gets access to the sidecar file as well for the duration of that block.

And that's a quick rundown of how your apps can use NSFileCoordinator to gain access to files with the same name, but a different filename extension as ones that the user has selected without triggering a user consent prompt.

Now, to securely infer user intent, the Open and Save panels are now always hosted out of process.

As a result, the class inheritance and view hierarchies have changed, which could affect your app if you have subclass NSOpenPanel or NSSavePanel.

And apps can no longer programmatically accept the panels by calling the OK method, the user must do that themselves.

There are also slight changes to the NSOpenSavePanelDelegate methods as well.

Apps can no longer rewrite the user selection using this method.

And accessing the files that URLs provided to these methods may trigger user consent prompts, because these methods are invoked by the panels while the user is still interacting with the panel, so they haven't selected the file yet.

So therefore, your app has not yet been granted access.

Now, apps can test whether a given file is readable or writable without triggering consent prompts using these APIs.

And, so long as your app only accesses files that the that in itself creates or that the user that it receives via file open events or drag and drop or Open panel or Save panel selections, your app's permission to access those files is inferred and no user consent prompt is necessary.

However, if a consent prompt is required, all of the new protected file system locations support purpose strings.

And purpose strings can be specified in your Info.plist to explain the context of the access when a consent prompt is displayed.

And while purpose strings for these categories are optional, if your app access is one of these protected locations, intentionally, a purpose string for that location is highly recommended so that the user understands why your app is accessing their documents.

Now, if you find in your testing your app is triggering consent prompts that you don't expect, you can click the Don't Allow button and go to the console app and look for the resulting Sandbox violation, that'll tell you the file that your app was trying to access and the backtrace that caused to require the consent prompt.

So that's how macOS Catalina is protecting user's documents and how the standard UI and how it's using standard UI interactions to infer which documents they expect apps to access.

So let's look at how macOS protects user data that's managed by the system and how your apps can request access to that data if necessary.

Here, we see the categories of data protected starting in macOS Mojave.

Now, some software such as disk management or backup software work with all files regardless of their content.

And those software those apps can use the same APIs that we just saw a minute ago to determine whether a given file is readable or writable.

And then depending on what's appropriate for the app, they could skip inaccessible pass or they could alert the user and direct them to approve the app in the security and privacy preference pane to have full functionality.

And here's where they would approve that for Full Disk Access.

And while we're here, I'd like to talk about one of the enhancements that we've made in macOS Catalina to how apps are approved for Full Disk Access, while users can still manually add apps to the list using the plus button here.

One piece of feedback that we've gotten from developers is that it can be awkward for users to locate their app's privileged helpers.

So, now in macOS Catalina, executables that are denied access to files due to a lack of Full Disk Access approval are now prepopulated unchecked.

Here, we see one such helper identified by its executable name.

If that helper were embedded in a bundle, the display name in icon specifying the bundles Info.plist would be displayed instead.

So, again, this data is accessible for apps that have been preapproved for Full Disk Access, apps can test for authorization using FileManager or POSIX level APIs, and if necessary, can guide the user to the security and privacy preference pane, where the user can then approve the app if that's what they want.

Now, in Catalina, the universe of data that requires preapproval for Full Disk Access has been expanded to include trash.

Now, a lot of people think of files as being gone when they move them to their trash.

So the last thing they would expect is for something to go digging through their trash files.

That's terrifying, at this size.

As with these other categories, trash may contain lots of privacy sensitive data.

However, unlike these other categories, trash is file centric and does have API for manipulating those files.

APIs like these, which move files into the user's trash.

Now, I'd like to drill down on the FileManager trash item API a little.

It takes a URL of the file to move to the trash as it's argument.

Now, the caller needs to already have access to that file, you can't move files to the trash that you're you don't have access to yourself.

But when successful, it populates the out resulting URL parameter, with an NSURL for the file at its new location in the user's trash.

And it still has access to that URL.

To make sense, it had access to the file before it moved it.

It still has access to the file after it moved it.

And this allows you to use FileManager APIs, for example, to move the file back out of the trash.

So in summary, while apps require Full Disk Access to enumerate the files in the trash or to see those files contents, no authorization is required to move files into the trash or to access files that they had previously put in the trash.

Finally, I like to talk briefly about automation.

macOS Mojave introduced consent requirements for automation of the system or of other apps.

And this is important to prevent malware from abusing apps that you've trusted with your data.

So, first, there's synthetic events.

And synthetic input events are typically used by accessibility software in providing aid for keyboard or mouse input.

But because user consent dialogs, user intent inference or various other security prompts all rely on user input, it's important that synthetic input events are only allowed from apps that the user has installed for the purpose of acting as their proxy.

Here's a sample code Here's an example of code that simulates a key press and key release.

The first time this code runs and tries to post these events as if they were actually typed by the user, the events are discarded.

And a dialog like this one is displayed alerting the user that they will need to go to the security and privacy preference pane to authorize the app for accessibility features.

Now, earlier, we looked at this sample code for listening to keyboard events.

If I change the listenOnly parameter to defaultTap, like that, CGEventTapCreate now creates a modifying event tab, where the callback can alter the event stream.

And this means now your app has a way to influence what events are delivered to the rest of the system, where a listen-only event requires authorization for input monitoring, a modifying event app requires authorization for accessibility features.

Now, apps can test whether the user has approved the app to synthesize local synthesize input events using the IOHIDCheckAccess function.

And this is the same API for checking authorization for keyboard input monitoring.

But, here you can see that we're passing the kIOHIDRequestTypePostEvent instead.

So, that was automation via synthetic events.

Now, let's talk about automating apps via Apple Events.

So, the user must consent before one app can use AppleScript or raw Apple Events to control the actions of another app.

These consent prompts make it clear, which apps are acting under the influence of which other apps and give the user control over that automation.

Now, there are exceptions for Apple Events that don't give the sending process access to privacy sensitive data.

Many of these events are exposed via NSWorkspace APIs.

The AEDeterminePermissionToAutomate Target function can be used to test whether authorization would be required to send Apple Events to a target application.

And here's an example, testing whether the caller can send any Apple event to Keynote.

And by passing Boolean true for the askUserIfNeeded parameter, you could ask that a approval prompt is triggered if necessary.

But I will point out that if a prompt is displayed, the calling thread is blocked waiting for the user's interaction, so you wouldn't want to call this on your app's main thread.

Now, this API is pretty low level, returning an OSStatus code indicating whether the caller is permitted to send the Apple event to the target or not, whether trying to send the Apple event would result in a prompt confirming the user's consent, or whether the target is not running at the moment and that trying to send the Apple even would cause it to launch, or whether some error occurred.

So that was a quick summary of how macOS captures user consent before allowing apps to automate other apps, and how your apps can determine whether consent was given and adjust accordingly.

So, now, in macOS Catalina, user consent is required to record your screen or keyboard, that's an addition to the existing protections for camera and microphone.

And now, a number of common location common document locations are also protected such as user's desktop documents downloads, their iCloud Drive or third-party storage, removable network volumes and, of course, trash.

Now, the Privacy Preferences Policy Control MDM payload has also been extended with services for the new protected resources in macOS Catalina.

And, I'd like to point out that during development, you may want to trigger be able to retrigger prompts while you're testing your app behavior.

And you can use these same service names that you see on the left side, with a tccutil command line tool to reset the prompting status for the respective protected resources.

So, earlier, we heard from Garrett about the enhancements in Gatekeeper, and we just discussed some of the improvements in the user privacy protections in macOS Catalina.

I'd like to recap by saying, remember to sign and notarize all of the software that you distribute.

And, don't modify those bundles once signed.

If you do need to modify the bundles, be sure that you just transform them into another bundle that is also signed.

And, for user privacy protections, try to leverage standard UI as much as possible.

Be sure to handle errors and APIs that may return, if the user sorry, handle any errors, APIs may return if the user declines consent.

And remember that once users authorize your app to access their personal data, it's the responsibility to protect their privacy passes to you.

So handle user's data with care.

Thank you very much.

I hope you enjoy the rest of the week here at WWDC.

There's a security lab immediately afterwards if you have any questions about these or any other of the privacy or security protections in macOS.

Thank you very much.

[ Applause ]

Apple, Inc. AAPL
1 Infinite Loop Cupertino CA 95014 US