NOWSECURE NOW AVAILABLE IN THE MICROSOFT AZURE MARKETPLACE

Microsoft Azure customers gain access to NowSecure Mobile App Security and Privacy Testing for scalability, reliability, and agility of Azure to drive mobile appdev and shape business strategies.

Media Announcement
NOWSECURE NOW AVAILABLE IN THE MICROSOFT AZURE MARKETPLACE NOWSECURE NOW AVAILABLE IN THE MICROSOFT AZURE MARKETPLACE Show More
magnifying glass icon

Certificate pinning for Android and iOS: Mobile man-in-the-middle attack prevention

Posted by

Rono Dasgupta

Mobile Security Analyst
At NowSecure, Rono finds vulnerabilities in mobile technology and attempts to exploit them. He is passionate about helping developers understand and improve the security of their products and systems. Rono holds a master's degree in security informatics from Johns Hopkins University.
Certification Pinning

Implementing certificate pinning in mobile apps that handle highly sensitive data provides too much benefit to be passed over. Enterprises and developers for whom security is not a high priority, however, may decide against implementing certificate pinning due to challenges arising from improper configurations. Initially positioned to solve the problem of compromised CAs, cert pinning has proven to defend against another type of man-in-the-middle (MITM) attack. Specifically, if a user can be tricked into installing a malicious self-signed certificate on a mobile device, an attacker can execute a MITM attack on them. While traditional certificate validation (without pinning) does protect apps against many types of MITM attacks, it doesn’t prevent all of them. When a user is tricked into installing a malicious certificate, certificate pinning can still prevent the interception of an app’s network traffic.

Certificate pinning refers to the security practice of validating the certificates used in your application requests against publicly known certificates administered by certificate authorities. When a mobile app makes a request to a back-end server, a number of checks may occur and cert pinning is one of them.

This check relies on publicly available information, and confirms that the server the mobile app has requested information from is one with a verified certificate. It can protect your application from man-in-the-middle attacks.

Why Thotcon reminded me of mobile man-in-the-middle attacks

On my way to Chicago’s biggest security conference last month — Thotcon — I disabled my Android device’s Wi-Fi. I didn’t want to provide any 1337 hackers an opportunity to compromise my mobile data. The organizers offered free, secure Wi-Fi that required installing a device profile or certificates. This made me think about how easily users can be convinced to make changes to their device that make them vulnerable to a mobile man-in-the-middle attack.

Many attendees connected to the Thotcon Wi-Fi access point using these instructions provided by the organizers:

Information about Thotcon Wi-Fi access certificate pinning

Set up included downloading and installing either a certificate authority (CA) bundle in the .crt file format or an iOS profile in the .mobileconfig format. Initially, I was shocked that people would do such a thing (installing certificates can be dangerous, I’ll explain why below). Upon examining the actual certificate, however, I discovered there was nothing to worry about. The certificate was solely used for server and client authentication. Thotcon had configured a secure 802.1x EAP-enabled Wi-Fi access point.

Here’s the iOS certificate used for Wi-Fi authentication at Thotcon:

iOS certificate for Thotcon Wi-Fi authentication

A hypothetical man-in-the-middle attack on iOS devices

Security-minded people, such as Thotcon attendees, probably double-check a profile before downloading it. But, many users wouldn’t think twice. We know that phishing works. The State of the Phish Report 2017 from Wombat reported that 76 percent of information security professionals said their organization had fallen victim to a phishing attack.

Imagine someone arriving at a conference venue and attempting to connect to Wi-Fi on their device. Say that a Wi-Fi SSID is broadcasted with a name similar to the conference’s legitimate access point. During the connection attempt, the user is redirected to a website that looks authentic and instructs them to download and install a profile onto their device.

For our purposes, let’s assume that the user has an urgent need to access the Internet and so installs the profile, ignoring the rather vague and innocuous warnings. The user is happy to have free Wi-Fi access unaware that an attacker has ensnared them, gaining complete access to the network traffic and the ability to decrypt nearly everything sent to and received by the device.

Mobile man-in-the-middle attacks do not require physical access to a device

An attacker does not always need physical access to a device in order to install a malicious root CA and compromise a user’s network traffic.

What is an iOS profile and how can it be used against me?

iOS profiles push custom configuration settings onto an iOS device. Typically, network administrators use this method to efficiently set up secure Wi-Fi access or VPNs on employee devices. Apple makes it easy to build custom iOS profiles with the free Apple Configurator 2.

Apple Configurator 2 allows you to add several settings to the profile including:

  • Custom certificates for installation
  • Network settings (e.g., what wireless network the device should join automatically
  • Proxy settings
  • VPN settings

Here’s a screen shot of the tool’s UI:

Apple Configurator 2 user interface

Distributing a malicious iOS profile

Once the attacker builds the profile, they need a way to push it onto the devices of unsuspecting users — enter social engineering. The easiest way is to use a captive portal for Wi-Fi access. A captive portal is a web page that requires a user to enter information or accept terms after connecting with a Wi-Fi network, but before they’re granted Internet access (e.g., at a coffee shop, hotel, or airport).

Researchers at SensePost developed a tool called Mana for Defcon 22 to show just how easy it is to MITM unsuspecting people using rogue Wi-Fi access points and captive portals. When a victim connects to the rogue access point, they are redirected to a captive portal on their mobile browser that tells them to download the malicious iOS profile. Once the profile is downloaded, the user is prompted to install it.

Installing an iOS profile

You can see that the profile in this case is marked “Not signed,” but an attacker can easily sign the profile using a code-signing certificate.

Tricking users into installing malicious iOS profiles

The malicious profile presented to the user contains a self-signed certificate. The certificate needs to be self-signed for the device to install it as a root CA. Clicking on “Install” takes the user to a page that warns the user that installing the certificate will add it to the list of trusted certificates on their device (see screenshot below). This warning clearly fails to convey the actual danger of installing the certificate — that network traffic could be compromised.

iOS certificate installation warning

Once a user installs the profile, which adds the certificate to a device’s list of trusted certificates, an attacker that is able to redirect all network traffic through their proxy can basically decrypt all the HTTPS traffic sent and received by the device. From the attacker’s perspective, carrying out this kind of MITM attack required very little time or effort. While profiles don’t exist for Android devices, an attacker could still trick an unsuspecting user into installing certificates onto the device using similar techniques.

Tricking users into installing malicious certificates on Android devices

On Android, the user has to install the certificate manually using the device’s security settings and under “Credential storage” tapping “Install from storage.” The user then has to select the downloaded certificate file in the .cer format and install it.

Please note: By default, apps don’t work with user-installed CA certificates on Android 7.0 and up. The “Trusted Credentials” setting under “Credential Storage” is now divided into two sections — “User” and “System”. Only “System” CAs are trusted by default. This is a fantastic security feature that all platforms should adopt in the near future. App developers can choose to let their apps work with manually added CA certificates, but they need to understand the security ramifications of doing so.

Certificate pinning: Mobile man-in-the-middle attack prevention

An essential part of mobile man-in-the-middle attack prevention is user education. Especially telling users that regardless of how urgently they need to access Wi-Fi, they should be wary of any requests to install a profile or certificate on their device. However, even with security awareness training, users continue to fall victim to phishing schemes and the like.

Beyond user education, mobile app developers can also help protect users against mobile man-in-the-middle attacks on Android or iOS by implementing certificate pinning in their apps. Cert pinning was originally created to protect against the threat of a rogue CA. Pinning also ensures that none of your app’s network data is compromised even if a user has a malicious root certificate installed on their device. In the hypothetical phishing and profile installation scenario, any app that pins its certificates would fail to connect rather than transmit any data sent over a compromised connection. Certificate pinning is a great way to make mobile apps that handle sensitive information that much more secure and protect users too.

By design, apps generally trust the root CAs installed on the device by the manufacturer. Certificate pinning “pins” the certificates your app trusts so that the app rejects certificates presented by an untrusted server and kills the connection. If a connection is killed, you may inconvenience the user, but isn’t that better than allowing the user and your app to fall victim to a network MITM attack?

If certificate pinning is so great, why don’t all enterprise mobile apps use it?

At NowSecure, I routinely test mobile apps for security vulnerabilities, which includes full network penetration testing. Most of the apps I come in contact with satisfy minimum network security requirements including communicating over HTTPS, performing certificate validation, and using HTTP Strict Transport Security (HSTS) to prevent downgrade attacks. Developers, however, don’t seem to have adopted certificate pinning with the same gusto. And some developers seem reluctant to implement it in their apps.

Below are some reasons developers might not implement certificate pinning, along with my suggestions for why developers of apps that handle sensitive information should still consider it.

  1. Implementing certificate pinning is too complex, forces us to rewrite our code, and complicates app builds in our staging/QA environments.
    There are some simple ways to implement certificate pinning in your app, some of which I explain later in this post. Also, public-key pinning can make migrating code between development environments easier. Public key pinning pins the public key of the certificate, which remains constant across multiple certificates. This allows you to use the same public key to issue new certificates without having to rewrite any code for different environments.
  2. Pinning certificates that are bound to change on a regular basis (rolling certificates) would force us to update the app binary every time the certificate changes.
    Pinning the hashed public keys of the certificates can also help with this issue. When doing this, the public key of the certificates has to remain static across all new certificates. In addition to this, backup keys should always be added to the pinning configuration to maintain app connectivity at all times.
  3. Using HTTPS for app communications and verifying the certificate authority chain-of-trust is enough to keep our app secure.
    Regarding the hypothetical attack discussed earlier, if a malicious root CA is installed on the device and the device is connected to an attacker’s server, the attacker can still successfully intercept, read, and modify network traffic.
  4. Certificate pinning is unnecessary because a CA compromise is unlikely.
    Certificate pinning has done a great job reducing the threat of a rogue CA. However, as explained earlier, a CA compromise is not the only vector for a mobile MITM attack.

Can certificate pinning be bypassed?

To bypass proper certificate pinning, an attacker would need physical access to the targeted mobile device. From there, the attacker would need to root or jailbreak the device and modify the functions performing certificate pinning during runtime. A number of tools exist that can perform these steps.

In the end, bypassing certificate pinning is difficult in comparison to the attacks certificate pinning can protect your app against.

How to implement certificate pinning on Mobile apps?

The first thing to determine based on your requirements is what you actually want to ‘pin’ in the application – the certificate itself or the public key of the certificate. Pinning the public key is often the preferred option since it is easier to manage when the server certificates change. The public keys can remain static, removing the necessity to push updates to your app every time the certificates are updated.

Certificate pinning for Android

Manual methods for deploying cert pinning on Android apps include making `HttpsURLConnection` only trust only a certain set of CAs. For more detail about this manual method, see the OWASP Certificate and Public Key Pinning Technical Guide.

The Android Developer website describes a newer technique for certificate pinning on Android which involves providing hashes of certificates’ public keys along with backup keys in an app’s `res/xml/network_security_config.xml` file. Backup keys help maintain app usage if and when the CA being pinned or the keys themselves have to be changed for some reason. If you don’t include backup keys, you will be forced to update your app in such cases.

The following snippet from the Android Developer site shows the res/xml/network_security_config.xml file.

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">example.com</domain>
        <pin-set expiration="2018-01-01">
            <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
            <!-- backup pin -->
            <pin digest="SHA-256">fwza0LRMXouZHRC8Ei+4PyuldPDcf3UKgO/04cDM1oE=</pin>
        </pin-set>
    </domain-config>
</network-security-config>

You can use a number of third-party libraries to implement certificate pinning in your Android apps. The popular Android library, OkHttp is widely used for this purpose through its `CertificatePinner` class. The following three code snippets from the OkHttp website illustrate the process.

String hostname = "<yourdomain.com>";
     CertificatePinner certificatePinner = new CertificatePinner.Builder()
         .add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
         .build();
     OkHttpClient client = OkHttpClient.Builder()
         .certificatePinner(certificatePinner)
         .build();

     Request request = new Request.Builder()
         .url("https://" + hostname)
         .build();
     client.newCall(request).execute();

The code snippet above deliberately includes a false SHA256 hash value to simulate a broken configuration, and so it will throw an exception (the output shown in the snippet below). Looking at the output, you can find your server keys if you don’t already know them. The SHA256 hashes in the resulting output are for your server certificates’ public keys.

 javax.net.ssl.SSLPeerUnverifiedException: Certificate pinning failure!
   Peer certificate chain:
     sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=: CN=publicobject.com, OU=PositiveSSL
     sha256/klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=: CN=COMODO RSA Secure Server CA
     sha256/grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=: CN=COMODO RSA Certification Authority
     sha256/lCppFqbkrlJ3EcVFAkeip0+44VaoJUymbnOaEUk7tEU=: CN=AddTrust External CA Root
   Pinned certificates for publicobject.com:
     sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
   at okhttp3.CertificatePinner.check(CertificatePinner.java)
   at okhttp3.Connection.upgradeToTls(Connection.java)
   at okhttp3.Connection.connect(Connection.java)
   at okhttp3.Connection.connectAndSetOwner(Connection.java)

You will need to paste those SHA256 hashes into the `CertificatePinner` configuration as shown here:

CertificatePinner certificatePinner = new CertificatePinner.Builder()
       .add("publicobject.com", "sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=")
       .add("publicobject.com", "sha256/klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=")
       .add("publicobject.com", "sha256/grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=")
       .add("publicobject.com", "sha256/lCppFqbkrlJ3EcVFAkeip0+44VaoJUymbnOaEUk7tEU=")
       .build();

TrustKit for Android is another third-party library you can use to help you implement certificate pinning in your mobile apps.

Certificate pinning for iOS

On iOS, certificate pinning can be achieved through a `NSURLConnectionDelegate` using `NSURLConnection`. See the OWASP Certificate and Public Key Pinning Technical Guide for more detail about this method.

Developers can also use the TrustKit library for implementing certificate pinning on iOS. The following code snippet from the Trustkit repository shows how to enable certificate pinning in Objective-C apps.

NSDictionary *trustKitConfig =
  @{
    kTSKSwizzleNetworkDelegates: @NO,
    kTSKPinnedDomains : @{
            @"www.datatheorem.com" : @{
                    kTSKExpirationDate: @"2017-12-01",
                    kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048],
                    kTSKPublicKeyHashes : @[
                            @"HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=",
                            @"0SDf3cRToyZJaMsoS17oF72VMavLxj/N7WBNasNuiR8="
                            ],
                    kTSKEnforcePinning : @NO,
                    kTSKReportUris : @[@"http://report.datatheorem.com/log_report"],
                    },
            @"yahoo.com" : @{
                    kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa4096],
                    kTSKPublicKeyHashes : @[
                            @"TQEtdMbmwFgYUifM4LDF+xgEtd0z69mPGmkp014d6ZY=",
                            @"rFjc3wG7lTZe43zeYTvPq8k4xdDEutCmIhI5dn4oCeE=",
                            ],
                    kTSKIncludeSubdomains : @YES
                    }
            }};
    
    [TrustKit initializeWithConfiguration:trustKitConfig];

TrustKit can also be used for apps written in Swift as shown in this code snippet (also from the TrustKit Github repository).

let trustKitConfig = [
            kTSKSwizzleNetworkDelegates: false,
            kTSKPinnedDomains: [
                "yahoo.com": [
                    kTSKExpirationDate: "2017-12-01",
                    kTSKPublicKeyAlgorithms: [kTSKAlgorithmRsa2048],
                    kTSKPublicKeyHashes: [
                        "JbQbUG5JMJUoI6brnx0x3vZF6jilxsapbXGVfjhN8Fg=",
                        "WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18="
                    ],]]] as [String : Any]
        
        TrustKit.initialize(withConfiguration:trustKitConfig)

Other third-party libraries that help with certificate pinning on iOS apps include AlamoFire and AFNetworking.

Please note: Update any third-party libraries used for certificate pinning

If you’re already using a third-party library to handle your app’s network communications, make sure you’ve updated to the most recent version. For example, OkHttp versions prior to 2.7.4 and versions 3.x prior to 3.1.2 are vulnerable to man-in-the-middle attacks as a result of not sanitizing the server’s certificate chain. The vulnerability has been patched in the newer versions.

How do I test that certificate pinning has been properly implemented?

The best way to test your app’s certificate pinning is to execute a MITM attack against it. For example, if an app relies on a vulnerable version of OkHttp for certificate pinning, mobile app security testing will identify such a vulnerability. To start, you’ll need some sort of network proxy tool such as Burp Suite, OWASP ZAP, Mitmproxy, or Fiddler to intercept application network communications. In order to intercept traffic, you will need to install your chosen proxy as a trusted root CA on the test device that will run the app. Usually, a tester will need to do this manually (unless you’re using our NowSecure Workstation mobile app penetration testing toolkit).

Once a proxy is installed as a root CA, it essentially acts as a man-in-the-middle and can intercept, decrypt and modify all HTTPS communications between the device, the app, and backend servers.

As you interact with the app as part of a test, and the proxy successfully  intercepts network traffic from your app, your app isn’t performing certificate pinning. If you believe you’ve properly implemented certificate pinning, and the proxy still intercepts your app’s network data, a deeper dive into the app source code may be necessary to determine the problem.

If you need third-party validation of the security of your Android or iOS apps in general, or certificate pinning in particular, the NowSecure services team can help. And for help training mobile app developers in secure coding practices or upskilling security analysts, check out the roster of free mobile security courses available through NowSecure Academy.