In this article I’m trying to untangle a very confusing notion for some people (myself included) - App Transport Security. We will have several examples to work with and then try to construct our own setting according to our requirements.
Overview
NSAppTransportSecurity : Dictionary {
NSAllowsArbitraryLoads : Boolean
NSAllowsArbitraryLoadsForMedia : Boolean
NSAllowsArbitraryLoadsInWebContent : Boolean
NSAllowsLocalNetworking : Boolean
NSExceptionDomains : Dictionary {
<domain-name-string> : Dictionary {
NSIncludesSubdomains : Boolean
NSExceptionAllowsInsecureHTTPLoads : Boolean
NSExceptionMinimumTLSVersion : String
NSExceptionRequiresForwardSecrecy : Boolean // Default value is YES
NSRequiresCertificateTransparency : Boolean
}
}
}
The above code snippet from Apple docs shows the object that is responsible for loosening network security. This topic has been very confusing for me, but I’ve finally got myself untangled 🎄 🐈 💡 !
As always, nothing serves better as a good example and a good picture. Here is a picture I consider to be good:
First, there are two ways how to enable ATS: do nothing (better one unless absolutely needed) or add NSAppTransportSecurity
and set NSAllowsArbitraryLoads
to key. But keep in mind that in this case Apple will make you answer for your actions 🚓 in court.
What do we need?
Begore we begin, let’s assume that we own an application which uses API at https://bakerst221b.com. Also, it downloads images from http://rams.bakerst221b.com and also periodically checks weather at http://weather.co.uk. Let’s first see some examples and the decide.
Example 1. Too loose
Here is the first setting:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
This basically means the following: “Hey, System. Allow this application to communicate over HTTP, with any TLS version of its like in case of HTTPS, with or without PFS and to local domains as well (since NSAllowsArbitraryLoads
is true
).” Sounds pretty bad, but how bad that is in real life? What if the application in question is a web browser? Well, in that case that option above would be the only option at all 🤷♀️ , since if ATS was left with default settings (no NSAppTransportSecurity
in Info.plist
or in Info.plist
but NSAllowsArbitraryLoads
set to false
), our browser would not be able to connect to many websites, which do not correspond to its high standards.
Example 2. Getting tighter
Now consider the second setting:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>bank.com</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
It now says the following: “Turn off ATS, but keep it for bank.com
(since NSAllowsArbitraryLoads
is true
) and its subdomains (since NSIncludesSubdomains
is true
) only allowing them to communicate over HTTP (since NSExceptionAllowsInsecureHTTPLoads
is true
)”. When this might be handy? For example, our application communicates with different services which are not very secure 😉, but it wants to communicate securely with its own API (bank.com
). But since there sometimes pictures on the same domain, which are loaded via HTTP, the application softens the policy and allows HTTP connection for bank.com
, but keep in mind that other ATS restriction for bank.com
are still in place: if it’s HTTP - good TLS, PFS and no connection to local domains.
Example 3. Getting really serious
Now consider the following example:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSExceptionDomains</key>
<dict>
<key>rams.bank.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
Now the application says to the system: “Hey, buddy, turn on ATS (since NSAllowsArbitraryLoads
is false
). I know, it’s your default behaviour, but I need to set some exceptions. For rams.bank.com
🐱 only (since NSExceptionDomains
is set to rams.bank.com
) allow communication over HTTP (since NSExceptionAllowsInsecureHTTPLoads
is true
), since we don’t care about them much.” This is considered the best option if HTTP connection is mandatory.
So here we observe an opposite behaviour. While in the second example we turned ATS globally (for everyone) except bank.com
, now we turn on it globally except for rams.bank.com
. You can see that it really work both ways here, in the official docs.
Example 4. This strange world
Let’s assume this setting:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSAllowsArbitraryLoadsForMedia</key>
<true/>
</dict>
The app says: “Hey, System, turn off ATS globally and allow crapy loads for media which uses AVFoundation framework (since NSAllowsArbitraryLoadsForMedia
is true) (i.e. I need to download any 💩 of my choice).” Right? Well… this new key that we added to the setting is changing that all: NSAllowsArbitraryLoadsForMedia
. For iOS 9.0 or macOS 10.11 yes, it just turns on ATS globally and ignores whether NSAllowsArbitraryLoadsForMedia
is true
or false
. For newer versions vice versa: ignores NSAllowsArbitraryLoads
set to true
and only lossening security for media (NSAllowsArbitraryLoadsForMedia
set to true
). Domains in NSExceptionDomains
will not be affected and stick to their default values.
Other keys with the same behaviour are:
NSAllowsArbitraryLoadsInWebContent
- to loosen ATS for WebViews only (WKWebView
,UIWebView
,WebView
for macOS) while forNSURLSession
ATS will still be valid. Domains inNSExceptionDomains
will not be affected and stick to their default values.NSAllowsLocalNetworking
- allow connection to unqualified domains and.local
. If you need connecting via IP, setNSAllowsArbitraryLoads
totrue
instead. In newer versions allowed by default.NSExceptionDomains
can be used to specify exceptions for local domains.
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
The above setting is recommended by Apple for backward compatibility. Why? Older versions ignore NSAllowsLocalNetworking
set true
and allow NSAllowsArbitraryLoads
globally instead. Newer version seeing that very same setting (since NSAllowsLocalNetworking
is enabled by default, no need to specify in Info.plist
) ignores NSAllowsArbitraryLoads
set to true
and keeps other ATS requierements.
One last example:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSAllowsArbitraryLoadsForMedia</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>bank.com</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>
</dict>
For newer versions we have the following setting: “Hey, keep ATS turned on globally (NSAllowsArbitraryLoads
is false
), but allow loading arbitrary media which uses AVFoundation framework (NSAllowsArbitraryLoadsForMedia
is true
), but don’t allow even that for bank.com
(NSExceptionDomains
) and its subdomains (NSIncludesSubdomains
is true
).”
Back to our 🐏
Let’s refresh out requirements:
… let’s assume that we own an application which uses API at https://bakerst221b.com. Also, it downloads images from http://rams.bakerst221b.com and also periodically checks weather at http://weather.co.uk.
Connection to https://bakerst221b.com is secure and all the requirements are met. But connection to a subdomain http://rams.bakerst221b.com is insecure and requires loosening ATS. Also, we have a service which also needs HTTP.
Do we need to enable or disable NSAppTransportSecurity
globally? Since we mostly need HTTPS, it makes more sense to keep in enabled globally (NSAllowsArbitraryLoads
we set to false
). Then, we add a dictionary for NSExceptionDomains
and add two domains as exceptions for this policy: rams.bank.com
and weather.co.uk
. For each we only need to allow HTTP traffic, but keep other security controls, therefore, set NSExceptionAllowsInsecureHTTPLoads
for both as true
and that’s pretty much it!
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSExceptionDomains</key>
<dict>
<key>rams.bank.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
<key>weather.co.uk</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
Conclusion
I hope that will clarify how to use ATS for you app as securely as possible. My advice is to loosen as little, as you can and separate domains, which meet the requirements and will be protected by ATS and those that don’t. To help you with configuring ATS:
nscurl --ats-diagnostics --verbose https://bakerst221b.com