TLS 1.3 URLSessionDownloadTask

I have a very strange error, I have an enterprise app that run over 4000 iPod touch, the version has been deployed since 3-4 months


and since monday this week we started to got intermittent SSL error only for URLSessionDownloadTask with Background Session


my ATS in info.plist are configured correctly


When debugging I got the following error:


Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made."


I then ran the following command in terminal


nscurl --ats-diagnostics https://myServerURL --verbose


I got this following error ( the same I got in debug) for TLS 1.3


2018-02-09 11:49:00.845 nscurl[24730:1227795] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9800)

Result : FAIL

Error : Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={_kCFStreamErrorCodeKey=-9800, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, NSUnderlyingError=0x7ff2a8100f50 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, _kCFNetworkCFStreamSSLErrorOriginalValue=-9800, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9800}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., ......


why suddently it is trying to use TLS v1.3 ?


In my info.plist I have put the minimum to 1.2 which is the default


I just found there is a way to restrict the TLS in URLSessionConfiguration, I will try that


but any logical explanation about this?


thanks

Replies

TLS 1.3 is not yet standardised. iOS 11 has a preliminary implementation of it, but that implementation will not be enabled until the standardisation process is complete. WWDC 2017 Session 701 Your Apps and Evolving Network Security Standards has the details here.

nscurl
is trying TLS 1.3 as a diagnostic helper. You should not automatically infer from that that current versions of iOS 11 are trying TLS 1.3. The error you’re seeing, from both
nscurl
and your app, is a very general TLS negotiation error (
errSSLProtocol
), and thus this is not evidence that TLS 1.3 is in play. If you believe it is — and that would be super weird — you can check that theory by looking at a packet trace. QA1176 Getting a Packet Trace describes how to get a packet trace on iOS.

I have an enterprise app that run over 4000 iPod touch, the version has been deployed since 3-4 months

and since monday this week we started to got intermittent SSL error only for

URLSessionDownloadTask
with Background Session

I suspect this is a symptom of a server-side problem. TLS problems only have 4 possible causes:

  • The client app (A)

  • iOS’s TLS implementation (B)

  • The network itself (C)

  • The server (D)

I presume you’re constraining A and B, that is, in the past week you haven’t rolled out a new version of your app (A) or a new version of iOS (B) to your client devices. Correct?

If so, that leaves C and D. Moreover, the intermittent nature of this problem is an indication of a load balancer issue, or an issue with some subset of servers behind that load balancer.

My recommendation is that you talk to your server folks about this. If necessary you can use RVI to take a packet trace (per QA1176, above) to see what’s actually going wrong wit the failed connections.

Share and Enjoy

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

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

thanks Eskimo, yes correct, the app was deployed 2 months ago and it just started to behave like that recently.


Also it only happens with one of our app that used 2 URLSessions, 1 urlSession for all GET and 1 background session for HTTP PUT


I have another app where I do not used background session and we don't have problem, don't know if it could be related.


I am interfacing an SAP system, the infrastructure looks like this


iPod Touch --> HTTPS to F5 appliance --> HTTP to SAP Gateway Backend


looks like the SSL error is between the iPod and the F5


I found this in the challenge completion handler,


    func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (Foundation.URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
       
        guard session == backgroundSession else { return }
        if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
            completionHandler(.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))
            //completionHandler(.performDefaultHandling, nil)



if I used the performDefaultHandling, it works (line 6)


but the code has always be the useCredential and it used to works,


the network guys told me nothing has change from the Network perspective


thanks

helpfull information, the F5 use a sign certificate (CA = GoDaddy)


in the retail store, the WiFi is very restricted (no access to internet)


at the head office we have multiple WiFi to test the app, the problem only happens with the store's WiFi


if we used another WiFi at the Head Office or a VPN and try to test it, it always works


thanks

I found this in the challenge completion handler,

What is that code meant to do? Right now it seems to disable all HTTPS server trust evaluation, allowing any server to impersonate your server. That’s probably not what you intended.

if I used the

performDefaultHandling
, it works (line 6)

Cool. In that case I recommend that you do that. In fact, you can remove all of your code handling the

NSURLAuthenticationMethodServerTrust
authentication challenge. And if that’s that’s the only authentication challenge you’re handling, you might as well remove the
urlSession(_:didReceive:completionHandler:)
method entirely.

If you don’t need to override HTTPS server trust evaluation, you shouldn’t!

the network guys told me nothing has change from the Network perspective

Yeah, they always say that (-:

Share and Enjoy

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

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

Eskimo, we found something else, I agree with your points.


The network guys did a trace and it looks like we are trying to communicate with GoDaddy on the ServerTrust challenge

and all the ports are blocked, but if they allow everything it works

And this happens only on my second URLSession which is configured as a background session, and this is intermittent and only started last week.


I used one URLSession for all my HTTP GET and another one (background session) for HTTP PUT & POST


here is a code snippet


//extension NetworkClient: URLSessionDelegate {
    public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (Foundation.URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
      
        if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
            completionHandler(.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))
        } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic {
            completionHandler(.useCredential, credential)
        }
    }
}
//extension NetworkClient: URLSessionTaskDelegate {
    public func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (Foundation.URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
      
        if challenge.previousFailureCount == 0 {
            completionHandler(.useCredential, credential)
        } else {
            completionHandler(.cancelAuthenticationChallenge, nil)
        }
    }
}



iPod < --HTTPS-- > F5 Appliance. < --BasicAuth-- > SAP Gateway


Scenario: 1 URLSession

When I launch my app, I refresh the application data with URLSession #1

I received the challenge for server trust on line 05

then after I receive the challenge for basic auth on line 15

I know I could change line 05 to use .performDefaultHandling and provide a default else clause but

there are no problem at all with this approach and it always works in our production environment


Scenario: 2 URLSession

When I launch my app, I refresh the application data with URLSession #1

I received the challenge for server trust on line 05

then after I receive the challenge for basic auth on line 15

When I submit my first save (HTTP PUT or POST) with URLSession #2

I received the same server trust challenge on line 05 and I am not receiving the session challenge for basic auth,

looks like it knows I already authenticate before with URLSession #1

The intermittent bug is that sometimes we received SSL error after the challenge for URLSession #2 on line 05

which I resolved by changing it to .performDefaultHandling

We trace it and found that it's trying to talk with GoDaddy and because we are in a private network the port are not open

but that is strange that sometimes it works and sometimes it don't

right now the network guys will adjust the firewall to open the ports for it but we are trying to investigate why suddently the server trust challenge is trying to communicate with GoDaddy, any idea?


I always received the basic auth challenge at the task level so like you said maybe I could get rid of the hole implementation of URLSessionDelegate or changing line 05 to .performDefaultHandling


thanks

This has got too complex for the amount of time I can spend here on DevForums. If you’d like me to help you dig deeper, you should open a DTS tech support incident and we can pick it up in that context.

However, there is one thing that specifically called out to me in your post. You wrote:

I know I could change line 05 to use

.performDefaultHandling
and provide a default else clause but there are no problem at all with this approach and it always works in our production environment

But… but… but… line 5 is completely disabling HTTPS server trust evaluation. A man-in-the-middle attack could return any certificate and your app would allow the connection through. This seems really bad to me.

Share and Enjoy

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

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

yeah I will fix that, thanks for you help, very appreciated,


we decided to go with the .performDefaultHandling and also the network guys open the firewall for our CA GoDaddy


extension NetworkClient: URLSessionDelegate {
    public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (Foundation.URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic {
            completionHandler(.useCredential, credential)
        } else {
            completionHandler(.performDefaultHandling, nil)
        }
    }
}


cheers!