This post documents ProtonMail’s iOS security model and illustrates how our application protects sensitive data on client iOS devices. The advantages and limitations of this approach our discussed, and the source code can be viewed here. You can also ProtonMail’s iOS open source announcement.
You can also read our previously published ProtonMail threat model, which gives general information about what ProtonMail is and is not designed to protect against. Our iOS trust model is available on our Github page.
In this document, terms like Keychain, File Data Protection classes, Secure Enclave, etc., are defined in Apple’s security documentation found here. This document is technical in nature, but is also designed to be accessible to a general audience, with key features described in plain language.
As with most iOS applications, ProtonMail needs to store some data locally on your device:
- Encrypted messages, attachments, and metadata
- Public and private keys for sending and reading messages
- An access token for communication with the back end
- A token for push notification payload decryption
- User account details
- Technical data, such as date of first login and version of caching system
Some of these pieces of data are kept in Keychain, some are stored in our local CoreData database, and some are located in the UserDefaults dictionary inside the application directory. iOS offers a high level of data protection using its built-in security system: Keychain Data Protection and File Data Protection classes. But on top of these we have also introduced our own additional layer of protection called the AppKey Protection System.
We explain the security features of the ProtonMail iOS app in greater detail below, followed by four key security recommendations from our team.
Default data protection on iOS
ProtonMail leverages many of the default protections available on iOS. Late-model iPhones and iPads offer an extremely high level of security, provided the user takes proper precautions, including using a strong device password and maintaining the latest software versions. We have not tried to reinvent the wheel when it comes to Apple’s security system, which has been thoroughly vetted and audited.
Most Keychain items are saved with the kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly flag that decrypts data locally after the first time the device is unlocked. We cannot use more strict flags because the app must often work in the background, even when the device is locked (for example, when sending large attachments). In order to share items stored in Keychain (such as access tokens, for example) across all our application extensions (Share and Push Application Extensions), we keep them in one Keychain Access Group. kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly also prevents an attacker from retrieving data via a logical extraction from a rebooted device or restoring a stolen backup on another device.
The data protection class for files is set as NSFileProtectionComplete in app entitlements with default exceptions for CoreData (which includes messages, attachments, folders, contacts, etc.) and UserDefaults (which includes user account information and settings) files, which the app needs to be able to access in the background and when the device is locked.
ProtonMail’s AppKey Protection System
While the default iOS security mechanisms are satisfactory for many purposes, ProtonMail operates on a heightened threat model, which requires stronger protection than what is available by default to guard against enhanced attacks. Known attacks include mobile forensic hardware, stalkerware, and recently discovered state-sponsored spyware targeting Chinese minority groups. (We have provided further details in this security recommendation.)
To increase the chance of protecting data even when the iOS sandbox is compromised or if a rogue application manages to steal decrypted items in Keychain, we have introduced the AppKey Protection System.
This system works by encrypting all sensitive local data with a per-login local key called AppKey, which is not stored in a place that can be compromised. Users can activate this protection system by turning on TouchID, FaceID, or PIN protection in their app settings.
- Messages, attachments, contacts, labels, addresses, and other items saved in CoreData
- User account information and settings
- The user’s encryption keys
- Access token
On every app launch, the AppKey ciphertext stored in Keychain is decrypted and placed into the app memory before any other app process can work. The app is not functional unless the user authenticates with their PIN, TouchID, or FaceID. Without this authentication, the app cannot access local data and does not know anything about the user to request data from the server.
A side effect of this architecture is that the Share extension, which allows users to send files or links via ProtonMail directly from another app, requires authentication every time. This takes the user to an in-app authentication modal before opening the Composer.
AppKey is kept in the app’s memory for a user-determined amount of time, which is configured in the app settings after enabling FaceID, TouchID, or PIN protection (i.e. the Auto lock timer). The user can require authentication after the Auto lock timer expires or after each time the app goes to the background.
Here’s how AppKey is handled for each app authentication scenario:
TouchID or FaceID protection: The Secure Enclave chip (see page 8 in the iOS security documentation) generates an asymmetric keypair, keeps the private key, and gives the public key to the app. The ProtonMail app encrypts the AppKey with the public key and saves the ciphertext to Keychain, asking Secure Enclave to decrypt it every time the ProtonMail app needs to retrieve the cleartext AppKey. Secure Enclave decrypts the AppKey only after biometric authentication or device passcode, and the private key never leaves Secure Enclave.
PIN protection: The ProtonMail app derives a temporary key from the user’s PIN string, symmetrically encrypts the AppKey with this temporary key, and saves the ciphertext to Keychain. The temporary key and PIN string are never persisted and are removed from memory as fast as possible. Without the PIN input, the AppKey is not accessible.
No protection: For cases when the user does not want to switch on additional protection in their app settings, the AppKey is saved cleartext in Keychain. This case is weak against forensic attacks and in cases when the device is compromised, but the data should not be extractable from backups.
Push notifications encryption
Unfortunately, we could not apply the AppKey Protection System to the contents of push notifications because this decryption happens in a Notification Service Application Extension, which runs in a separate process and cannot request users’ local authentication by means of TouchID, FaceID, or PIN.
On each login, the app generates an asymmetric keypair, saves the private key on Keychain, and sends the public key to Proton’s push notification server accompanied by the user’s session ID. This server encrypts every push notification with the public key, and the application extension decrypts it using the private key from Keychain, ensuring that Apple (or an intelligence agency sitting on Apple’s servers) does not have access to the contents of push notifications. Raw push notifications are not persisted on the device for long and are not included in backup. The private key is removed from Keychain on logout, and the public key is not reused across sessions.
More info about the Notification Service Application Extension is available in iOS documentation.
All emails are treated as HTML emails and are rendered by means of the WKWebView component. Since incoming messages can contain malware, we’ve implemented a number of protective techniques.
Content Security Policy against XSS
The Content Security Policy (CSP) is a computer security standard introduced to prevent cross-site scripting (XSS), clickjacking, and other code injection attacks resulting from execution of malicious content in the trusted web page context. We take advantage of this technology when presenting a message body and the Composer in WebViews.
During the process of loading the HTML into WebView, CSP configuration passes three states:
- Restricts everything until DOMPurify sanitizes the HTML
- Allows only embedded images (which arrive inside an encrypted message and are considered a local resource)
- Optionally allows loading of remote resources (according to whether the user has enabled “Auto show images” in the app settings)
The Content Security Policy for Composer additionally allows inline scripts that are needed for editing.
Content Security Policy against tracking
Including remote content in the body of a message is a common method of tracking used by marketing companies. When a typical email client renders the message body HTML, it requests remote content (i.e. images, background-svg, fonts, or scripts) from a server, which can thereby obtain the user’s IP address, click ratio, or message opening status.
When the “Auto show images” option in the ProtonMail iOS app settings is disabled, it limits the WebView and controls what remote data can be loaded (including all kinds of remote content, not only images). This locking is powered by the CSP configuration of WebView.
Normal attachments are cached as encrypted files in the app directory for a period of time depending on the iOS version (usually three or four days), or whenever the user logs out of their ProtonMail account in the app. Embedded attachments are part of the message body itself.
When an attachment is decrypted, the cleartext file is stored in the app directory during the time the attachment is being previewed via the native QLPreviewController. These files are removed as soon as QLPreviewController is closed.
Man-in-the-middle (MITM) attacks are more difficult to accomplish in ProtonMail because of our use of end-to-end encryption and zero-access encryption. We have further implemented a number of safeguards in our overall security architecture, including Address Verification which prevents MITM via a fake public key. There are also safeguards against network level MITM. The following information is specific to our iOS client:
We use TrustKit for certificate pinning. If the app notices a man-in-the-middle attack, you will see an alert pop up, and the app will cut the connection to the ProtonMail server.
We do our best to protect your data even when the device is compromised, but every security system has its limits in a compromised device. We do not run any jailbreak detections because they are trivial to bypass for a sophisticated attacker and only cause problems for legitimate jailbreak users.
Audits and open source
The ProtonMail iOS client is released as free and open source software under a GNU General Public License. The app has been audited by the renowned security firm SEC Consult, and we have published the results of this audit. We invite the developer community to inspect our code and participate in our public bug bounty program. Bounty submissions can be sent to firstname.lastname@example.org.
Our recommendations for keeping your device secure
No system is 100% secure, but we have invested heavily in the security of our iOS app. You can help maximize the security of your own device by taking a few simple measures:
- Enable TouchID, FaceID, or PIN protection in the app settings, which is the only way to take advantage of our AppKey Protection System.
- Install all the latest iOS updates.
- Set a long alphanumeric device passcode in your device settings.
- Do not install untrusted configuration profiles or root certificates in your device settings.
- Do not open links or download attachments from untrusted senders.
If you have any questions about the ProtonMail iOS security model, please contact our team at email@example.com. Thank you for your support.
The ProtonMail Team
This work was conducted by Anatoly Rosencrantz and Yanfeng Zhang from the ProtonMail team.
You can get a free secure email account from ProtonMail here.
We also provide a free VPN service to protect your privacy.