HTTPS & Network Security Config

Every networking lesson in this section – OkHttp, Retrofit, Volley, WebSockets – assumed requests travel over a secure connection. This lesson explains why that assumption matters, and how Android lets you control it precisely.

Why Plain HTTP Is Risky

Regular HTTP traffic is sent as plain text. Anyone sharing the same network – a public Wi-Fi hotspot, a compromised router, anyone between the phone and the server – can read every byte, including recipe data, auth tokens, or session cookies your RecipeBox app sends. Worse, they can also modify the traffic in transit, injecting fake responses your app would trust completely. HTTPS encrypts the connection so that eavesdropping and tampering both become impractical. For any app handling real user data, HTTPS is not an optional nicety – it is the baseline.

usesCleartextTraffic Defaults to false

Recognizing how risky plain HTTP is, modern Android blocks it by default. The manifest attribute android:usesCleartextTraffic controls this, and starting with apps targeting Android 9 (API 28) and above, it defaults to false – meaning a plain http:// request from your app is refused by the OS before it even leaves the device.

<!-- AndroidManifest.xml -->
<application
    android:usesCleartextTraffic="false"
    android:networkSecurityConfig="@xml/network_security_config"
    ... >
Explanation: this is a deliberate security improvement, not an accident. If your RecipeBox backend is fully on HTTPS (as it should be), you never need to touch this attribute – the secure default already protects you.

Network Security Config for Fine-Grained Control

Sometimes you genuinely need an exception – most commonly, allowing cleartext traffic only against a local development server while still blocking it everywhere else. A Network Security Config XML file gives you that precision instead of an all-or-nothing switch.

<!-- res/xml/network_security_config.xml -->
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">10.0.2.2</domain>
    </domain-config>

    <base-config cleartextTrafficPermitted="false" />
</network-security-config>
Explanation:
  • The <domain-config> block permits cleartext only for 10.0.2.2, the Android emulator's alias for your development machine's localhost.
  • The <base-config> at the bottom blocks cleartext for every other domain – including your real RecipeBox production API.
  • This is far safer than flipping usesCleartextTraffic to true app-wide, which would weaken security for every connection, not just your local server.

Referencing the Config from the Manifest

<application
    android:networkSecurityConfig="@xml/network_security_config"
    ... >
    ...
</application>

Certificate Pinning (Conceptual)

Even over HTTPS, an app normally trusts any certificate signed by a certificate authority the device already trusts. Certificate pinning adds an extra layer: you tell the app to only trust one or a few specific certificates for a domain, via a <pin-set> entry in the config. This guards against certain man-in-the-middle scenarios where an attacker has obtained a technically-valid certificate from a different authority.

<domain-config>
    <domain includeSubdomains="true">api.recipebox.example.com</domain>
    <pin-set>
        <pin digest="SHA-256">base64EncodedPinHere==</pin>
    </pin-set>
</domain-config>
This lesson keeps pinning at a conceptual level – it is a real technique worth knowing exists, but setting it up correctly (including a backup pin so you aren't locked out on certificate rotation) is a deeper topic of its own.

Trusting Self-Signed Certificates the Right Way

During local development it is common to run a server with a self-signed certificate. The tempting shortcut is to disable certificate validation entirely in code – don't do this. It silently makes every HTTPS connection in that build insecure, and this kind of shortcut has a habit of accidentally surviving into a release build.

Insecure shortcut to avoid: writing a custom TrustManager or HostnameVerifier that accepts every certificate unconditionally. This defeats HTTPS entirely, for every server your app talks to, not just your local one.

The correct approach is an explicit <trust-anchors> entry naming exactly the self-signed certificate you want trusted, scoped to your development domain only:

<domain-config>
    <domain includeSubdomains="true">10.0.2.2</domain>
    <trust-anchors>
        <certificates src="@raw/dev_cert" />
    </trust-anchors>
</domain-config>
Explanation: only connections to 10.0.2.2 trust this specific certificate. Every other domain, including production, still relies on the normal system certificate store – no blanket weakening of certificate checking anywhere in the app.
Next up: Services & Foreground Service – running work in the background, independent of any single screen.