Configurazione della sicurezza di rete

La funzionalità Network Security Configuration ti consente di personalizzare le impostazioni di sicurezza di rete della tua app in un file di configurazione sicuro e dichiarativo senza modificare il codice dell'app. Queste impostazioni possono essere configurate per domini specifici e per un'app specifica. Le funzionalità chiave di questa funzionalità sono:

  • Trust anchor personalizzati: personalizza le autorità di certificazione (CA) attendibili per le connessioni sicure di un'app. Ad esempio, considerare attendibili determinati certificati autofirmati o limitare l'insieme di CA pubbliche considerate attendibili dall'app.
  • Override solo per il debug:esegui il debug in sicurezza delle connessioni protette in un'app senza rischi aggiuntivi per la base installata.
  • Disattivazione del traffico con testo in chiaro:proteggi le app dall'utilizzo accidentale del traffico con testo in chiaro (non criptato).
  • Trasparenza dei certificati: limita le connessioni sicure di un'app all'utilizzo di certificati registrati in modo verificabile.
  • Certificate pinning:limita la connessione sicura di un'app a certificati particolari.

Aggiungere un file di configurazione della sicurezza di rete

La funzionalità Network Security Configuration utilizza un file XML in cui specifichi le impostazioni per la tua app. Devi includere una voce nel manifest dell'app per indirizzare a questo file. Il seguente estratto di codice di un manifest mostra come creare questa voce:

<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
    <application android:networkSecurityConfig="@xml/network_security_config"
                    ... >
        ...
    </application>
</manifest>

Personalizzare le CA attendibili

Potresti voler che la tua app consideri attendibile un insieme personalizzato di CA anziché quello predefinito della piattaforma. I motivi più comuni sono:

  • Connessione a un host con una CA personalizzata, ad esempio una CA autofirmata o emessa internamente a un'azienda.
  • Limitare l'insieme di CA solo a quelle che ritieni attendibili anziché a tutte le CA preinstallate.
  • Considerare attendibili CA aggiuntive non incluse nel sistema.

Per impostazione predefinita, le connessioni sicure (che utilizzano protocolli come TLS e HTTPS) di tutte le app considerano attendibili le CA di sistema preinstallate e le app che hanno come target Android 6.0 (livello API 23) e versioni precedenti considerano attendibile anche l'archivio CA aggiunto dall'utente per impostazione predefinita. Puoi personalizzare le connessioni della tua app utilizzando base-config (per la personalizzazione a livello di app) o domain-config (per la personalizzazione per dominio).

Configura una CA personalizzata

Potresti voler connetterti a un host che utilizza un certificato SSL autofirmato o a un host il cui certificato SSL è emesso da una CA non pubblica di cui ti fidi, ad esempio la CA interna della tua azienda. Il seguente estratto di codice mostra come configurare l'app per un'autorità di certificazione personalizzata in res/xml/network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">example.com</domain>
        <trust-anchors>
            <certificates src="@raw/my_ca"/>
        </trust-anchors>
    </domain-config>
</network-security-config>

Aggiungi il certificato CA autofirmato o non pubblico, in formato PEM o DER, a res/raw/my_ca.

Limitare l'insieme di CA attendibili

Se non vuoi che la tua app consideri attendibili tutte le CA attendibili dal sistema, puoi specificare un insieme ridotto di CA da considerare attendibili. In questo modo, l'app è protetta da certificati fraudolenti emessi da una qualsiasi delle altre CA.

La configurazione per limitare l'insieme di CA attendibili è simile all'attendibilità di una CA personalizzata per un dominio specifico, tranne per il fatto che nella risorsa vengono fornite più CA. Il seguente estratto di codice mostra come limitare l'insieme di CA attendibili della tua app in res/xml/network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">secure.example.com</domain>
        <domain includeSubdomains="true">cdn.example.com</domain>
        <trust-anchors>
            <certificates src="@raw/trusted_roots"/>
        </trust-anchors>
    </domain-config>
</network-security-config>

Aggiungi le CA attendibili, in formato PEM o DER, a res/raw/trusted_roots. Tieni presente che se utilizzi il formato PEM, il file deve contenere solo dati PEM e nessun testo aggiuntivo. Puoi anche fornire più elementi <certificates> anziché uno solo.

Considerare attendibili CA aggiuntive

Potresti voler che la tua app consideri attendibili CA aggiuntive non considerate attendibili dal sistema, ad esempio se il sistema non include ancora la CA o se la CA non soddisfa i requisiti per l'inclusione nel sistema Android. Puoi specificare più origini certificati per una configurazione in res/xml/network_security_config.xml utilizzando un codice come il seguente estratto.

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config>
        <trust-anchors>
            <certificates src="@raw/extracas"/>
            <certificates src="system"/>
        </trust-anchors>
    </base-config>
</network-security-config>

Configura le CA per il debug

Quando esegui il debug di un'app che si connette tramite HTTPS, potresti voler connetterti a un server di sviluppo locale che non dispone del certificato SSL per il server di produzione. Per supportare questa funzionalità senza alcuna modifica al codice dell'app, puoi specificare CA solo per il debug, considerate attendibili solo quando android:debuggable è true, utilizzando debug-overrides. In genere, gli IDE e gli strumenti di build impostano questo flag automaticamente per le build non di rilascio.

Questo è più sicuro del solito codice condizionale perché, come precauzione di sicurezza, gli store non accettano app contrassegnate come eseguibili in modalità di debug.

Il seguente estratto mostra come specificare le CA solo per il debug in res/xml/network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <debug-overrides>
        <trust-anchors>
            <certificates src="@raw/debug_cas"/>
        </trust-anchors>
    </debug-overrides>
</network-security-config>

Certificate Transparency

Nota: il supporto della trasparenza dei certificati è disponibile solo a partire da Android 16 (livello API 36).

Certificate Transparency (CT, RFC 6962) è uno standard internet progettato per migliorare la sicurezza dei certificati digitali. Richiede alle CA di inviare tutti i certificati emessi a un log pubblico che li registra, aumentando la trasparenza e la responsabilità nella procedura di emissione dei certificati.

Mantenendo un registro verificabile di tutti i certificati, CT rende molto più difficile per i malintenzionati falsificare i certificati o per le CA emetterli per errore. In questo modo, è possibile proteggere gli utenti da attacchi man in the middle e altre minacce alla sicurezza. Per ulteriori informazioni, consulta la spiegazione su transparency.dev. Per saperne di più sulla conformità CT su Android, consulta le norme CT di Android.

Il comportamento predefinito della trasparenza dei certificati dipende dal livello API:

Disattivare la trasparenza dei certificati

Nota : per Android 16 (livello API 36), attiva la trasparenza dei certificati impostando <certificateTransparency enabled="true"/> (è disattivata per impostazione predefinita).

Se intendi che la tua app si connetta alle destinazioni senza richiedere che i relativi certificati vengano registrati nei log di Certificate Transparency, puoi disattivare questa funzionalità.

Ad esempio, potresti voler consentire alla tua app di connettersi a secure.example.com senza richiedere la trasparenza dei certificati.

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">secure.example.com</domain>
        <certificateTransparency enabled="false"/>
    </domain-config>
</network-security-config>

Client Hello criptato

Nota: il supporto di Encrypted Client Hello è disponibile solo a partire da Android 17 (livello API 37) e richiede che la libreria di rete dell'app supporti ECH. La configurazione specificata qui avrà effetto solo se la libreria di rete ha adottato ECH.

Encrypted Client Hello (ECH, RFC 9849) è un'estensione del protocollo TLS progettata per migliorare la privacy delle connessioni sicure. Funziona criptando le parti sensibili dell'handshake TLS iniziale, in particolare il campo Server Name Indication (SNI).

Crittografando l'SNI, ECH impedisce agli intermediari di rete di osservare il nome di dominio specifico a cui il client sta tentando di connettersi. In questo modo, gli utenti non vengono identificati o monitorati in base ai domini che visitano, riducendo una significativa falla della privacy presente negli handshake TLS standard.

Il comportamento predefinito di Encrypted Client Hello dipende dal livello API:

  • A partire da Android 17 (livello API 37), ECH viene utilizzato in modalità "opportunistica" per impostazione predefinita. Le app possono disattivare la funzionalità o modificarne il comportamento a livello globale o per dominio.
  • Su Android 16 (livello API 36) e versioni precedenti, ECH non è disponibile.

Disattivare Encrypted ClientHello

Puoi disattivare la funzionalità. Ad esempio, se vuoi disattivare ECH quando effettui connessioni solo a disable-ech.example.com, ma mantenere ECH attivo per tutti gli altri domini, puoi utilizzare la seguente configurazione:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config>
        <domainEncryption mode="enabled"/>
    </base-config>
    <domain-config>
        <domain includeSubdomains="true">disable-ech.example.com</domain>
        <domainEncryption mode="disabled"/>
    </domain-config>
</network-security-config>

Traffico con testo in chiaro

Gli sviluppatori possono attivare o disattivare il traffico cleartext (utilizzando il protocollo HTTP non criptato anziché HTTPS) per le loro applicazioni. Per ulteriori dettagli, consulta NetworkSecurityPolicy.isCleartextTrafficPermitted().

Il comportamento predefinito del traffico in chiaro dipende dal livello API:

  • Fino ad Android 8.1 (livello API 27), il supporto cleartext è abilitato per impostazione predefinita. Le applicazioni possono disattivare il traffico con testo in chiaro per una maggiore sicurezza.
  • A partire da Android 9 (livello API 28), il supporto di cleartext è disabilitato per impostazione predefinita. Le applicazioni che richiedono il traffico con testo in chiaro possono attivare il traffico con testo in chiaro.

Disattivare il traffico con testo in chiaro

Nota: le indicazioni riportate in questa sezione si applicano solo alle app che hanno come target Android 8.1 (livello API 27) o versioni precedenti.

Se vuoi che la tua app si connetta alle destinazioni utilizzando solo connessioni sicure, puoi disattivare il supporto del traffico di testo non criptato verso queste destinazioni. Questa opzione consente di evitare regressioni accidentali nelle app a causa di modifiche agli URL forniti da fonti esterne, come i server di backend.

Ad esempio, potresti voler fare in modo che la tua app garantisca che le connessioni a secure.example.com vengano sempre effettuate tramite HTTPS per proteggere il traffico sensibile da reti ostili.

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">secure.example.com</domain>
    </domain-config>
</network-security-config>

Attiva il traffico con testo in chiaro

Nota: le indicazioni riportate in questa sezione si applicano solo alle app che hanno come target Android 9 (livello API 28) o versioni successive.

Se la tua app deve connettersi a destinazioni utilizzando il traffico in chiaro (HTTP), puoi attivare il supporto del traffico in chiaro per queste destinazioni.

Ad esempio, potresti voler consentire alla tua app di effettuare connessioni non sicure a insecure.example.com.

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">insecure.example.com</domain>
    </domain-config>
</network-security-config>

Se la tua app deve autorizzare il traffico di testo non criptato verso qualsiasi dominio, imposta cleartextTrafficPermitted="true" in base-config. Tieni presente che questa configurazione non sicura deve essere evitata, se possibile.

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true">
    </base-config>
</network-security-config>

Bloccare i certificati

Normalmente, un'app considera attendibili tutte le CA preinstallate. Se una di queste CA dovesse emettere un certificato fraudolento, l'app sarebbe a rischio di attacchi on-path. Alcune app scelgono di limitare l'insieme di certificati che accettano limitando l'insieme di CA di cui si fidano o tramite il certificate pinning.

Il pinning dei certificati viene eseguito fornendo un insieme di certificati tramite hash della chiave pubblica (SubjectPublicKeyInfo del certificato X.509). Una catena di certificati è valida solo se contiene almeno una delle chiavi pubbliche bloccate.

Tieni presente che, quando utilizzi il pinning dei certificati, devi sempre includere un token di backup in modo che, se sei costretto a passare a nuove chiavi o a modificare le CA (quando esegui il pinning a un certificato CA o a una CA intermedia), la connettività della tua app non venga interessata. In caso contrario, devi eseguire un aggiornamento dell'app per ripristinare la connettività.

Inoltre, è possibile impostare una data di scadenza per i pin dopo la quale l'operazione non viene eseguita. In questo modo, si evitano problemi di connettività nelle app che non sono state aggiornate. Tuttavia, l'impostazione di un tempo di scadenza per i pin potrebbe consentire agli autori di attacchi di bypassare i certificati bloccati.

L'estratto seguente mostra come bloccare i certificati in res/xml/network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">example.com</domain>
        <pin-set expiration="2018-01-01">
            <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
            <!-- backup pin -->
            <pin digest="SHA-256">fwza0LRMXouZHRC8Ei+4PyuldPDcf3UKgO/04cDM1oE=</pin>
        </pin-set>
    </domain-config>
</network-security-config>

Comportamento di ereditarietà della configurazione

I valori non impostati in una configurazione specifica vengono ereditati. Questo comportamento consente configurazioni più complesse mantenendo leggibile il file di configurazione.

Ad esempio, i valori non impostati in un elemento domain-config vengono presi dall'elemento principale domain-config, se nidificato, o dall'elemento base-config, in caso contrario. I valori non impostati in base-config utilizzano i valori predefiniti della piattaforma.

Ad esempio, considera un caso in cui tutte le connessioni ai sottodomini di example.com devono utilizzare un insieme personalizzato di CA. Inoltre, il traffico di testo non criptato verso questi domini è consentito tranne quando ci si connette a secure.example.com. Se nidifichi la configurazione per secure.example.com all'interno della configurazione per example.com, non è necessario duplicare trust-anchors.

L'estratto riportato di seguito mostra l'aspetto di questa nidificazione in res/xml/network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">example.com</domain>
        <trust-anchors>
            <certificates src="@raw/my_ca"/>
        </trust-anchors>
        <domain-config cleartextTrafficPermitted="false">
            <domain includeSubdomains="true">secure.example.com</domain>
        </domain-config>
    </domain-config>
</network-security-config>

Configurazione di localhost

In genere non è necessario applicare le funzionalità di sicurezza di rete per le connessioni localhost. Ad esempio, la trasparenza dei certificati è raramente necessaria per le connessioni localhost.

A partire da Android 17 (livello API 37) e versioni successive, se non è stata definita alcuna configurazione per localhost, viene inclusa una configurazione implicita. Per impostazione predefinita, questa configurazione esegue le seguenti operazioni:

  • Consente il traffico con testo in chiaro.
  • Non applica Certificate Transparency (CT).
  • Non applica il blocco dei certificati.
  • Delega a <base-config> per i trust anchor.

Una configurazione è considerata come targeting di localhost se il dominio è:

  • localhost
  • ip6-localhost o
  • un indirizzo IP numerico e InetAddress.isLoopback() è true (ad esempio, 127.0.0.1 o [::1])

Formato del file di configurazione

La funzionalità Network Security Configuration utilizza un formato di file XML. La struttura generale del file è mostrata nel seguente esempio di codice:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config>
        <trust-anchors>
            <certificates src="..."/>
            ...
        </trust-anchors>
    </base-config>

    <domain-config>
        <domain>android.com</domain>
        ...
        <trust-anchors>
            <certificates src="..."/>
            ...
        </trust-anchors>
        <pin-set>
            <pin digest="...">...</pin>
            ...
        </pin-set>
    </domain-config>
    ...
    <debug-overrides>
        <trust-anchors>
            <certificates src="..."/>
            ...
        </trust-anchors>
    </debug-overrides>
</network-security-config>

Le sezioni seguenti descrivono la sintassi e altri dettagli del formato del file.

<network-security-config>

possono contenere:
0 o 1 di <base-config>
Un numero qualsiasi di <domain-config>
0 o 1 di <debug-overrides>

<base-config>

sintassi:
<base-config cleartextTrafficPermitted=["true" | "false"]>
    ...
</base-config>
possono contenere:
<trust-anchors> <certificateTransparency> <domainEncryption>
description:
La configurazione predefinita utilizzata da tutte le connessioni la cui destinazione non è coperta da un domain-config.

Tutti i valori non impostati utilizzano i valori predefiniti della piattaforma.

La configurazione predefinita per le app che hanno come target Android 9 (livello API 28) e versioni successive è la seguente:

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

La configurazione predefinita per le app che hanno come target Android 7.0 (livello API 24) fino ad Android 8.1 (livello API 27) è la seguente:

<base-config cleartextTrafficPermitted="true">
    <trust-anchors>
        <certificates src="system" />
    </trust-anchors>
</base-config>

La configurazione predefinita per le app che hanno come target Android 6.0 (livello API 23) e versioni precedenti è la seguente:

<base-config cleartextTrafficPermitted="true">
    <trust-anchors>
        <certificates src="system" />
        <certificates src="user" />
    </trust-anchors>
</base-config>

<domain-config>

sintassi:
<domain-config cleartextTrafficPermitted=["true" | "false"]>
    ...
</domain-config>
possono contenere:
1 o più <domain>
0 o 1 <certificateTransparency>
0 o 1 <trust-anchors>
0 o 1 <pin-set>
0 o 1 <domainEncryption>
Qualsiasi numero di <domain-config>
description:
Configurazione utilizzata per le connessioni a destinazioni specifiche, come definito dagli elementi domain.

Tieni presente che se più elementi domain-config coprono una destinazione, viene utilizzata la configurazione con la regola di corrispondenza del dominio più specifica (più lunga).

<dominio>

sintassi:
<domain includeSubdomains=["true" | "false"]>example.com</domain>
attributi:
includeSubdomains
Se "true", questa regola di dominio corrisponde al dominio e a tutti i sottodomini, inclusi i sottodomini dei sottodomini. In caso contrario, la regola si applica solo alle corrispondenze esatte.

<certificateTransparency>

sintassi:
<certificateTransparency enabled=["true" | "false"]/>
description:
Se true, l'app utilizzerà i log Certificate Transparency per convalidare i certificati. Quando un'app utilizza il proprio certificato (o l'archivio utente), è probabile che il certificato non sia pubblico e quindi non verificabile utilizzando Certificate Transparency. Per impostazione predefinita, la verifica è disabilitata per questi casi. È comunque possibile forzare la verifica utilizzando <certificateTransparency enabled="true"/> nella configurazione del dominio. Per ogni <domain-config>, la valutazione segue questo ordine:
  1. Se certificateTransparency è attivato, abilita la verifica.
  2. Se un <trust-anchors> è "user" o in linea (ovvero "@raw/cert.pem"), disattiva la verifica.
  3. In caso contrario, fai affidamento alla configurazione ereditata.

<domainEncryption>

sintassi:
<domainEncryption mode=["enabled" | "opportunistic" | "disabled"]/>
description:
Controlla il comportamento di Encrypted Client Hello (ECH) per le connessioni ai domini specificati.

Nota: l'elemento domainEncryption dipende dalla libreria di rete dell'app che supporta ECH. Il comportamento specificato viene applicato solo se la libreria di rete ha adottato ECH. Le app non devono gestire autonomamente le configurazioni ECH, ma devono fare affidamento sulle proprie librerie di rete per farlo durante l'handshake TLS.

L'attributo mode può essere uno dei seguenti:

  • enabled: applica ECH a una connessione quando la configurazione ECH viene fornita durante la creazione dell'handshake TLS e abilita ECH GREASE in caso contrario.
  • opportunistic: utilizza ECH su una connessione quando la configurazione ECH viene fornita durante la creazione dell'handshake TLS. Se la connessione non va a buon fine o la configurazione non è stata fornita, torna a un ClientHello TLS standard non criptato. Questa modalità non abilita ECH GREASE.
  • disabled: non tentare di utilizzare ECH o ECH GREASE su nessuna connessione.

Se non specificato, il valore predefinito di mode è "opportunistic".

<debug-overrides>

sintassi:
<debug-overrides>
    ...
</debug-overrides>
possono contenere:
0 o 1 <trust-anchors>
description:
Override da applicare quando android:debuggable è "true", il che si verifica normalmente per le build non di rilascio generate da IDE e strumenti di build. I trust anchor specificati in debug-overrides vengono aggiunti a tutte le altre configurazioni e il pinning dei certificati non viene eseguito quando la catena di certificati del server utilizza uno di questi trust anchor solo per il debug. Se android:debuggable è "false", questa sezione viene completamente ignorata.

<trust-anchors>

sintassi:
<trust-anchors>
...
</trust-anchors>
possono contenere:
Qualsiasi numero di <certificates>
description:
Set di trust anchor per connessioni sicure.

<certificates>

sintassi:
<certificates src=["system" | "user" | "raw resource"]
              overridePins=["true" | "false"] />
description:
Set di certificati X.509 per gli elementi trust-anchors.
attributi:
src
L'origine dei certificati CA. Ogni certificato può essere uno dei seguenti:
  • un ID risorsa non elaborato che rimanda a un file contenente certificati X.509. I certificati devono essere codificati in formato DER o PEM. Nel caso dei certificati PEM, il file non deve contenere dati non PEM aggiuntivi, come i commenti.
  • "system" per i certificati CA di sistema preinstallati
  • "user" per i certificati CA aggiunti dagli utenti
overridePins

Specifica se le CA di questa origine ignorano il pinning dei certificati. Se "true", il pinning non viene eseguito sulle catene di certificati firmate da una delle CA di questa origine. Questo può essere utile per il debug delle CA o per testare attacchi man-in-the-middle sul traffico sicuro della tua app.

Il valore predefinito è "false", a meno che non sia specificato in un elemento debug-overrides, nel qual caso il valore predefinito è "true".

<pin-set>

sintassi:
<pin-set expiration="date">
...
</pin-set>
possono contenere:
Qualsiasi numero di <pin>
description:
Un insieme di pin della chiave pubblica. Affinché una connessione sicura sia attendibile, una delle chiavi pubbliche nella catena di attendibilità deve essere nel set di pin. Consulta <pin> per il formato dei segnaposto.
attributi:
expiration
La data, nel formato yyyy-MM-dd, in cui i pin scadono, disattivando il blocco. Se l'attributo non è impostato, i PIN non scadono.

La scadenza contribuisce a evitare problemi di connettività nelle app che non ricevono aggiornamenti al set di pin, ad esempio quando l'utente disattiva gli aggiornamenti delle app.

<pin>

sintassi:
<pin digest=["SHA-256"]>base64 encoded digest of X.509
    SubjectPublicKeyInfo (SPKI)</pin>
attributi:
digest
L'algoritmo di digest utilizzato per generare il pin. Al momento è supportato solo "SHA-256".