Stuart Breckenridge

App Transport Security, Cloudfront, and S3

App Transport Security is a new feature in iOS 9 and OS X 10.11 and it’s enabled by default. This means that you can’t use standard HTTP for requests without making changes to application’s Info.plist.

Take the following simple request:

let request = NSMutableURLRequest(URL: NSURL(string: "http://something.cloudfront.net/some.file")!)
request.HTTPMethod = "HEAD"
let queue = NSOperationQueue()
let session = NSURLSession(configuration: sessionConfiguration, delegate: nil, delegateQueue: queue)
let downloadTask = session.downloadTaskWithRequest(request) { (url, response, error) -> Void in
            print(response)
        }

This request fails in iOS 9 with the following error and a nil response:

App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. 

Temporary exceptions can be configured via your app's Info.plist file.

As Cloudfront supports HTTPS the request URL can be changed:

let request = NSMutableURLRequest(URL: NSURL(string: "https://something.cloudfront.net/some.file")!)

However, when running the request a different error appears:

NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)

For my specific configuration – Cloudfront CDN with data hosted in Amazon S3 – Forward Secrecy is not enabled (or, at the very least, it’s causing issues). You don’t have to tear down ATS entirely to get things working, you just need to disable the requirement for Forward Secrecy in your Info.plist:

<key>NSAppTransportSecurity</key>
	<dict>
		<key>NSExceptionDomains</key>
		<dict>
			<key>something.cloudfront.net</key>
			<dict>
				<key>NSExceptionRequiresForwardSecrecy</key>
				<false/>
			</dict>
		</dict>
	</dict>

This resolved my issue, hopefully it helps someone else to.