Security is a vital aspect of every piece of software. Apple has made this very clear with the introduction of App Transport Security (ATS for short) several years ago. The company introduced App Transport Security alongside iOS 9 and macOS 10.11 (El Capitan) during WWDC 2015.

What Is App Transport Security?

The idea that underlies App Transport Security is simple. With ATS, Apple aims to enforce apps to embrace several best practices to secure the connections between the app and the servers it communicates with. By enforcing these best practices, Apple attempts to minimize the risk of eavesdropping and man-in-the-middle (MITM for short) attacks. In the end, Apple aims to protect the user and its data. That is what App Transport Security is ultimately about.

But ATS is more than a set of best practices. It is a not so subtle signal from Apple to developers that security is of utmost importance. Over the years, Apple has made it clear that it considers privacy and security a top priority. The problem is that the company cannot control every aspect of the app you are building. It attempts to enforce these best practices through App Transport Security. That is ATS in a nutshell.

A Bit of History

As I wrote earlier, Apple is unique in always prioritizing the user's privacy and security. That commitment resulted in the introduction of App Transport Security in 2015.

A Response to a Growing Need

Apple is well aware of how third-party apps behave on its platforms. It noticed that some apps willingly or unknowingly exposed the user's data by, for example, using unencrypted connections to communicate with servers. To be clear, Apple isn't the only company that strives for a more secure web. Google has long been a proponent of a more secure and private web. If you use Google's Chrome browser, you have almost certainly seen a warning when you try to visit a website with one or more security issues. I'm sure you agree that that is a good thing.

With more of our data stored in the cloud, data breaches and attacks have increased substantially. Apple decided to take a more proactive approach with App Transport Security.

Enter App Transport Security

App Transport Security was more than just adding a new feature developers could use. Apple wanted to make a statement by setting a new standard. ATS by default blocks outgoing connections from apps that aren't secured using Transport Layer Security (TLS) 1.2 with forward secrecy. I'm sure you agree that Apple's move was bold and disruptive, but it had a clear effect. The introduction of ATS signaled to developers the need to prioritize the security of their apps or risk features no longer working. Apple attempts to address two issues with ATS, (1) keep data confidential and (2) maintain data integrity.

By enforcing strong encryption methods, ATS forces apps to keep data confidential, safeguarding it from potential eavesdroppers. Eavesdropping is one thing, but your app also needs to prevent a third party from manipulating the data it sends to a server. Using secure protocols, your app can ensure the data it sends to a server isn't tampered with while in transit, preserving its integrity from source to destination.

More than Security

App Transport Security is more than security, though. Data is worth a lot to companies and it is therefore important to protect it. Securing the data of your app's users isn't a nice-to-have, it is essential. It is part of your app's foundation.

Technical Overview

Now that you understand Apple's reasoning and motivation for introducing App Transport Security, it is time to understand how ATS works and impacts your app's behavior.

The Default Behavior of ATS

When Apple introduced App Transport Security alongside iOS 9 and macOS 10.11, ATS enforced secure connections by default. What does that mean in practice?

TLS: ATS forces apps to connect to web services using a secure connection over HTTPS. It requires the use of Transport Layer Security (TLS) 1.2 or later. Older, less secure versions of TLS or SSL are not allowed.

Forward Secrecy: To provide additional protection against potential compromises of server keys, ATS also requires the use of cipher suites that support forward secrecy, such as ECDHE_RSA or ECDHE_ECDSA.

No Weak Cryptographic Ciphers: Several cryptographic protocols are known to have vulnerabilities. ATS prevents an app from communicating with a web service using weak cryptographic cyphers or cryptographic cyphers that have known vulnerabilities.

Certificate Transparency: As of iOS 12.2 and macOS 10.14.4, ATS requires that certificates used in server connections be compliant with certificate transparency, a protocol designed to prevent SSL certificates from being misissued. What does that mean?

SSL/TLS certificates are digital certificates that authenticate the identity of a website and enable an encrypted connection. These certificates are issued by Certificate Authorities (CAs). In the past, if a certificate authority was compromised or acted negligently, it could misissue a certificate for a domain. Malicious actors could then use this certificate to masquerade as the legitimate website, leading to man-in-the-middle attacks and other security breaches.

The goal of Certificate Transparency is to address this problem by providing an open framework in which SSL certificates can be audited and monitored. The benefits of Certificate Transparency are increased accountability, a rapid response, and enhanced trust. We revisit Certificate Transparency later in this article.

Configurations and Exceptions

The default configuration of App Transport Security is very strict, and not every app can fully comply with its requirements. The good news is that your app can configure the requirements defined by ATS through exceptions.

Domain-specific Exceptions: You can define exceptions for one or more domains. Ideally, these exceptions are temporary solutions. The goal is to update or modernize the servers that need an exception so that they comply with the standards set by ATS.

Allow Arbitrary Loads: This setting allows apps to bypass ATS entirely. That's right. It is possible to disable App Transport Security entirely. This option is primarily intended for debugging and should not be used in production apps. I talk more about this in a few moments.

Granular Controls: App Transport Security gives you the flexibility to configure ATS in such a way that it meets your app's needs. For example, you can specify the minimum TLS versions, or you can allow insecure HTTP loads for domains you define. That is a much better option than to disable App Transport Security entirely.

Why You Shouldn't Disable ATS?

Working with App Transport Security can be frustrating at times, so it might be tempting, especially if you need to work with old(er) servers, to disable ATS. That is a risky move, though. Let me explain why.

User Data at Risk: By disabling ATS, you might be exposing the user's data to third parties. While ATS may seem like a burden at times, it is designed to help you catch security issues.

App Store Implications: Apple may question or reject apps that disable ATS without a valid reason. I'm not saying that Apple will reject your app if you disable ATS, but it could be a problem in the future.

Future-Proofing: Security should always be top of mind, and preparing your app for the future is good. The digital landscape evolves at breakneck speed. More stringent security measures will likely become the norm. If you decide to embrace ATS today, then you prepare your app for the future.

As I wrote earlier, Apple's ATS is designed with a singular goal in mind, ensuring that the data your app sends to servers is transmitted safely and securely. By enforcing modern encryption standards and providing you with the tools to implement them, App Transport Security helps you protect your app—and its users—against a myriad of threats.

Configuring ATS in Your App

You can configure App Transport Security for your app through your target's Info.plist. Let's take a look at the options you have.

App Transport Security Configuration

NSAppTransportSecurity: The value of the NSAppTransportSecurity key is a dictionary that defines the App Transport Security configuration for your app. As I wrote earlier, ATS, by default, enforces the most stringent security measures. You can use this dictionary to define exceptions or fine-tune requirements.

NSAllowsArbitraryLoads: By setting the value for NSAllowsArbitraryLoads to YES, you disable App Transport Security for your app. This means in practice that your app can make requests over insecure network connections. As I wrote earlier, I don't recommend taking this approach for several reasons. You can use this option for debugging purposes or during development.

Configuring Domain-Specific Exceptions

It is possible to change the App Transport Security configuration for one or more domains using domain-specific exceptions.

NSExceptionDomains: The value of the NSExceptionDomains is a dictionary of one or more domains for which you want to define exceptions. Each exception domain corresponds with a dictionary of ATS exceptions. Let's take a look at the exceptions ATS supports.

NSIncludesSubdomains: By setting NSIncludesSubdomains to YES, the exceptions you define also apply to the subdomains of the domain.

NSExceptionAllowsInsecureHTTPLoads: Set NSExceptionAllowsInsecureHTTPLoads to YES to allow insecure HTTP connections for the domain (and optionally its subdomains).

NSExceptionRequiresForwardSecrecy: Set NSExceptionRequiresForwardSecrecy to NO to allow the use of TLS connections without forward secrecy for the domain (and optionally its subdomains).

NSExceptionMinimumTLSVersion: You can use NSExceptionMinimumTLSVersion to specify the minimum TLS version for network connections to the domain (and optionally its subdomains). The possible values are TLSv1.0, TLSv1.1, TLSv1.2, and TLSv1.3.

App Transport Security also defines keys for third-party exceptions. They warrant an explanation because they can be confusing at first. The keys are NSThirdPartyExceptionAllowsInsecureHTTPLoads, NSThirdPartyExceptionRequiresForwardSecrecy, and NSThirdPartyExceptionMinimumTLSVersion. The behavior of these keys is similar to the counterparts we discussed earlier. The difference is that they are used specifically for third-party domains your app needs to communicate with. In other words, you don't control the servers.

Let's say you are developing an app that integrates with a third-party weather API. Your app fetches weather data from the third-party weather API. The problem is that the weather API doesn't use SSL. ATS, by default, blocks insecure network connections, so fetching weather data from the weather API fails. That is expected.

One solution is to define an exception for this third-party domain in the target's Info.plist.

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSExceptionDomains</key>
    <dict>
        <key>weatherapi.example.com</key>
        <dict>
            <key>NSThirdPartyExceptionAllowsInsecureHTTPLoads</key>
            <true />
        </dict>
    </dict>
</dict>

This simple change ensures that your app can continue to make requests to the weather API while still enforcing ATS's secure connection requirements for other domains. While this solution works, it is critical that you have a plan in place to address this security issue because, don't be fooled, this is a security issue you are working around. My suggestion would be to move away to a secure alternative. An API that allows traffic over HTTP is a red flag in my book.

There are a few other App Transport Security configuration options that come in useful when working with ATS.

NSAllowsLocalNetworking: The NSAllowsLocalNetworking key is useful when your app talks to a local server. Set its value to YES to allow your app to communicate with devices in the local network without HTTPS.

NSAllowsArbitraryLoadsInWebContent: If your app relies on web views, then this configuration option is convenient or even necessary. By setting its value to YES, your app can load content in a web view over HTTP. I like this addition because you don't need to worry about the content the user can access through the web view. You can still enforce App Transport Security without having to worry about web views.

About Certificate Transparency

We briefly discussed certificate transparency earlier. App Transport Security supports certificate transparency, but Apple's documentation is a bit confusing. By setting NSRequiresCertificateTransparency to YES, server certificates delivered to your app are required to be Certificate Transparency compliant. However, Apple's documentation clearly states that you shouldn't use NSRequiresCertificateTransparency. Why is that? What does Apple mean?

The documentation states the following:

Don’t use this key. The current system enforces this behavior, and new certificates can’t meet the requirement on older systems.

Current System Enforces This Behavior: Certificate Transparency (CT) is now a requirement for most web certificates. Newer versions of Apple's operating systems enforce Certificate Transparency checks by default for SSL/TLS connections. What does that mean for you? If you are developing for a recent version of iOS, tvOS, macOS, or watchOS (or visionOS), CT checks are already in place and there's no need to specify it separately in your app's configuration. That's clear. Right?

New Certificates Can't Meet the Requirement on Older Systems: Older systems that don't inherently support or understand Certificate Transparency will not be able to validate certificates that require CT. If an app were to mandate CT on such systems using the NSRequiresCertificateTransparency key, it might inadvertently block valid certificates, leading to connection failures.

With this in mind, Apple's recommendation is straightforward, that is, avoid using the NSRequiresCertificateTransparency key. This ensures consistent behavior across different versions of the operating system and prevents potential connection issues.

Best Practices

Strive to use the default App Transport Security settings to prioritize security and protect your users' data.

Only use exceptions when necessary and be prepared to justify them during App Store review.

If you use exceptions, then review them regularly and update your app's ATS configuration when needed. This is especially important when a new (major) version of the operating system is released. Focus on optimizing your app's security and be compliant with Apple's security guidelines.

App Transport Security in Practice

While the default settings of ATS are designed for maximum security, there are instances where you need to tailor its behavior to accommodate requirements that are specific to your app. Let's explore a few common use cases and how to configure ATS accordingly.

Development and Debugging

Use Case: When you are developing or debugging your app, you might be working with a local server, mock endpoints, or services that aren't configured with the TLS settings ATS requires.

Configuration: You can set NSAllowsArbitraryLoads to YES to temporarily bypass ATS restrictions during development or while you're debugging an issue. This isn't my preferred approach because it is too easy to forget to remove the NSAllowsArbitraryLoads when you are ready to release a new version. For local networking, set NSAllowsLocalNetworking to YES.

Don't forget to revert these changes when you no longer need them. Don't commit these changes to source control.

Integration with Legacy Systems

Use Case: While this is becoming increasingly rare, it is possible your app needs to communicate with a legacy system that doesn't support the latest TLS protocols or other ATS requirements.

Configuration: Add a domain-specific exception for the domain using NSExceptionDomains. Within the domain dictionary, set keys like NSExceptionAllowsInsecureHTTPLoads or NSExceptionMinimumTLSVersion as needed. We discussed this earlier.

Third-Party Services Without Full ATS Compliance

Use Case: You're integrating with a third-party service or API that doesn't meet all ATS requirements.

Configuration: In this scenario, use NSThirdPartyExceptionAllowsInsecureHTTPLoads or other third-party exception keys (see above) under NSExceptionDomains for the specific third-party domain.

Embedded Web Content

Use Case: Your app contains a web view (e.g., WKWebView) that displays content from various sources on the web.

Configuration: As I wrote earlier, set NSAllowsArbitraryLoadsInWebContent to YES to bypass ATS, allowing the web view to load content over HTTP or HTTPS without individual ATS exceptions.

Specialized Networking Scenarios

Use Case: It is possible your app connects to a server with a rather unconventional configuration, for example, it uses custom cipher suites or an older TLS version.

Configuration: Create an exception domain under NSExceptionDomains and set keys like NSExceptionMinimumTLSVersion or NSExceptionRequiresForwardSecrecy to meet the configuration of the server.

Testing and Debugging App Transport Security Issues

I hope you agree that App Transport Security is a welcome addition, although it initially received quite a bit of criticism. Whatever your stance is, you need to make sure your app can continue to communicate with the services it requires to function. This means you need to test and debug potential issues related to ATS. Let's wrap up this article with some tips to find, test, and resolve problems related to ATS.

Testing ATS Compliance

nscurl: Bundled with macOS, nscurl is a command-line tool that helps test ATS configurations against your servers. By running various tests, this nifty tool can identify potential incompatibilities between your server's setup and ATS requirements.

SSL Server Test: SSL Server Test is a service provided by SSL Labs and provides a comprehensive analysis of your server's SSL/TLS configuration, highlighting potential vulnerabilities and compatibility issues.

Proxy Tools: Tools like Charles, Proxyman, and Wireshark are invaluable for app developers like you and me. They allow you to inspect network traffic between your app and server, providing insights into potential security issues or misconfigurations.

Common Pitfalls and Their Solutions

As I wrote earlier, working with App Transport Security can be frustrating at times. What follows is a list of common issues and pitfalls to avoid.

Mismatched TLS Versions: If the server your app attempts to connect to doesn't support the required TLS version (e.g., TLS 1.2), App Transport Security will block the connection.

The solution is obvious. Upgrade the server to support the necessary TLS version. Alternatively, you can set NSExceptionMinimumTLSVersion in your target's ATS settings to the TLS version of the server. Consider this a temporary solution, though.

Inadequate Cipher Suites: As you learned in this article, App Transport Security requires servers to support forward secrecy. If the server only offers non-compliant cipher suites, connections will fail.

Update the server's configuration to support cipher suites that offer forward secrecy, such as ECDHE_RSA or ECDHE_ECDSA.

Expired or Invalid Certificates: If the server's SSL certificate is expired, self-signed, or otherwise invalid, ATS will block the connection.

Renew or replace the certificate with a valid one issued by a recognized Certificate Authority (CA).

Debugging ATS Issues with Network Logs

Console.app: I feel Console.app is underused or underappreciated. It can provide valuable information through system and app logs to debug ATS issues. Filter the logs by your app's name to help locate and inspect networking-related logs, including ATS errors.

Device Logs: When you are testing on an actual device, you can access the device logs through Xcode. The device logs often contain detailed error messages related to ATS issues, which can help you find the root cause of networking-related issues.

Descriptive Errors: The errors ATS outputs are very helpful and descriptive. They typically point out the exact reason for the failure. For example, "The resource could not be loaded because the App Transport Security policy requires the use of a secure connection." This is pretty clear. Right?

Common Questions

What is the difference between NSExceptionAllowsInsecureHTTPLoads and NSThirdPartyExceptionAllowsInsecureHTTPLoads?

Both NSExceptionAllowsInsecureHTTPLoads and NSThirdPartyExceptionAllowsInsecureHTTPLoads are keys within the App Transport Security configuration that allow for exceptions to the default ATS requirements. At first glance, it may seem as if they can be used interchangeably. There is a subtle but important difference, though.

NSExceptionAllowsInsecureHTTPLoads

Purpose: This key allows your app to make insecure HTTP (instead of HTTPS) connections to the domain it is associated with.

Use Case: It is designed for domains that your app directly communicates with and that you have control over or a direct relationship with. Let's take a look at an example. Your app communicates with a server that hasn't been upgraded to support HTTPS. In that scenario, you use this configuration option to temporarily allow insecure connections from your app to the server.

NSThirdPartyExceptionAllowsInsecureHTTPLoads

Purpose: Like NSExceptionAllowsInsecureHTTPLoads, this key allows your app to make insecure HTTP (instead of HTTPS) connections to the domain it is associated with. The key difference is that the domain is a third-party domain (e.g., apple.com).

Use Case: It is designed for domains that your app needs to communicate with but that you don't control. Let's take a look at another example. Your app relies on a third-party service or API that doesn't support HTTPS, for example, a weather service. You use this configuration option to allow insecure connections from your app to the server.

The distinction between these two keys is largely semantic, helping to clarify the intent and ownership of the domains being excepted from ATS's default behavior. That said, from a technical perspective, both keys achieve the same result. They both allow insecure HTTP connections to the domain.

It is important to keep the security implications in mind when using these configuration options. By allowing insecure connections to a server, you expose your app to potential man-in-the-middle attacks and other vulnerabilities. It's always recommended to use these exceptions as temporary solutions and to move to secure HTTPS connections as soon as feasible.