Handle certificates in your iOS app to prevent man-in-the-middle attacks on your app and on your server.

Any app that connects over the Internet to a hosted service must have a security policy in place. The security policy will depend primarily on the sensitivity of the data being transferred between the app and the server. However, even if the app doesn't transfer sensitive customer information, it should protect itself from attack. Even the least harmful attack will ruin the user's experience and the app's reputation.

Today most web services (including Twitter and Facebook) require request authentication with OAuth as part of getting login credentials from the user. The developer gets a consumer and secret key after registering the app. When the app accesses the web service, the app first exchanges its encoded keys for a bearer token. For the life of this token, the app has permission to access the service's API.

It is highly recommended that your app access a web service that is protecting itself with OAuth authentication.

Along with OAuth, there is HTTPS, which puts HTTP on top of the SSL/TLS protocol, providing data encryption and X.509 certificate exchange. It is possible with iOS to use only the data encryption and turn off certificate exchange, but in most cases this isn't necessary. Apple has installed many root certificates from trusted certificate authorities.

If the server your app is accessing has a self-signed certificate installed, you can continue without or bypass the credential you receive with the authentication challenge. Or, because you know the host in advance, you can trust it. Either way, you are not protected from a man-in-the middle attack.

Man-in-the-middle attack

A man-in-the-middle attack is a form of active eavesdropping in which the attacker inserts itself in the middle of an Internet connection between the iOS app and a server. The diagram in Figure A shows the attacker's steps, labeled 1 through 5.

Normal traffic between mobile app and server. The attacker gets the request from the app and makes changes to it. The request is forwarded to the server. The attacker gets the response from server and makes changes. The response is forwarded to the app.

Apps that transfer sensitive customer data, like credit card and payment information, must be protected from man-in-the middle attacks. The best protection is a mutual authentication scheme, where certificates are exchanged to make sure the app is connected to a trusted server and to make sure the server is connected to a trusted app.

NSURLConnectionDelegate

We will be assuming the NSURLConnection class is used, without a third-party library, to connect to a server. For our example, the NSURLConnectionDelegate protocol methods will handle the authentication.

Since iOS 5, the following connection delegate methods have been deprecated:

They have been replaced by the willSendRequestForAuthenticationChallenge method; only when this method is not implemented will the deprecated methods get called. We will implement this method to perform the client and the server certificate validations.

Client certificate validation

The server validates the client's certificate. The server's administrator provides the client certificate. Its private key is stored on the server, and the public key is provided as part of the authentication challenge. The iOS app stores the certificate in DER format. It may be necessary to convert from PEM to DIR (binary) format:

$ openssl x509 –inform PEM –outform DER –in cert.pem –out cert.der

Add the cert.der file to the iOS app's resource bundle; this is only the public key, so it is safe to store. The following method, shouldTrustProtectionSpace, loads the certificate from the main bundle, creates a certificate from the loaded data, and establishes a chain of trust in the server's protection space, anchored on the bundled certificate.

The Security framework must be included in the Xcode project.

The established server trust is evaluated. The following shows the possible results from the Apple documentation.

kSecTrustResultInvalid: Invalid setting or result indicates evaluate failed to complete successfully

Invalid setting or result indicates evaluate failed to complete successfully kSecTrustResultProceed: You may trust the certificate

You may trust the certificate kSecTrustResultConfirm: Ask permission from the user

Ask permission from the user kSecTrustResultDeny: The certificate should not be trusted

The certificate should not be trusted kSecTrustResultUnspecified: The user did not specify a trust setting.

The user did not specify a trust setting. kSecTrustResultRecoverableTrustFailure: Trust denied but may retry after changing setting.

Trust denied but may retry after changing setting. kSecTrustResultFatalTrustFailure: Trust denied and no simple fix may be applied

Trust denied and no simple fix may be applied kSecTrustResultOtherError: A failure other than trust evaluation

You may recover from a recoverable trust failure by getting and setting the exceptions in the established server trust and re-evaluating.

if (trustResult == kSecTrustResultRecoverableTrustFailure) { CFDataRef errDataRef = SecTrustCopyExceptions(serverTrust); SecTrustSetExceptions(serverTrust, errDataRef); SecTrustEvaluate(serverTrust, &trustResult); }

Server certificate validation

With server authentication, the SSL certificate used for the HTTPS connection is compared with a certificate stored on the client. Following the instructions in the article How to Validate SSL Certificates on iOS, we compare the certificate data hashes. SHA256 is used to keep it simple and reasonably secure. Implement the SHA256 method as shown in the article.

Putting it all together

The willSendRequestForAuthenticationChallenge delegate method calls shouldTrustProtectionSpace to handle the client certification validation. If the certificate validates successfully, the server's certificate is retrieved, hashed, and compared to the hash value EXPECTED_CERTIFICATE_BASE64_SHA256.

If the certificate hash values match, the challenge sender's useCredential method is called (success!). Otherwise, the challenge sender's cancelAuthenticationChallenge method must be called. This will trigger a call to the didCancelAuthenticationChallenge protocol method. Here is where you may inform the user that the connection failed.

Conclusion

Not all iOS apps that connect to a web service need to implement a mutual authentication scheme. They should connect to a web service that requires OAuth authentication. For those apps that transfer sensitive customer data, mutual authentication is the best protection.

Also read: