by Talal Haj Bakry and Tommy Mysk
UPDATE (May 6, 2024): Added a section for Facebook
If you enjoyed this work, you can support us by checking out our apps:
- TextCrafter – Craft & Share
- Ctrl – The best presentation companion for your Apple Watch
- Canvas – Draw Together, Wirelessly!
tl;dr
Starting May 1, 2024, Apple requires developers to declare reasons if their apps use APIs that can potentially be misused to collect unique device signals. These unique signals enable abusers to derive a device identifier or fingerprint and result in tracking user activities across different apps of different developers. Such APIs are referred to as required reason API. To prevent misuse of these APIs, Apple will reject apps that don’t describe their use of the APIs in their privacy manifest file. However, we found out that apps such as Google Chrome, Instagram, Spotify, and Threads don’t adhere to their declared reasons.
Background
In WWDC 2023, Apple announced a new privacy measure to prevent device fingerprinting, a practice prohibited by Apple Developer Program License Agreement. Apple published a preliminary list of APIs that have the potential of being misused to collect unique device signals. Before using any of these APIs, developers need to declare the reason for using the APIs in a privacy manifest file.
The list will change over time as more APIs will be added as needed. Apple has provided a list of approved reasons for each API. Developers have to review their code and then pick the approved reason that best describes their use of the API. In addition, developers are also responsible for third-party SDKs included in their apps and, therefore; are required to describe their use of required reason API. Apple also published a list of SDKs that require a privacy manifest and signature.
Maintaining the privacy manifest to pass Apple’s App Review process is another burden that developers have to take. Is it worth the overhead?
In Practice
Describing the use of required reason API is a great attempt to prevent fingerprinting. The process also educates developers about such APIs and why access to them should be minimized and signals retrieved from the APIs should remain on-device and never be sent off-device. But this is in theory.
In practice, we analyzed the network traffic of several popular apps that were updated after May 1, when this new requirement took effect. We focused on the API that retrieves a device’s boot time, or system uptime. It is the elapsed time in seconds since a device was restarted. Combined with a few other signals, the system uptime leads to generating a very accurate fingerprint of a device.
The use of system boot time APIs requires declaring an approved reason. The possible approved reasons for using the system uptime API are as follows:
35F9
.1 Declare this reason to access the system boot time in order to measure the amount of time that has elapsed between events that occurred within the app or to perform calculations to enable timers.
Information accessed for this reason, or any derived information, may not be sent off-device. There is an exception for information about the amount of time that has elapsed between events that occurred within the app, which may be sent off-device.
8FFB
.1 Declare this reason to access the system boot time to calculate absolute timestamps for events that occurred within your app, such as events related to the UIKit or AVFAudio frameworks.
Absolute timestamps for events that occurred within your app may be sent off-device. System boot time accessed for this reason, or any other information derived from system boot time, may not be sent off-device.
3D61
.1 Declare this reason to include system boot time information in an optional bug report that the person using the device chooses to submit. The system boot time information must be prominently displayed to the person as part of the report.
Information accessed for this reason, or any derived information, may be sent off-device only after the user affirmatively chooses to submit the specific bug report including system boot time information, and only for the purpose of investigating or responding to the bug report.
https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api#4278394
All the approved reasons emphasize that information retrieved by the APIs may not be sent off-device.
The following sections show how apps adhere to their declared reasons.
Facebook declares the approved reason 35F9.1
for accessing the system boot time, as shown in its privacy manifest file (extracted from the app binaries):
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
Our testing shows that Facebook still sends the system uptime off-device. This is a screenshot of the request:
Google Chrome
Google Chrome declares the approved reason 35F9.1
for accessing the system boot time, as shown in its privacy manifest file (extracted from the app binaries):
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
As shown above, Reason 35F9.1
instructs developers not to send the accessed information off-device. However, our testing shows that Google Chrome still sends the system uptime off-device. This is a screenshot of the request:
Instagram also declares the approved reason 35F9.1
for accessing the system boot time, as shown in its privacy manifest file (extracted from the app binaries):
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
Our testing shows that Instagram still sends the system uptime off-device. This is a screenshot of the request:
Spotify
Spotify declares the approved reasons 35F9.1
and 8FFB.1
for accessing the system boot time, as shown in its privacy manifest file (extracted from the app binaries):
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
<string>8FFB.1</string>
</array>
</dict>
As shown above, both declared reasons instruct developers not to send the accessed information off-device. However, our testing shows that Spotify still sends the system uptime off-device. This is a screenshot of the request:
Threads
Threads, an Instagram app, declares the approved reason 35F9.1
for accessing the system boot time, as shown in its privacy manifest file (extracted from the app binaries):
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
Our testing shows that Instagram still sends the system uptime off-device. This is a screenshot of the request:
Final Words
While forcing developers to describe their use of required reason API is a great starting point to stop fingerprinting, it gives a false sense of privacy. Apple doesn’t provide a mechanism to enforce what developers declare. We have seen this approach when Apple introduced Privacy Nutrition Labels. There is no mechanism to verify what developers show on their apps’ Privacy Nutrition Labels.
All in all, you should always exercise judgment and only install apps of developers you trust.
Technical details
App | Version |
462.0.0 | |
Google Chrome | 124.0.6367.111 |
329.0 | |
Spotify | 8.9.36 |
Threads | 329.0 |