Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port MASTG-TEST-0022: Testing Custom Certificate Stores and Certificate Pinning (android) (by @guardsquare) #3035

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
69 changes: 65 additions & 4 deletions Document/0x05g-Testing-Network-Communication.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,17 @@ The default configuration for apps targeting Android 6.0 (API level 23) and lowe
</base-config>
```

#### Certificate Pinning
### Certificate Pinning

The Network Security Configuration can also be used to pin [declarative certificates](https://developer.android.com/training/articles/security-config.html#CertificatePinning "Certificate Pinning using Network Security Configuration") to specific domains. This is done by providing a `<pin-set>` in the Network Security Configuration, which is a set of digests (hashes) of the public key (`SubjectPublicKeyInfo`) of the corresponding X.509 certificate.
[Certificate pinning](0x04f-Testing-Network-Communication.md/#restricting-trust-identity-pinning) can be employed in Android apps to safeguard against Machine-in-the-Middle (MITM) attacks by ensuring that the app communicates exclusively with remote endpoints possessing specific identities.

While effective when implemented correctly, insecure implementations potentially enable attackers to read and modify all communication. For more general details on pinning, refer to @MASWE-0047.

Several approaches to certificate pinning exist, depending on the app's API level and the libraries used. Below, we highlight the most common methods. For a deeper dive into the specific implementations, see ["Deep Dive into Certificate Pinning on Android"](https://securevale.blog/articles/deep-dive-into-certificate-pinning-on-android/).

#### Pinning via Network Security Configuration (API 24+)

The **Network Security Configuration (NSC)** is the preferred and recommended way to implement certificate pinning in Android, as it provides a declarative, maintainable, and secure approach without requiring code changes. It applies to all network traffic managed by the Android framework within the app, including `HttpsURLConnection`-based connections and `WebView` requests (unless a custom `TrustManager` is used). For communication from native code, NSC does not apply, and other mechanisms need to be considered.

When attempting to establish a connection to a remote endpoint, the system will:

Expand All @@ -105,9 +113,9 @@ If at least one of the pinned digests matches, the certificate chain will be con
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config>
Use certificate pinning for OWASP website access including sub domains
<!-- Use certificate pinning for OWASP website access including sub domains -->
<domain includeSubdomains="true">owasp.org</domain>
<pin-set expiration="2018/8/10">
<pin-set expiration="2028-12-31">
<!-- Hash of the public key (SubjectPublicKeyInfo of the X.509 certificate) of
the Intermediate CA of the OWASP website server certificate -->
<pin digest="SHA-256">YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=</pin>
Expand All @@ -119,6 +127,59 @@ If at least one of the pinned digests matches, the certificate chain will be con
</network-security-config>
```

**Important Considerations:**

- **Backup Pins:** Always include a backup pin to maintain connectivity if the primary certificate changes unexpectedly.
- **Expiration Dates:** Set an appropriate [expiration date](https://developer.android.com/privacy-and-security/security-config#CertificatePinning) and ensure timely updates to prevent the app from bypassing pinning after the date has passed.
- **Scope of Application:** Be aware that this configuration applies only to connections made using `HttpsURLConnection` or libraries that rely on it. Other networking libraries or frameworks may require separate pinning implementations.

#### Pinning using Custom TrustManagers

Before Network Security Configuration became available, the recommended way to implement certificate pinning was to create a custom `TrustManager` (using `javax.net.ssl` APIs) and override the default certificate validation. You can still use this approach on modern Android versions for flexibility or when you need more direct control.

This approach involves:

1. Loading the server's certificate(s) into a `KeyStore`.
2. Creating a custom `TrustManager` that only trusts the certificate(s) in the `KeyStore`.
3. Initializing an `SSLContext` with the custom `TrustManager`.
4. Applying the custom `SSLContext` as the socket factory for the network connections (e.g., `HttpsURLConnection`).

**Important Note:** This is a **low-level approach and is prone to errors** if not done carefully. Some key considerations include:

- [`SSLSocket` does not automatically verify hostnames](https://developer.android.com/privacy-and-security/security-ssl#WarningsSslSocket), so you must handle this manually using a `HostnameVerifier` with a safe implementation (this includes explicitly checking the return value of `HostnameVerifier.verify()`). More information can be found in the [Android documentation](https://developer.android.com/privacy-and-security/risks/unsafe-hostname).
- [Do **not** include a "trust-all" `TrustManager`](https://developer.android.com/privacy-and-security/security-ssl#UnknownCa) that silently accepts all certificates. This opens the door for attackers to intercept and modify user data with minimal effort.
- Certificates loaded from raw resources can be extracted if someone repackages the APK. As with NSC, that risk is mitigated by Android's APK signing model, though for highly sensitive apps, additional protections (obfuscation, integrity checks) may be warranted.

#### Pinning using Third-party Libraries

Several third-party libraries offer built-in support for certificate pinning, simplifying the implementation process in some cases. These libraries typically utilize the custom `TrustManager` method, providing higher-level abstractions and additional features. Notable examples include:

For example, [OkHttp](https://github.com/square/okhttp)'s offers pinning in its `CertificatePinner`. Under the hood, it uses a custom `TrustManager` to enforce pinning rules.

#### Pinning in WebViews

For in-app `WebView` traffic on Android, the easiest approach is to rely on the **Network Security Configuration**. Since Android automatically applies NSC rules to WebView traffic within the same application, any pinning rules you set up in `network_security_config.xml` will also apply to resources loaded in that WebView.

If you need additional customization beyond what NSC offers, you could implement pinning by intercepting requests at the WebView level (e.g., using `shouldInterceptRequest`), but in most cases the built-in support is sufficient and simpler.

#### Pinning in Native Code

It's also possible to implement pinning in [native code](https://developer.android.com/ndk) (C/C++/Rust). By embedding or dynamically verifying certificates within compiled native libraries (`.so` files), you can increase the difficulty of bypassing or modifying the pinning checks via typical APK reverse engineering.

That said, this approach requires significant security expertise and a careful design to manage certificates or public key hashes in native space. Maintenance and debugging also typically become more complex.

#### Pinning in Cross-Platform Frameworks

Cross-platform frameworks like Flutter, React Native, Cordova and Xamarin might require special considerations. Depending on the framework one of the following can apply:

- The framework might support NSC. This is the case for Flutter apps on Android, but the NSC needs to be enabled in the `AndroidManifest`. See the [Flutter documentation](https://docs.flutter.dev/release/breaking-changes/network-policy-ios-android#migration-guide) on how to enable the network policy.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems this "feature" as well as the support for android:usesCleartextTraffic="true" in the manifest was reverted in Flutter 2.2: https://docs.flutter.dev/release/breaking-changes#reverted-change-in-2-2

According to this comment it's still kind of there: flutter/flutter#106678 (comment) but I'm not sure what's going on.

It looks like the way to do pinning in Flutter is via securityContext.setTrustedCertificatesBytes or securityContext.setTrustedCertificates

See https://api.flutter.dev/flutter/dart-io/SecurityContext/setTrustedCertificatesBytes.html


- The framework might use other networking libraries under the hood, which need to be configured appropriately. E.g., React Native uses OkHttp on Android, which can be configured with a custom `CertificatePinner`.

- The framework might offer plugins to perform certificate pinning. This is the case for example for Cordova.

- The framework might not offer any built-in mechanisms to perform certificate pinning (as it the case for Xamarin). In this case, pinning needs to be implemented manually.

### Security Provider

Android relies on a [security provider](https://developer.android.com/training/articles/security-gms-provider.html "Update your security provider to protect against SSL exploits") to provide SSL/TLS-based connections. The problem with this kind of security provider (one example is [OpenSSL](https://www.openssl.org/news/vulnerabilities.html "OpenSSL Vulnerabilities")), which comes with the device, is that it often has bugs and/or vulnerabilities.
Expand Down
30 changes: 30 additions & 0 deletions tests-beta/android/MASVS-NETWORK/MASTG-TEST-0240.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
title: Missing Certificate Pinning in Network Security Configuration
platform: android
id: MASTG-TEST-0240
type: [static]
weakness: MASWE-0047
---

## Overview

Apps can configure certificate pinning using the [Network Security Configuration]("../../../Document/0x05g-Testing-Network-Communication.md#certificate-pinning"). For each domain, one or multiple digests can be pinned.

The goal of this test is to check if any certificate pinning exists.

!!! note "Limitations"
Since there are many different ways to achieve certificate pinning in the code, checking statically if the application performs pinning might not reveal all such locations. To make sure certificates are pinned for all relevant connections, additional dynamic analysis can be performed.

## Steps

1. Reverse engineer the app (@MASTG-TECH-0017).
2. Inspect the AndroidManifest.xml, and check if a `networkSecurityConfig` is set in the `<application>` tag.
3. Inspect the referenced network security config file, and extract all domains which have a pinned certificate.

## Observation

The output should contain a list of domains which enable certificate pinning.

## Evaluation

The test case fails if no `networkSecurityConfig` is set, or any relevant domain does not enable certificate pinning.
30 changes: 30 additions & 0 deletions tests-beta/android/MASVS-NETWORK/MASTG-TEST-0241.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
title: Expired Certificate Pins in the Network Security Configuration
platform: android
id: MASTG-TEST-0241
type: [static]
weakness: MASWE-0047
---

## Overview

Apps can configure expiration dates for pinned certificates in the [Network Security Configuration (NSC)]("../../../Document/0x05g-Testing-Network-Communication.md#certificate-pinning") by using the `expiration` attribute. When a pin expires, the app no longer enforces certificate pinning and instead relies on its configured trust anchors. This means the connection will still succeed if the server presents a valid certificate from a trusted CA (such as a system CA or a custom CA defined in the app's configuration). However, if no trusted certificate is available, the connection will fail.

If developers assume pinning is still in effect but don't realize it has expired, the app may start trusting CAs it was never intended to.

> Example: A financial app previously pinned to its own private CA but, after expiration, starts trusting publicly trusted CAs, increasing the risk of compromise if a CA is breached.

The goal of this test is to check if any expiration date is in the past.

## Steps

1. Reverse engineer the app (@MASTG-TECH-0017).
2. Inspect the AndroidManifest.xml, and check if a `networkSecurityConfig` is set in the `<application>` tag. If yes, inspect the referenced file, and extract the expiration dates for every domain.

## Observation

The output should contain a list of expiration dates for pinned certificates.

## Evaluation

The test case fails if any expiration date is in the past.
29 changes: 29 additions & 0 deletions tests-beta/android/MASVS-NETWORK/MASTG-TEST-0242.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
title: Missing Certificate Pinning in Network Traffic
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a second dynamic test that uses Frida e.g. via objection or other scripts that try to bypass pinning in order to detect it. There are caveats of course as the original test indicates but it's useful.

See https://mas.owasp.org/MASTG/techniques/android/MASTG-TECH-0012/

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cpholguera not sure I understand what you mean. The link you mention explains how to deactivate pinning using Frida etc. How would that help identifying if pinning is done / done correctly?

What could be (somewhat) helpful, is to try the Frida scripts to see where it attaches, to then statically analyse those locations (relating to the to-be-created test for #3183).

Am I missing something?

platform: network
id: MASTG-TEST-0242
type: [network]
weakness: MASWE-0047
---

## Overview

There are multiple ways an application can implement certificate pinning, including via the Android Network Security Config, custom TrustManager implementations, third-party libraries, and native code. Since some implementations might be difficult to identify through static analysis, especially when obfuscation or dynamic code loading is involved, this test uses network interception techniques to determine if certificate pinning is enforced at runtime.

The goal of this test case is to observe whether a [MITM attack]("../../../Document/0x04f-Testing-Network-Communication.md#mitm-attack) can intercept HTTPS traffic from the app. A successful MITM interception indicates that the app is either not using certificate pinning or implementing it incorrectly.

If the app is properly implementing certificate pinning, the MITM attack should fail because the app rejects certificates issued by an unauthorized CA, even if the CA is trusted by the system.

## Steps

1. Set up an intercepting proxy, for example @MASTG-TOOL-0077 or @MASTG-TOOL-0097.
2. Install the application on a device connected to that proxy, and intercept the communication.
3. Extract all domains which were intercepted.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And monitor logcat for clues while doing this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cpholguera which clues do you have in mind?
The way I did this before was to just look at the intercepting proxy, logcat from the device never added anything too useful for this particular test.


## Observation

The output should contain a list of domains for which the interception was successful.

## Evaluation

The test case fails if any relevant domain was intercepted.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about...?

Suggested change
The test case fails if any relevant domain was intercepted.
The test fails if any **security-sensitive domain** was intercepted.

We could add a new prerequisite and link to it here. As in https://github.com/OWASP/owasp-mastg/blob/master/prerequisites/identify-security-relevant-contexts.md

For example (REVIEW, AI generated):

Identifying Security-Sensitive Relevant Domains

What is a Security-Sensitive Relevant Domain?

A relevant domain is any endpoint where failing to enforce certificate pinning could expose sensitive data or compromise security. This typically includes:

  • Authentication endpoints (e.g., login APIs, OAuth/token exchanges)
  • Endpoints handling sensitive user data (e.g., financial transactions, personal data transfers)
  • Endpoints that initiate encrypted tunnels (e.g., VPN, secure messaging services)
  • Internal API endpoints used for secure app-server communication

What is NOT a Security-Sensitive Relevant Domain?

  • Third-party CDNs or analytics services that the app does not control

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cpholguera I agree this would benefit a lot from a definition. Maybe sth to discuss in our next meeting, I don't think this is easy to define.

Imo, security-sensitive depends a lot on the app context, but also on the test / weakness itself. I could imagine certificate pinning being mandatory for L2 for specific domains, but it certainly shouldn't be mandatory for all domains for L1.

In the AI generated text, the 3rd party or analytics service is already not ideal imo. Whilst I agree with this for certificate pinning, I don't think analytics services are by default not security sensitive. E.g., thinking about GDPR, I can imagine personal identifiers being sent there, which need to be handled with care and are security sensitive...

3 changes: 3 additions & 0 deletions tests/android/MASVS-NETWORK/MASTG-TEST-0022.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ platform: android
title: Testing Custom Certificate Stores and Certificate Pinning
masvs_v1_levels:
- L2
status: deprecated
covered_by: [MASTG-TEST-0x22-1,MASTG-TEST-0x22-2,MASTG-TEST-0x22-3]
deprecation_note: New version available in MASTG V2
---

## Overview
Expand Down