¿Qué es Network Security Config?

El Network Security Configuration es un archivo XML que define la política de seguridad de red de tu app. Te permite controlar qué conexiones están permitidas, en qué certificados confiar y qué comportamiento tener en debug, todo sin escribir código Java/Kotlin.

<!-- res/xml/network_security_config.xml -->
<network-security-config>
    <!-- Tu configuración acá -->
</network-security-config>

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

Forzar HTTPS para todos los dominios

Desde Android 9.0 (API 28), el tráfico HTTP en texto plano está bloqueado por defecto para apps con targetSdk >= 28. Podés ser explícito en el NSC:

<network-security-config>
    <!-- Configuración base: bloquear todo cleartext -->
    <base-config cleartextTrafficPermitted="false">
        <trust-anchors>
            <!-- Confiar en las CAs del sistema (default) -->
            <certificates src="system" />
        </trust-anchors>
    </base-config>
</network-security-config>

Trust anchors — en qué CAs confiar

Por default Android confía en todas las CAs del sistema (DigiCert, Let's Encrypt, etc.). En apps enterprise podés querer confiar también en una CA privada corporativa:

<network-security-config>
    <base-config>
        <trust-anchors>
            <!-- Las CAs del sistema -->
            <certificates src="system" />
            <!-- TU CA corporativa (archivo en res/raw/) -->
            <certificates src="@raw/ca_corporativa" />
        </trust-anchors>
    </base-config>

    <!-- Configuración específica por dominio -->
    <domain-config>
        <domain includeSubdomains="true">api.empresa-interna.com</domain>
        <trust-anchors>
            <!-- Solo confiar en la CA interna para este dominio -->
            <certificates src="@raw/ca_corporativa" />
        </trust-anchors>
    </domain-config>
</network-security-config>

Certificados adicionales solo en debug

Para desarrollo y QA, a veces necesitás interceptar el tráfico con herramientas como Charles o mitmproxy. Podés permitir CAs de usuario solo en builds de debug:

<network-security-config>
    <base-config cleartextTrafficPermitted="false">
        <trust-anchors>
            <certificates src="system" />
        </trust-anchors>
    </base-config>

    <!-- Solo en debug: confiar también en CAs instaladas por el usuario -->
    <!-- Esto permite interceptar con Charles/mitmproxy en desarrollo -->
    <debug-overrides>
        <trust-anchors>
            <certificates src="system" />
            <certificates src="user" />  <!-- CAs del usuario (solo debug) -->
        </trust-anchors>
    </debug-overrides>
</network-security-config>

debug-overrides solo aplica en debug buildsEl bloque <debug-overrides> se ignora automáticamente en builds de release. Podés dejarlo en el archivo sin preocuparte de que afecte a producción.

Certificate pinning declarativo

El NSC también soporta certificate pinning sin código. Vas a necesitar el hash del pin del certificado (explicamos cómo obtenerlo en la lección 05):

<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">api.miapp.com</domain>
        <pin-set expiration="2026-12-31">
            <!-- Pin del certificado actual (SHA-256 de la clave pública) -->
            <pin digest="SHA-256">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</pin>
            <!-- Pin de backup OBLIGATORIO — sin esto, si el cert expira quedás bloqueado -->
            <pin digest="SHA-256">BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=</pin>
        </pin-set>
    </domain-config>
</network-security-config>

El pin de backup es obligatorioSi el certificado del servidor expira o se rota y no tenés un pin de backup válido, tu app queda bloqueada para todos los usuarios hasta que publiques una actualización. Siempre incluí al menos un pin de backup para el certificado intermedio o la CA raíz.

Cleartext traffic por dominio

Si tenés una razón legítima para usar HTTP en un dominio específico (ej: localhost en desarrollo, un CDN de terceros legacy):

<network-security-config>
    <base-config cleartextTrafficPermitted="false" />

    <!-- Excepción: localhost para desarrollo -->
    <domain-config cleartextTrafficPermitted="true">
        <domain>localhost</domain>
        <domain>10.0.2.2</domain>  <!-- IP del host desde el emulador -->
    </domain-config>
</network-security-config>