How to disable ATS etc. for LAN servers?

I've got an app that communicates with a credit card reader over HTTPS using WiFi. The credit card reader supplies a certificate, but the domain name of the certificate does not compute, because we are on a private LAN with DHCP. For example the credit card reader's IP address is 10.5.5.5. But another location might have their DHCP giving out 10.5.2.213 or whatever.


So the question is, how can I configure my app so that it will trust the certificates from these terminals, but only at certain known possible local IP ranges? It will not be feasible to have the devices pre-installed with the certificates.


All I really need to do is just have the iOS clients not care about server name mismatch, or add whole ranges of local IPs. I mean, if a server is on the LAN, why can't we assume it's trusted (for crying out loud it's on the LAN).


I already tried:


- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler{
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
}  


This does nothing at all.


I also tried fully disabling ATS and it still fails.


It's not going to be feasible to add the certificate from the credit card terminals to every iPad because the only way to get the certificate off of the terminals is via an OpenSSL command, and the iPads are not owned by us anyway. We need the app to be able to trust these certs by default, no matter what LAN IP they are on. How can we achieve that?

Replies

There's not much point using HTTPS if the certificate is deliberately "untrustable" (domain name does not match server's address). Anything you do to get around that would leave you wide open to MITM attacks. Might as well just use plain HTTP.

So the question is, how can I configure my app so that it will trust the certificates from these terminals, but only at certain known possible local IP ranges?

As junkpile said, determining trust based on IP addresses is pretty pointless.

I mean, if a server is on the LAN, why can't we assume it's trusted (for crying out loud it's on the LAN).

You trust your WLAN? I don’t. There are a variety of ways that LANs might be attacked. For example:

  • There might be a compromised STA on the WLAN. Most APs don’t protect you from STA to STA attacks, for example, [ARP spoofing][refAs-Wtfe].

  • The AP itself might have been compromised. Based on recent news reports, the security of your average ISP-provided AP is quite dire.

https://en.wikipedia.org/wiki/ARP_spoofing

What I’d do in your situation is:

  1. Configure the reader with a certificate that issued by a CA you control.

  2. Bundle the CA’s root certificate in your app.

  3. In your app, disable ATS; there’s some subtlety here, which I’ll cover below.

  4. In your app, when you get the server trust authentication challenge, do your own trust evaluation; again, there’s some subtlety here which I’ll come back to later.

  5. If that trust evaluation succeeds, allow the connection by resolving the challenge with

    .UseCredential
    ; if not, deny it with
    .CancelAuthenticationChallenge
    .

Running your own CA need not be difficult. Technote 2326 Creating Certificates for TLS Testing shows one simple approach. OTOH, if you’re dealing with financial transactions you may suffer from regulatory entanglements, which is not something I can advise you on.

On the subject of disabling ATS, ATS has no special affordance for local devices. For the backstory here, read this thread.

That leaves you with various options:

  • If the reader supports Bonjour, which it should, you could disable ATS for

    local
    and all its subdomains.
  • You could use some sort of DNS-for-IP-address synthesiser.

  • If neither of the above work out, you will have to disable ATS globally for your app using

    NSAllowsArbitraryLoads
    .

WARNING In the last case you really should use

NSExceptionDomains
to re-enable ATS for any other domains used by your app. For example, if your app uses HTTPS to talk to some business logic web service, you want to make sure that that communication is protected by the enhanced security guaranteed by ATS.

On the subject of trust evaluation, there’s likely to be two sticking points here:

  • trusted certificate authority

  • trust policy

On the certificate authority front, you can use

SecTrustSetAnchorCertificates
to configure a trust object to trust your CA’s root certificate. Note that this implicitly disables trust for other root certificates (otherwise configured by
SecTrustSetAnchorCertificatesOnly
), which is what you’d want to do in this case. If you do configure the trust to use just your CA’s root certificate then, if the trust evaluates successfully, you know that the server holds a digital identity that was issued by your CA.

As far as trust policy is concerned, the trust object you get from the server trust authentication challenge will be set up to do standard (RFC 2818) TLS trust evaluation. Effectively it’s the policy you get back from

SecPolicyCreateSSL
. That checks that the server name you connect to matches the server name in the certificate. That’s probably too strict for your needs: that is, you don’t care which reader you’re talking to, as long as its a legitimate reader. As such, you need to disable the server name check. The simplest way to do that is to use the basic X.509 trust policy (as returned by
SecPolicyCreateBasicX509
).

OTOH, if you care which reader you’re talking to, you can tweak things to check that you’re talking to the right reader. For example, imagine you issued the reader 1 a certificate for the name “reader1”. To check that you’re talking to that reader, simply create an TLS trust policy with that name.

For more information about server trust evaluation, see 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"

Eskimo, I do not make the reader. The reader is a third-party device. I have no control over what certificate it hands out. It is made in China and does not support Bonjour. It will always be run on a LAN. It only communicates over HTTPS (rejects HTTP).


Its cert is from a trusted authority (Comodo), so that's not the problem. The problem is that the device is on a local network and thus will always have a hostname mismatch. I have already disabled ATS entirely and just re-enabled it for the other external URLs that we use.


However, where I am stuck, is in relation to disabling hostname matching for NSURLSession. I precisely followed the instructions from Technote TN2232 and it simply does nothing at all. Every time I get an NSURLErrorDomain Code -1200 (_kCFNetworkCFStreamSSLErrorOriginalValue -9801), it never calls the NSURLSessionDelegate or NSURLSessionTaskDelegate methods for "didReceiveChallenge". I've tried configuring the NSURLSession with every permutation of settings possible, and no dice. I always get this error:


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


Got any sample code for how to use SecPolicyCreateBasicX509 with NSURLSession? The TechNote does not even mention this function.

Thanks

To be frank, "How do I configure ATS to work with a device that's configured incorrectly?" is probably a question that you get a more prompt answer for through Developer Technical Support. The answers you get on the forum are free, and giving someone an answer where they're working with things configured correctly and being used responsibly makes people feel good. All of the alternatives feel like work. 😟


More importantly, DTS should be in a situation to answer specific code level questions, and help debug your code, better than people can do over the forum.

With regards adopting

SecPolicyCreateBasicX509
, check out
SecTrustSetPolicies
. There’s no sample for this in TN2232 because it’s just a single API call.

Alternatively, you can create a copy of the trust object and customise that as you see fit. Look at the code in Listing 5 of TN2232. In that code it makes a copy of the trust policy using

SecTrustCopyPolicies
. If you want to override the trust policy you should replace that call with a call to the trust policy creation routine of your choice. In short, take Listing 5 and replace
SecTrustCopyPolicies
with
SecPolicyCreateBasicX509
.

Its cert is from a trusted authority (Comodo), so that's not the problem.

Do they all have the same certificate?

Or do all the certificates have something in common? Something that’s guaranteed to only be in these device certificates and not any other certificate issued by Comodo?

It’d **** if someone could spoof your reader simply by buying their own certificate from Comodo )-:

it never calls the NSURLSessionDelegate or NSURLSessionTaskDelegate methods for "didReceiveChallenge".

Indeed. It looks like you’re having other problems talking to that server. Specifically, deep in the error message there’s an error code of 9801, or

errSSLNegotiation
, which is documented as “Cipher Suite negotiation failure”. It’s possible that your client and the device have no cypher suites in common.

To get to the bottom of this you’ll need to look at a packet trace of the TLS negotiation. QA1176 Getting a Packet Trace shows how to get a packet trace.

Share and Enjoy

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

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

DTS will close for the winter holidays at the end of business on Wed, 23 Dec 2015 and re-open on Mon, 4 Jan 2016.