Blog Post

Privacy Manifests and Required Reason APIs on iOS

Illustration: Privacy Manifests and Required Reason APIs on iOS

At WWDC 2023, Apple announced upcoming requirements for iOS apps and SDKs to include privacy manifests beginning 1 May 2024.

A privacy manifest is a property list file that declares the privacy practices of an app or SDK, such as if the software collects any data, how this data is used, and whether the data is linked to individual users. This information allows app developers bundling third-party SDKs to show the app’s privacy details on the App Store. The WWDC session on privacy manifests covers this in greater detail.

With the release of PSPDFKit 13.3 for iOS, we added privacy manifests to our frameworks. In this blog post, we’ll discuss the approach we took for adding the necessary details.

Privacy Tracking

The best way to get started with a manifest file is to add details, including whether the app or SDK tracks any kind of user data, along with the domains with which the apps communicate for tracking purposes. These are specified in the manifest file against the NSPrivacyTracking and NSPrivacyTrackingDomains keys, respectively.

If your app collects any kind of data, then you need to also describe the kind of data collected, along with its usage.

Use of Required Reason APIs

The next step is to list all the required reason APIs Apple has outlined as necessary in the privacy manifest files. This is because, according to Apple documentation, “some APIs that your app uses to deliver its core functionality…have the potential of being misused to access device signals to try to identify the device or user, also known as fingerprinting.”

If any of these APIs are used for reasons not listed, developers are required to fill out a form detailing their APIs and use cases and requesting a new approved reason.

For us, the task of tracking down the usage of the required reason APIs and ensuring they adhere to the guidelines was challenging. This is because PSPDFKit for iOS is a huge SDK with a codebase dating back to 2011, and it was necessary to review each of the listed APIs individually.

We began with text search, which gave us promising results. You can use the regular expression below to search for these APIs in your project. This will find most occurrences and give you a good starting point to audit their usage:

creationDate|modificationDate|fileModificationDate|contentModificationDateKey|creationDateKey|getattrlist|getattrlistbulk|fgetattrlist|fstat|fstatat|lstat|getattrlistat|systemUptime|mach_absolute_time|volumeAvailableCapacityKey|volumeAvailableCapacityForImportantUsageKey|volumeAvailableCapacityForOpportunisticUsageKey|volumeTotalCapacityKey|systemFreeSize|systemSize|statfs|statvfs|fstatfs|fstatvfs|getattrlist|fgetattrlist|getattrlistat|activeInputModes|UserDefaults
Information

The regex above isn’t exhaustive, as it doesn’t include the stat API. That’s because searching for stat not only turns up required reason APIs, but it also has unrelated false positives, such as status functions or properties with the word status in them. As such, it’s better to manually search for stat.

Using our regex search results, we looked into the exact use of each of these APIs, and we tried to determine if there was a different way to achieve the same thing they did without using required reason APIs.

Tracking Down the Usage

Here are three examples of APIs we found we were using and what we did to comply with Apple guidelines.

ProcessInfo.systemUptime Usage

When ProcessInfo.systemUptime is called, it returns information about how long the system has been awake since it was last rebooted. PSPDFKit supports adding biometric data for ink signature annotations by using the touch input timestamps of the annotation drawings. The timestamps are provided by the drawing inputs’ UITouch.timestamp, which is relative to the system uptime. We used ProcessInfo.systemUptime to convert this relative timestamp to an absolute timestamp by adding the system uptime (ProcessInfo.systemUptime) to the UITouch.timestamp to destroy the privacy-sensitive data about the system boot time.

It’s ironic that we accessed the privacy-sensitive information to cancel that information out; the ProcessInfo.systemUptime API can easily be misused to find out the exact boot time of a device by adding the uptime from the current date time to categorize the user into a small group of users who booted their device around the same time.

We found an alternate approach that wouldn’t entail the usage of ProcessInfo.systemUptime for generating timestamps for biometric data for ink signatures. More specifically, we changed the touch timestamps to be relative to the first touch instead of the Unix timestamp.

statfs Usage

The statfs API — which returns information about the mounted file systems, such as the total size of the file system, free space, file system type name, etc. — falls under the file timestamp APIs category, which requires a specified reason. We were using this API to get the type of the file system where a document was stored, but not to identify users in any manner. Determining whether the file system type of the document is APFS helped us know whether cheap APFS copies could be made, which in turn aided with conflict resolution while saving PDF documents.

The information returned here — such as total data blocks in the system, free blocks, etc. — can be maliciously used to make a unique signature of a device, which can then be used to track a user across different apps and websites. This becomes especially potent when combined with other information, such as the device model and OS version.

We didn’t need this data, so we replaced statfs with the URL.getResourceValue(_:forKey:) APIs, specifically using URLResourceKey.volumeTypeNameKey to determine the file system and retrieve volume type names such as APFS or HFS.

URLResourceKey.volumeTypeNameKey is a relatively new API that didn’t exist when we first added support for conflict resolution, which is why we had to resort to using statfs.

UserDefaults Usage

UserDefaults is another foundation API that’s a required reason API. We used this API to store user preferences set for annotation tools, as well as to persist the currently visible document page for state restoration. The search also showed we were using systemFreeSize to check if there was enough space on the disk for storing temporary cache files.

We weren’t able to find alternates for these APIs that didn’t require the use of these required reason APIs. Fortunately, the usage of them falls within the guidelines. Thus, we added the reasons (E174.1 for systemFreeSize and CA92.1 for UserDefaults) to declare their usage in the privacy manifest file.

Conclusion

Apple’s new requirements may feel overwhelming to address, but what worked for us was to carry out a careful audit of our usage and try to find alternative APIs that would achieve the same result as the initial ones we used. However, we weren’t able to find alternates for all the required reason APIs we use, as some of them provide essential functionality.

It’s mandatory for all apps and SDKs to include the privacy manifest beginning 1 May 2024. This not only makes it easier for users to understand what an app does under the hood, but it also helps us as developers in reevaluating the choices we make during development to ensure privacy.

Author
Nishant Desai iOS Engineer

As a result of his fascination with computers, Nishant became an iOS engineer. Other than computers, he loves cricket, cars, coffee, cinemas, and chocolates! He also likes to read up on facts and stories from history.

Share post
Free trial Ready to get started?
Free trial