ATS and local IP addresses

Hi,

I’m facing some problems in connecting in HTTPS to a local host of my LAN after enabling ATS in my app

The doc of ATS states that “App Transport Security (ATS) applies only to connections made to public host names. The system does not provide ATS protection to connections made to: Internet protocol (IP) addresses.”

However, if I try to invoke my service on https://192.168.1.2, an SSL error is raised.

I tried to add NSAllowsLocalNetworking to my .plist, but nothing has changed.

What should I do in order to solve the problem?

Thanks in advance

Replies

Last I checked

NSAllowsLocalNetworking
works just fine. Be aware, however, that this only disables the additional security checking done by ATS. NSURLSession still does standard ( RFC 2818) server trust evaluation on your HTTPS connections. If the server you’re talking to presents a certificate that’s not trusted by default (very likely given that it’s hard to get a CA to issue a certificate for a 192.168/16 address), you must override HTTPS server trust evaluation as described in Technote 2232 HTTPS Server Trust Evaluation.

Share and Enjoy

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

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

On this topic, I found the documentation misleading on this page:

https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html


under the topic:

"Availability of ATS for Remote and Local Connections"


My reading implied that I didn't need any sort of exceptions in the Info.plist to talk to IP Addresses. That's not correct, I still need to set NSAllowsLocalNetworking to YES. I just filed this radar for it.


rdar://29187969

Thanks eskimo for your reply.

Although I've enabled NSAllowsLocalNetworking, my NSURLSession fails with the following error codes

_kCFNetworkCFStreamSSLErrorOriginalValue=-9824, _kCFStreamErrorCodeKey=-9824, _kCFStreamErrorDomainKey=3, _kCFStreamPropertySSLClientCertificateState=0}

The delegate methods that manage the authentication challenges are never called.

What does this mean? That the network component I'm trying to reach is not compliant with ATS at all?

Thanks

That's not correct, I still need to set NSAllowsLocalNetworking to YES. I just filed this radar for it (r. 29187969).

By an amazing coincidence I’ve been researching this as part of a DTS incident. As far as I can tell, iOS 10 works as documented (which is different from iOS 9). That is, ATS protection does not apply for resources loaded via IP address. Here’s how I tested this:

  1. In Xcode 8.1 I created a new project from the Single View Application template.

  2. I modified the view controller’s

    viewDidLoad()
    method, as shown below. I did not modify the
    Info.plist
    , meaning it has no
    NSAppTransportSecurity
    dictionary.
  3. I ran that app on a device running iOS 10.1.1.

  4. On launch it prints

    success
    , indicating that ATS did not block the load.
  5. I then ran the same app on an iOS 9.3 device (I had to lower the deployment target to make this work, of course). On launch it prints

    error
    , and I get a nice message in the consoling confirm that ATS blocked the request.

I’m not sure why you’re seeing different behaviour. Perhaps you could post more details about how you tested this.

Share and Enjoy

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

let myEmail = "eskimo" + "1" + "@apple.com"
override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    var request = URLRequest(url: URL(string: "http://88.97.8.212")!)
    request.cachePolicy = .reloadIgnoringLocalCacheData
    URLSession.shared.dataTask(with: request) { (data, response, error) in
        if let _ = error {
            NSLog("error")
        } else {
            NSLog("success")
        }
    }.resume()
}

Error -9824 is

errSSLPeerHandshakeFail
, which is a fairly general error. However, given that you don’t get the
NSURLAuthenticationMethodServerTrust
authentication challenge it seems likely that the TLS handshake failed really early, before the client was presented with the server’s certificate, which is what generates that challenge.

I suspect that this is not an ATS issue but a general TLS issue. If, for diagnostic purposes only, you disable ATS completely (by setting the ATS dictionary to contain just one key,

NSAllowsArbitraryLoads
), what do you see?

Share and Enjoy

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

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

Hi Eskimo,

so I think I have the same issue like fripp85_reply. Because fripp85_reply hasn’t answered your question yet and before I start a new thread, it maybe makes sense that I answer your question with my experience . I hope this ok!


I made a small sample App on XCode 8.1 with Base SDK 10.1. We are using NSURLSessions to do some local communication with some older hardware with self signed certifcates on it.

  1. - (void)startRequest{
  2. NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"<<HTTPS_WITH_LOCAL_IP_ADDRESS>>"]];
  3. NSURLSessionDataTask *dataTask = [_session dataTaskWithRequest:request
  4. completionHandler:^(NSData *data,
  5. NSURLResponse *response,
  6. NSError *error) {
  7. if (error) {
  8. _NSLog(@"FAIL");
  9. } else {
  10. _NSLog(@"SUCCESS");
  11. }
  12. }];
  13. [dataTask resume];
  14. }
  15. - (void)URLSession:(NSURLSession *)session
  16. task:(NSURLSessionTask *)task
  17. didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
  18. completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
  19. NSURLCredential *credential))completionHandler{
  20. … here we are some certificate checking stuff…
  21. completionHandler(NSURLSessionAuthChallengeUseCredential,[NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust]);
  22. }


When we are setting the NSAllowsArbitaryLoads to YES then all is working fine.

<key>NSAppTransportSecurity</key>

<dict>

<key>NSAllowsArbitraryLoads</key>

<true/>

</dict>


The app is checking the certifcates for the NSURLAuthenticationMethodServerTrust authentication challenge and accept the Challenge by using the completionhandler.


When we now add the NSAllowsLocalNetworking with YES

<key>NSAppTransportSecurity</key>

<dict>

<key>NSAllowsArbitraryLoads</key>

<true/>

<key>NSAllowsLocalNetworking</key>

<true/>

</dict>


We are getting the followoing error :



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


The problem is that the URLSession:task:didReceiveChallenge:completionHandler: will never get called when NSAllowsLocalNetworking is activated so we won’t have the chance to do accept it. Hope you have an idea how to solve this issue, and thank you in advance.



To give you maybe some more information here is the result from the ATS Diagnostic on the local URL:


/usr/bin/nscurl --ats-diagnostics --verbose

Starting ATS Diagnostics


Configuring ATS Info.plist keys and displaying the result of HTTPS loads to

A test will "PASS" if URLSession:task:didCompleteWithError: returns a nil error.

================================================================================


Default ATS Secure Connection

---

ATS Default Connection

ATS Dictionary:

{

}

2016-11-28 14:12:17.328 nscurl[9140:297067] CFNetwork SSLHandshake failed (-9824)

2016-11-28 14:12:17.328 nscurl[9140:297067] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9824)

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=-9824, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, NSUnderlyingError=0x7ffc587246a0 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, _kCFNetworkCFStreamSSLErrorOriginalValue=-9824, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9824}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=, NSErrorFailingURLStringKey= _kCFStreamErrorDomainKey=3}

The problem is that the

URLSession:task:didReceiveChallenge:completionHandler:
will never get called …

OK, so I tried you test here in my office and, with the server I’m testing against, I always get the

NSURLAuthenticationMethodServerTrust
challenge in this case. It’s clearly something specific about your server that’s triggering this ATS problem.

Try repeating your test on the latest 10.2 beta; we’ve made some fixes in this space in the 10.2 release.

Let me know how you get along because I have further suggestions if this doesn’t help.

ps You should be using

URLSession:didReceiveChallenge:completionHandler:
for
NSURLAuthenticationMethodServerTrust
authentication challenges, because such challenges are related to the session’s connections and not to a specific task. This isn’t required (if you don’t implement the session delegate callback, NSURLSession will route the challenge to the task-specific delegate callback, quoting the first task that used that connection) but it’s best practice to use the right delegate callback for the right challenge. Moreover, in the reverse case (for task-specific challenges like
NSURLAuthenticationMethodHTTPBasic
) you have to use the task-specific delegate callback otherwise things work work.

Share and Enjoy

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

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

Thanks for the advise. So I pressed very hard my thumbs and tried it with the XCode 8.2 beta 4 with 10.2 as BaseSDK but I have the same issue on the Simulator and on the real device.


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

[I] tried it with the XCode 8.2 beta 4 with 10.2 as BaseSDK but I have the same issue on the Simulator and on the real device.

OK. I think I know what’s going on here. It seems that

NSAllowsLocalNetworking
does not disable all parts of ATS for local requests. It removes ATS’s ‘server certificate must be issued by a trusted root’ requirement but it does not remove other ATS requirements. For example, when I try to connect to a local server I see an outgoing Client Hello message listing these cypher suites.
TLS_EMPTY_RENEGOTIATION_INFO_SCSV (0x00ff)
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c)
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b)
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 (0xc024)
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 (0xc023)
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (0xc00a)
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xc009)
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (0xc028)
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (0xc027)
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)

Note that they all use ECDHE, that is, they all require forward secrecy. In contrast, if I remove

NSAllowsLocalNetworking
and set
NSAllowsArbitraryLoads
, I see this:
… all of the above …
TLS_RSA_WITH_AES_256_GCM_SHA384 (0x009d)
TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009c)
TLS_RSA_WITH_AES_256_CBC_SHA256 (0x003d)
TLS_RSA_WITH_AES_128_CBC_SHA256 (0x003c)
TLS_RSA_WITH_AES_256_CBC_SHA (0x0035)
TLS_RSA_WITH_AES_128_CBC_SHA (0x002f)

That is, the client offers cypher suites that don’t require forward secrecy.

If you have a server that does not support any forward secrecy cypher suites (which is pretty common for older servers) and the Client Hello only contains forward secrecy cypher suites, the TLS handshake will fail.

I’ve filed a bug about this (r. 29418298).

I believe that you’ll see a similar problem if the server doesn’t support TLS 1.2. That is, the client will refuse to negotiate a lower TLS version because ATS is still partially enabled (r. 28586371).

I recommend that you use a packet trace to look at the TLS handshake in the

NSAllowsLocalNetworking
and
NSAllowsArbitraryLoads
cases to confirm that the problem you’re seeing matches the symptoms I’ve described above. If it does, your only option is to stick with
NSAllowsArbitraryLoads
.

I don’t have any info as to when these problems will be fixed.

Share and Enjoy

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

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

I checked the TLS handshake and your assumption seems to be right. I see in the handshake that TLS 1.0 with a cipher suite TLS_RSA_WITH_AES_256_CBC_SHA is used.


So we are planning to release a new version of our app in the beginning of the next year. We need to get in touch with appstore review team and ask if we are allowed to submit the app with NSAllowsArbitaryLoads instead of NSAllowsLocalNetworking until the bug you reported is fixed. Is this the right approach to get sure that we can update the app next year ?


Thanks for the great support!

I checked the TLS handshake and your assumption seems to be right.

OK.

Is this the right approach to get sure that we can update the app next year?

Sorry, but I can’t really advise you on App Review issues.

Share and Enjoy

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

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

Eskimo, can you elaborate on what NSAllowsLocalNetworking actually should be doing? Between the docs and forums posts I'm confused over its true purpose. Is it about HTTP vs HTTPS, local domains vs remote domains or both?

can you elaborate on what NSAllowsLocalNetworking actually should be doing?

NSAllowsLocalNetworking
has two effects:

It has no effect on IP address loads (

http://192.168.1.39/
). On iOS 10 and later such loads are always allowed.

You should set

NSAllowsLocalNetworking
in three situations:
  • If your deployment target is iOS 10 and later and you want to fetch from unqualified or local host names

  • If your deployment target includes iOS 9 and you want to fetch from unqualified or local host names

  • If your deployment target includes iOS 9 and you want to fetch from IP addresses

The last case is interesting. The only way to fetch from an IP address on iOS 9 is to disable ATS entirely (

NSAllowsArbitraryLoads
). However, that’s a very wide-ranging security exception, so you don’t want to do that on iOS 10 and later. The trick is to set both
NSAllowsLocalNetworking
and
NSAllowsArbitraryLoads
. This gives you best practice security on iOS 10 while still being compatible with iOS 9.

Share and Enjoy

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

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