The importance of protecting your APIs with SSL pinning

toonwire

Toonwire

Posted on November 20, 2020

The importance of protecting your APIs with SSL pinning

Preface

Networking is a core part of most apps, passing information between the application client and backend system(s). A very standard approach to this communication is TLS, a protocol meant for message integrity and message privacy between communicating parties. When establishing a connection using the TLS protocol, the first step is the supplying and verification of certificates.

E.g. a client initiates a request and the server responds with one or more digital certificates, which are then verified (or not) by the client. This verification establishes trust between the communicating entities. Once trust has been established, the cryptograhpic step can commence and finally the information exchanges can take place, using said encryption/decryption schemes.

So with SSL certificates set up and the TLS protocol invoked, our precious and secret information is securely communicated between front- and backend systems, right?

Many developers will assume so (or do not care about their network security), leaving handling of the TLS session to the OS.

Quick history tour

Any app will by default trust the system root certificate, but will on versions before Android 7 (Nougat), by default also trust user-defined certificates just as much. This being an obvious security issue, without developer intervention, was then changed in Android 7 and onwards to no longer accepting user-defined certificates by default. While this does make it so that the communication cannot be hijacked out-of-the-box, a very simple effort will open the security issue right back up.

Let's see how easy it is to listen in on a supposedly secure channel.

Setting the stage

Taking a look at a real world example, I have chosen the official parking app from the Swedish municipality of Gothenburg, Parkering Göteborg.

Being a parking app, the information flowing about includes the license plates of cars, credit cards for charging, along with personal user information etc. Information that really should be kept secure.

DISCLAIMER: I have been in contact with the developers of this app roughly 6 months ago, and made them aware of the issues presented here. Since then, the application have received numerous updates - including its security aspects.

Exposing HTTPS traffic

Here is a short outline of how to expose TLS communication in Android apps which rely on the OS to handle everything about the session.

Tools needed:

The Java build tools and Android SDK are only used for signing the apk.

Make the app trust user Certificate Authorities

Since apps will not trust user certificate authorities (since Android 7) just because the device trusts them, a small modification to the app must be made.

Download apk of target app

Usually you can find the app at APKMirror or similar sites.
E.g. APKPure

Decode apk file

apktool d app.apk -o decoded_app

Modify AndroidManifest.xml

Inside the decoded_app folder, find and open up the AndroidManifest.xml file.
Add android:networkSecurityConfig="@xml/network_security_config" to the application tag.

<!-- AndroidManifest.xml -->
...
<application 
    android:allowBackup="false" 
    android:appComponentFactory="androidx.core.app.CoreComponentFactory" 
    android:icon="@mipmap/ic_launcher" 
    android:label="@string/parking_application_name" 
    android:name="se.goteborg.pbolaget.android.ParkingApplication" 
    android:theme="@style/AppTheme"
    android:networkSecurityConfig="@xml/network_security_config"
    >
...
Enter fullscreen mode Exit fullscreen mode

Trust user Certificate Authorities

To make the app trust user-defined certificates once again, the network security configuration file simply needs to add the user argument to a certificate tag.

The network_security_config.xml file

<!-- network_security_config.xml -->
<network-security-config>  
    <base-config>  
        <trust-anchors>
            <!-- Additionally trust user added CAs -->  
            <certificates src="user" />  
        </trust-anchors>  
    </base-config>  
</network-security-config>
Enter fullscreen mode Exit fullscreen mode

Add network_security_config.xml to the decoded_app/res/xml folder.

Rebuild apk file

apktool b decoded_app -o ca_modified_app.apk

The new apk file can then be found at either decoded_app/dist/ca_modified_app.apk or as a sibling to the decoded_app folder.

Sign the modified app

Here, I am using the default created android debug keystore to sign the apk
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore {some_path_x}\.android\debug.keystore {some_path_y}\ca_modified_app.apk androiddebugkey -storepass android

Install the modified app

Transfer the ca_modified_app.apk to an Android device and press to install.

You may have to trust installations from outside Google Play (should prompt).

We now effectively have a copy of the original app installed on our device, which trusts user-defined certificates.
Now we just need to install one such certificate.

Install Charles' SSL certificate

On your device visit https://chls.pro/ssl to download the Charles SSL certificate.

After the download completes, install the certificate.

Man-in-the-middle

This step sets up your PC to act as an intermediary between the client and the server, using the HTTP(S) proxy.

Open network settings for the WiFi network your device is connected to.

Wi-Fi → Long press connected network → Modify network → Advanced options → Set Proxy to Manual → As hostname; enter the IPv4 address of your computer, which must be connected to the same network → As port, CharlesProxy defaults to 8888.

Enable SSL Proxy in Charles

Top menu → Proxy → SSL Proxying Settings → SSL Proxying → Check Enable SSL Proxying → Include location host = * and port = * (wildcards)

Reading the network traffic

Now, browsing the app will redirect all network traffic through the SSL proxy, which certificate is trusted by the device, making the traffic visible to the proxy (Charles), with exposed and unencrypted https requests and responses.

Exposed API of Parkering Göteborg

SSL pinning

So what can we, as developers, do to avoid this issue? Que SSL pinning.

SSL pinning is a technique which entails embedding your server certificate into the app itself. Once networking happens, the app can simply compare the certificate of the responding entity to the embedded certificate. If they do not match, chances are the response is not coming from our server. For more information on SSL security implementations, check out the docs.

The but

Although SSL pinning is neat, this security installment can be bypassed. However it is a much more involved process, and requires rooting the device amongst other things. Alas, we can rarely achieve complete security.

Updates

The most recent Android version, Android 11 (September 8. 2020), introduces further certificate installation restrictions, however seemingly only those prompted by applications. So while this prevents app automation of the man-in-the-middle attack presented here, the manual process will likely still work.

I hope you enjoyed this blog post. If you have any questions, feel free to comment below!

💖 💪 🙅 🚩
toonwire
Toonwire

Posted on November 20, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related