Why does NSURLSession in iOS9 return -9802?

I had a related issue a while ago explained in this thread. Now, when trying to connect to s3.amazonaws.com, I'm getting a -9802 error. As per the other thread, I used TLSTool to help discover the security settings for the problematic URL, "https://s3.amazonaws.com/...":


$ TLSTool s_client -connect s3.amazonaws.com:443
*  input stream did open
* output stream did open
* output stream has space
* protocol: TLS 1.2
* cipher: ECDHE_RSA_WITH_AES_128_CBC_SHA
* trust result: unspecified
* certificate subjects:
*   0 s3.amazonaws.com
*   1 VeriSign Class 3 Secure Server CA - G3
*   2 VeriSign Class 3 Public Primary Certification Authority - G5
*  input stream has bytes
*  input stream end
* close
* bytes sent 0, bytes received 0


Note that the protocol is TLSv1.2 and that the cipher is in the approved list in the Apple Doc specifying the requirements.


Then, I tried playing around with exceptions settings in my plist, and found the magic values (actually, not sure I need the NSIncludesSubdomains):


<key>s3.amazonaws.com</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
</dict>


What's up? Why do I need NSExceptionRequiresForwardSecrecy when I the site uses ECDHE_RSA_WITH_AES_128_CBC_SHA (on the list of approved forward ciphers) - bug? Am I missing some other red flag in the TLSTool output?


[I'm using the latest Xcode 7b4 ...]

Accepted Reply

What is not documented is that NSExceptionRequiresForwardSecrecy relaxes this check. The docs say that NSExceptionRequiresForwardSecrecy simply enables extra cypher suites and says nothing about relaxing the SHA-2/256 requirement. I've filed a bug (r. 22227634) to get the docs updated.

It turns out that the fact that

NSExceptionRequiresForwardSecrecy
relaxes the SHA-2/256 requirement is a bug; the intended behaviour of
NSExceptionRequiresForwardSecrecy
is the behaviour documented in the App Transport Security Technote, namely that it should just enable specific cypher suites.

Our plan is to fix this bug at some point in the future. We expect to fix this in some compatible fashion, so folks who’ve mistakenly used

NSExceptionRequiresForwardSecrecy
to disable the SHA-2/256 requirement won’t break. However, predicting the future is always a challenge.

Which brings us to what you should do right now. [A previous version of this post gave less concrete advice. The following is an update that tightens things up.] After discussing this with ATS Engineering our recommendations are:

  • If you're using a specific hosting service, you should consult your hosting service to get the latest advice.

  • In situations like this, where the server is fully compatible with ATS except for the SHA-2/256 certificate signing requirement, we recommend that you accurately document that state of affairs by using

    NSExceptionAllowsInsecureHTTPLoads
    .
  • You should attempt to make your server fully compatible with ATS as soon as possible.

  • When that happens, you should update your app with the more secure ATS settings.

I should stress that

NSExceptionAllowsInsecureHTTPLoads
isn't actually insecure. It's just as secure as your app currently is when it's running on iOS 8. Rather, it means that your app doesn't benefit from the extra security provided by ATS.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

Here's a sample project that has NSExceptionAllowsInsecureHTTPLoads.

Thanks. It’s really helpful to have a simple example to play with. And, after a bit of spelunking, I have a rough idea what’s going on.

Your test app accesses

api.braintreegateway.com
, so I compared its TLS setup to dhoerl’s S3 example that I’ve been testing:
$ TLSTool s_client -connect api.braintreegateway.com:443
…
* protocol: TLS 1.2
* cipher: ECDHE_RSA_WITH_AES_128_CBC_SHA256
* trust result: unspecified
* certificate info:
*  0 rsaEncryption 2048 sha1-with-rsa-signature 'api.braintreegateway.com'
…
^C
$ TLSTool s_client -connect s3.amazonaws.com:443
…
* protocol: TLS 1.2
* cipher: ECDHE_RSA_WITH_AES_128_CBC_SHA
* trust result: unspecified
* certificate info:
*  0 rsaEncryption 2048 sha1-with-rsa-signature 's3.amazonaws.com'
…
^C

The results are very similar, with two things to note:

  • Your site uses

    ECDHE_RSA_WITH_AES_128_CBC_SHA256
    , which is ‘better’ than the
    ECDHE_RSA_WITH_AES_128_CBC_SHA
    used by S3. Both are explicitly allowed by ATS.
  • Both sites use

    sha1-with-rsa-signature
    , which is the ATS roadblock we’re trying to get around.

I then set up a test project with an ATS dictionary like this:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSExceptionDomains</key>
    <dict>
        <key>s3.amazonaws.com</key>
        <dict>
            <key>NSExceptionAllowsInsecureHTTPLoads</key>
            <true/>
        </dict>
        <key>api.braintreegateway.com</key>
        <dict>
            <key>NSExceptionAllowsInsecureHTTPLoads</key>
            <true/>
        </dict>
    </dict>
</dict>

Weirdly, this works for S3 and not for your domain. Why?

After some digging I worked this out: the OS thinks that your site requires HTTP Strict Transport Security (HSTS). Now, I don’t know why the OS thinks that, but the fact that it does overrides the ATS exception in the

Info.plist
. In short, HSTS trumps ATS.

Alas, I’ve totally run out of time to investigate this in the context of DevForums. I’d be happy to look into the HSTS side of things in the context of a DTS tech support incident.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

It turns out the domain I'm actually interested in has HSTS headers like Skunkworks example. That seems to be the key.