Android Coden
Android 8 min lesen

Network Security Config

Lerne, wie du Netzwerkregeln für Release und Debug sauber trennst. So kontrollierst du Cleartext, Zertifikate und Test-Setups bewusst.

Network Security Config ist ein kleines, aber wichtiges Werkzeug für Android-Apps, die über HTTP oder HTTPS mit Servern sprechen. Du beschreibst damit nicht deine komplette Netzwerklogik, sondern die Sicherheitsregeln um sie herum: Welche Domains dürfen angesprochen werden, ob unverschlüsselter Cleartext erlaubt ist und welchen Zertifikaten deine App vertraut. Gerade wenn du mit Kotlin, Jetpack, Compose und einer klaren Data Layer arbeitest, gehört diese Konfiguration zur Release-Qualität deiner App.

Was ist das?

Network Security Config ist eine XML-Konfiguration, die du in deiner Android-App hinterlegst und im Manifest aktivierst. Sie sagt dem Android-System, welche Netzwerkverbindungen aus Sicht deiner App akzeptabel sind. Das betrifft vor allem drei Bereiche: Cleartext-Traffic, Zertifikate und spezielle Debug-Regeln.

Cleartext meint unverschlüsselten Datenverkehr, meistens über http:// statt https://. In einer produktiven App ist das fast immer falsch, weil Inhalte unterwegs gelesen oder verändert werden können. HTTPS ist deshalb der Normalfall. Trotzdem gibt es in der Entwicklung Situationen, in denen du kurzzeitig mit einem lokalen Server arbeitest, zum Beispiel 10.0.2.2 im Emulator oder einem internen Testgerät im WLAN. Genau hier brauchst du klare Regeln, damit ein Debug-Szenario nicht versehentlich zur Release-Einstellung wird.

Zertifikate sind der zweite Kernpunkt. Wenn deine App eine HTTPS-Verbindung öffnet, prüft Android, ob das Serverzertifikat vertrauenswürdig ist. Standardmäßig vertraut Android den System-Zertifizierungsstellen. Für interne Testumgebungen, Staging-Server oder lokale Proxys kann es nötig sein, zusätzliche Zertifikate im Debug-Build zu akzeptieren. Diese Ausnahme gehört aber nicht in den Release-Build.

Das mentale Modell ist: Deine Netzwerkbibliothek wie Retrofit, OkHttp oder Ktor stellt die Anfrage, aber Android entscheidet auf Plattformebene mit, ob die Verbindung nach deinen Sicherheitsregeln erlaubt ist. Network Security Config ist also eine Grenze zwischen App-Code und Plattform-Sicherheit. Sie gehört nicht in deine ViewModel-Logik, nicht in Compose-UI und nicht in Repository-Methoden. Sie unterstützt deine Architektur, indem sie Sicherheitsentscheidungen explizit und prüfbar macht.

Im Roadmap-Kontext passt das Thema in die Phase Data, Storage, Networking und Offline-First. Eine saubere Data Layer kapselt Netzwerkzugriffe, Cache und Fehlerbehandlung. Network Security Config ergänzt diese Schicht, indem sie festlegt, unter welchen Bedingungen überhaupt Netzwerkverbindungen aufgebaut werden dürfen. Wenn du offline-first arbeitest, willst du zudem klare Fehlerbilder: Ein fehlendes Netzwerk, ein abgelaufenes Zertifikat und blockierter Cleartext sind unterschiedliche Probleme. Eine explizite Konfiguration hilft dir, diese Fälle im Debugging auseinanderzuhalten.

Wie funktioniert es?

Du legst eine XML-Datei in res/xml/ an, zum Beispiel res/xml/network_security_config.xml. Danach verweist du im AndroidManifest.xml über android:networkSecurityConfig darauf. Android liest diese Datei beim Start deiner App und wendet die Regeln auf Netzwerkverbindungen an, die über die Plattform laufen.

Die wichtigsten Bausteine sind base-config, domain-config, trust-anchors und debug-overrides.

base-config definiert Standardregeln für alle Verbindungen deiner App. Hier kannst du etwa festlegen, dass Cleartext grundsätzlich verboten ist. Das ist eine gute Release-orientierte Ausgangslage, weil einzelne Ausnahmen dann bewusst dokumentiert werden müssen.

domain-config gilt für eine oder mehrere konkrete Domains. Damit kannst du Regeln enger fassen. Wenn eine alte interne API während einer Übergangsphase nur HTTP kann, könntest du Cleartext nur für genau diese Domain erlauben. Das sollte selten passieren und immer als technische Schuld sichtbar bleiben. Besser ist fast immer, den Server auf HTTPS umzustellen.

trust-anchors legt fest, welchen Zertifikatsquellen vertraut wird. Mit src="system" vertraust du den systemweiten Zertifizierungsstellen. Mit src="@raw/mein_ca_zertifikat" kannst du ein eigenes Zertifikat aus deinen Ressourcen einbinden. Das kann für Unternehmensumgebungen sinnvoll sein, ist aber sensibel: Wer ein falsches Zertifikat in den Release-Build packt, erweitert den Vertrauensbereich der App.

debug-overrides ist für Lernende besonders wichtig. Diese Regeln gelten nur, wenn die App als debuggable gebaut wird. Typische Beispiele sind lokale Zertifikate für einen Entwicklungsserver oder ein Proxy-Zertifikat, mit dem du Netzwerkverkehr in einer Debug-Session untersuchst. Der Vorteil ist die Trennung: Release-Regeln bleiben streng, Debug-Regeln können praxisnah sein.

Android unterscheidet dabei nicht danach, ob deine Oberfläche mit Compose oder XML gebaut ist. Auch deine Data Layer kann modern mit Coroutines, Flow und Repository-Pattern arbeiten. Die Sicherheitsentscheidung bleibt trotzdem zentral konfiguriert. Das ist gut, weil du nicht an mehreren Stellen im Code prüfen musst, ob http:// erlaubt ist oder ob eine Test-CA akzeptiert werden soll.

Im Alltag siehst du Network Security Config oft erst, wenn etwas nicht funktioniert. Ein API-Call schlägt fehl, obwohl die URL stimmt. In Logcat steht dann eventuell, dass Cleartext-Traffic nicht erlaubt ist oder dass die Zertifikatskette nicht validiert werden konnte. Das ist kein Grund, schnell eine globale Ausnahme zu setzen. Es ist ein Signal, die gewünschte Umgebung zu prüfen: Ist das ein lokaler Debug-Server, ein Staging-System oder wirklich Production?

Für Release-Praxis und Qualität ist wichtig: Die Konfiguration ist Teil deiner Sicherheitsoberfläche. Sie gehört in Code-Reviews, in Build-Variant-Prüfungen und in CI-Checks. Wenn dein Team getrennte Varianten wie debug, staging und release nutzt, sollte klar sein, welche Datei oder welche Ressourcen in welcher Variante landen. Ein häufiger Fehler ist, eine Debug-Ausnahme in main/res/xml/ abzulegen, obwohl sie nur in debug/res/xml/ stehen sollte.

In der Praxis

Ein sinnvolles Grundmuster ist: In Produktion kein Cleartext, Vertrauen nur in normale System-Zertifikate und Debug-Ausnahmen sauber isolieren. Für lokale Entwicklung kannst du dann gezielt erweitern, ohne die Release-App zu schwächen.

Eine mögliche Release-nahe Konfiguration sieht so aus:

<!-- app/src/main/res/xml/network_security_config.xml -->
<network-security-config>
    <base-config cleartextTrafficPermitted="false">
        <trust-anchors>
            <certificates src="system" />
        </trust-anchors>
    </base-config>

    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">api.example.com</domain>
    </domain-config>

    <debug-overrides>
        <trust-anchors>
            <certificates src="@raw/debug_ca" />
        </trust-anchors>
    </debug-overrides>
</network-security-config>

Im Manifest aktivierst du die Datei:

<application
    android:name=".App"
    android:networkSecurityConfig="@xml/network_security_config"
    android:usesCleartextTraffic="false">
    ...
</application>

Diese Konfiguration sagt: Standardmäßig ist unverschlüsselter Traffic verboten. Die Domain api.example.com bleibt ebenfalls auf HTTPS festgelegt. Nur in debuggable Builds wird zusätzlich das Zertifikat debug_ca akzeptiert. Damit kann dein Debug-Build zum Beispiel mit einem lokalen Testserver oder einem Entwicklungsproxy arbeiten, während der Release-Build davon nichts übernimmt.

Wenn du mit Build-Varianten arbeitest, kannst du noch strenger trennen. Lege eine produktionsnahe Datei unter app/src/main/res/xml/ ab und eine Debug-spezifische Variante unter app/src/debug/res/xml/. Androids Ressourcen-Merging wählt dann je nach Build-Type die passende Datei. Das ist oft übersichtlicher als viele Bedingungen in einer einzigen XML-Datei.

Beispiel für eine Debug-Variante mit lokaler HTTP-Ausnahme:

<!-- app/src/debug/res/xml/network_security_config.xml -->
<network-security-config>
    <base-config cleartextTrafficPermitted="false">
        <trust-anchors>
            <certificates src="system" />
        </trust-anchors>
    </base-config>

    <domain-config cleartextTrafficPermitted="true">
        <domain>10.0.2.2</domain>
        <domain>localhost</domain>
    </domain-config>

    <debug-overrides>
        <trust-anchors>
            <certificates src="@raw/debug_ca" />
        </trust-anchors>
    </debug-overrides>
</network-security-config>

Dazu passt eine einfache Repository-Nutzung in Kotlin. Die Repository-Klasse muss die Sicherheitsdetails nicht kennen. Sie bekommt nur den Fehler, wenn die Verbindung blockiert wird:

class ArticleRepository(
    private val api: ArticleApi
) {
    suspend fun loadArticles(): Result<List<Article>> {
        return runCatching {
            api.getArticles()
        }
    }
}

Das ist Absicht. Dein Repository bleibt für Datenzugriff, Mapping und Fehlerweitergabe zuständig. Die Frage, ob Cleartext erlaubt ist oder ein Zertifikat akzeptiert wird, bleibt in der Plattformkonfiguration. So vermeidest du verstreute Sonderfälle im Code.

Eine praktische Entscheidungsregel lautet: Jede Netzwerk-Ausnahme muss eine Umgebung und einen Besitzer haben. Wenn du Cleartext erlaubst, muss klar sein, ob das nur für Debug gilt, welche Domain betroffen ist und wann die Ausnahme entfernt wird. Wenn du ein zusätzliches Zertifikat akzeptierst, muss klar sein, ob es nur für lokale Entwicklung, Staging oder eine echte Produktionsanforderung gedacht ist.

Die typische Stolperfalle ist eine zu breite Ausnahme. cleartextTrafficPermitted="true" in base-config wirkt auf alle Domains. Das kann während der Entwicklung bequem wirken, untergräbt aber die Sicherheitsannahmen deiner App. Eine zweite Stolperfalle ist certificates src="user" in produktiven Builds. Damit vertraut deine App auch nutzerinstallierten Zertifikaten. Das kann in manchen Unternehmensszenarien gewollt sein, sollte aber nie unüberlegt passieren, weil dadurch etwa lokale Abfang-Proxys leichter akzeptiert werden können.

Validieren kannst du dein Verständnis mit drei einfachen Checks. Baue zuerst einen Debug-Build und rufe bewusst eine lokale HTTP-URL auf. Prüfe in Logcat, ob die Verbindung nur dann funktioniert, wenn deine Debug-Konfiguration sie erlaubt. Baue danach einen Release- oder release-nahen Build und verifiziere, dass dieselbe HTTP-URL scheitert. Prüfe zuletzt im Code-Review, ob Zertifikate und Cleartext-Ausnahmen in der richtigen Source-Set-Struktur liegen.

Für Tests ist wichtig, die Grenzen sauber zu sehen. Unit-Tests deiner Data Layer testen nicht die Android-Plattformregel selbst. Sie testen, wie dein Repository auf Fehler reagiert. Instrumentierte Tests oder manuelle Debug-Sessions können dagegen zeigen, ob eine konkrete Build-Variante mit der richtigen Network Security Config startet. In CI kannst du ergänzend prüfen, dass Release-Artefakte keine Debug-Zertifikate enthalten und dass die erwartete XML-Datei im Build genutzt wird.

Diese Trennung ist auch bei offline-first Apps hilfreich. Wenn eine Synchronisation fehlschlägt, willst du nicht nur anzeigen, dass „Netzwerk nicht geht“. Du willst im Log und in deinen Fehlerpfaden erkennen, ob der Server nicht erreichbar war, ob ein Zertifikat nicht passt oder ob Android Cleartext blockiert hat. Daraus entstehen bessere Debugging-Schritte und robustere Qualitätssicherung.

Fazit

Network Security Config macht Netzwerk-Sicherheitsregeln sichtbar, statt sie zufällig aus Bibliotheken, Build-Varianten und lokalen Entwickler-Setups entstehen zu lassen. Für dich heißt das: Sperre Cleartext im Release, halte Zertifikatsausnahmen eng begrenzt und nutze debug-overrides oder Debug-Source-Sets für lokale Arbeit. Prüfe das Gelernte aktiv, indem du einen erlaubten und einen verbotenen Netzwerkfall baust, Logcat liest, die Build-Variante wechselst und die XML-Datei im Code-Review wie produktionsrelevanten Code behandelst.

Quellen (6)
Redaktion

Geschrieben von

Redaktion

Das Redaktionsteam recherchiert und schreibt Artikel zu aktuellen Themen rund um Tech, Lifestyle und Ratgeber.