Sicurezza dell'attività

Android protegge gli utenti da app dannose e offre un'esperienza UI affidabile. Il framework di sicurezza delle attività comprende regole e restrizioni della piattaforma. Queste regole e limitazioni impediscono interruzioni indesiderate dell'interfaccia utente, il dirottamento delle attività e altre minacce alla sicurezza. Queste minacce riguardano quando e come i componenti dell'app vengono visualizzati sullo schermo. Un componente chiave di questo framework limita l'avvio di attività in background.

Limitazioni di avvio dell'attività in background

Un avvio di attività in background (BAL) si verifica quando un'app non in primo piano, senza attività visibili o un PendingIntent ricevuto da un'altra app tenta di avviare una nuova attività. Si tratta di un avvio di attività in background (BAL). Sebbene esistano casi d'uso legittimi, ad esempio quando viene avviata un'app sveglia, le intenzioni di trasmissione senza limitazioni portano a un'esperienza utente scadente e creano vulnerabilità di sicurezza.

Perché sono limitati?

A partire da Android 10 (livello API 29), la piattaforma ha imposto limitazioni su quando le app possono avviare attività in background. Queste protezioni contribuiscono a prevenire comportamenti dannosi delle app e a migliorare l'esperienza utente mitigando gli abusi comuni, tra cui:

  • Hijacking dell'interfaccia utente e annunci popup: un'app in background avvia inaspettatamente un'attività (spesso un annuncio) sopra l'app con cui l'utente sta interagendo, dirottando la sua sessione.
  • Phishing e impersonificazione: un'app in background avvia un'attività che imita un'altra app (ad esempio, una schermata di accesso falsa per un'app legittima) per rubare le credenziali dell'utente. Ciò si ottiene spesso tramite un attacco "Activity Sandwich", in cui un'attività dannosa viene inserita nello stack di attività di un'app legittima.
  • Tapjacking: un'app in background mostra un'attività trasparente o oscurata sopra un'altra app per intercettare i tocchi dell'utente e indurlo a compiere azioni non intenzionali.
  • Riattivazione delle app: un componente in background di un'app riattiva i componenti in primo piano di un'altra app per aumentare in modo illecito le metriche degli utenti attivi giornalieri.

Servizi in primo piano (per le attività in corso)

Se la tua app deve eseguire un'attività di lunga durata in background di cui l'utente deve essere a conoscenza, ad esempio riprodurre musica o monitorare un'attività fisica, devi utilizzare un servizio in primo piano. Un servizio in primo piano deve mostrare una notifica persistente che non può essere chiusa dall'utente. Questa notifica può fornire controlli interattivi (ad esempio, pulsanti di riproduzione/pausa per un'app di musica). In questo modo, l'utente è informato e ha il controllo, ma non viene interrotto da un'attività a schermo intero.

Seguendo questa gerarchia, iniziando con le notifiche standard e passando a opzioni più intrusive solo quando necessario, crei un'esperienza migliore e più prevedibile per i tuoi utenti.

Quando sono consentiti gli avvii di attività in background (eccezioni)

Un'app può avviare un'attività in background se è soddisfatta una delle seguenti condizioni:

  • L'app ha una finestra visibile, ad esempio un'attività in primo piano.
  • L'app è l'Input Method Editor (IME) corrente.
  • L'attività viene avviata da un PendingIntent inviato dal sistema (ad esempio, da un tocco di notifica).
  • L'app dispone dell'autorizzazione SYSTEM_ALERT_WINDOW concessa dall'utente.
  • All'app è stata concessa l'autorizzazione START_ACTIVITIES_FROM_BACKGROUND.
  • L'app è associata a un servizio a cui è stata concessa l'autorizzazione per avviare attività in background.
  • L'avvio viene avviato dall'app di avvio del dispositivo, ad esempio quando un utente tocca un'icona dell'app o interagisce con un widget.
  • L'avvio avviene da una parte fondamentale del sistema operativo che deve essere eseguita in qualsiasi momento, ad esempio il servizio di telefonia che avvia la schermata della chiamata in arrivo.

Nuovo rafforzamento e attivazioni obbligatorie

Per migliorare ulteriormente la sicurezza, Android ha introdotto regole più rigide che richiedono l'attivazione esplicita per le app che utilizzano PendingIntent e IntentSender per avviare attività. Un avvio è consentito solo se l'app che ha creato l'intent PendingIntent o l'app che lo invia acconsente a concedere i privilegi di avvio in background.

Nella maggior parte dei casi, l'app che invia l'intent PendingIntent deve essere quella che esegue l'opt-in, in quanto in genere è l'app con cui l'utente interagisce direttamente (ad esempio, toccando un pulsante).

I mittenti devono attivare PendingIntent

Quando la tua app ha come target Android 14 (livello API 34) o versioni successive, non concede più i privilegi BAL per impostazione predefinita quando invia un PendingIntent. Se non esegui l'opt-in esplicito, l'avvio dell'attività potrebbe essere bloccato, a meno che il creator di PendingIntent non abbia già concesso i propri privilegi.

Per garantire il successo di un lancio, il mittente deve attivare la concessione dei privilegi chiamando ActivityOptions.setPendingIntentBackgroundActivityStartMode() e la modalità consigliata è ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE (aggiunta nell'SDK 36).

Questa è una modalità più restrittiva e sicura. Concede l'autorizzazione solo se l'app di invio è visibile sullo schermo al momento dell'invio dell'PendingIntent. In questo modo, si garantisce con maggiore certezza che l'avvio dell'attività sia il risultato diretto dell'interazione di un utente con la tua app.

Tabella degli intent in attesa
Figura 1: flusso decisionale per l'avvio di attività in background.

Utilizza ActivityOptions.setPendingIntentBackgroundActivityStartMode() per concedere privilegi.

// Sender Side
ActivityOptions options = ActivityOptions.makeBasic()
    .setPendingIntentBackgroundActivityStartMode(
        ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE);

try {
    myPendingIntent.send(options.toBundle());
} catch (PendingIntent.CanceledException e) {
    Log.e(TAG, "The PendingIntent was canceled", e);
}
// Sender Side
val options = ActivityOptions.makeBasic().apply {
    pendingIntentBackgroundActivityStartMode = ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE
}

try {
    myPendingIntent.send(options.toBundle())
} catch (e: PendingIntent.CanceledException) {
    Log.e(TAG, "The PendingIntent was canceled", e)
}

I creator devono attivare PendingIntent

Quando la tua app ha come target Android 15 (livello API 35) o versioni successive, un'app che crea un PendingIntent non concede più i privilegi di avvio in background per impostazione predefinita. Per consentire al mittente di utilizzare i privilegi BAL della tua app, devi attivare esplicitamente l'opzione.

Utilizza ActivityOptions.setPendingIntentCreatorBackgroundActivityStartMode() per concedere privilegi.

// Creator Side
Intent intent = new Intent(context, MyActivity.class);
ActivityOptions options = ActivityOptions.makeBasic().setPendingIntentCreatorBackgroundActivityStartMode(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);

PendingIntent pendingIntent = PendingIntent.getActivity(context, REQUEST_CODE, intent, PendingIntent.FLAG_IMMUTABLE, options.toBundle());
// Creator Side
val intent = Intent(context, MyActivity::class.java)
val options = ActivityOptions.makeBasic().apply {
    pendingIntentCreatorBackgroundActivityStartMode = ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
}

val pendingIntent = PendingIntent.getActivity(context, REQUEST_CODE, intent,
        PendingIntent.FLAG_IMMUTABLE, options.toBundle())

Avvio con IntentSender

Le stesse limitazioni BAL si applicano anche quando si avviano attività utilizzando un IntentSender. Poiché un IntentSender viene ottenuto tramite PendingIntent.getIntentSender, è soggetto a requisiti di attivazione simili.

  • A partire da Android 14 (API 34), l'utilizzo di Context.startIntentSender() richiede l'attivazione esplicita lato mittente. Devi fornire anche il bundle ActivityOptions.
ActivityOptions options = ActivityOptions.makeBasic()
        .setPendingIntentBackgroundActivityStartMode(
            ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);

context.startIntentSender(myIntentSender, fillInIntent, flagsMask,
        flagsValues, extraFlags, options.toBundle());
val options = ActivityOptions.makeBasic().apply {
    pendingIntentBackgroundActivityStartMode = ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
}

context.startIntentSender(myIntentSender, fillInIntent, flagsMask,
        flagsValues, extraFlags, options.toBundle())
ActivityOptions options = ActivityOptions.makeBasic()
        .setPendingIntentBackgroundActivityStartMode(
            ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);

myIntentSender.sendIntent(context, code, intent, onFinished, handler,
        requiredPermission, options.toBundle());
val options = ActivityOptions.makeBasic().apply {
    pendingIntentBackgroundActivityStartMode = ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
}

myIntentSender.sendIntent(context, code, intent, onFinished, handler,
        requiredPermission, options.toBundle())

Diagramma di sequenza: limitazioni BAL

Tabella degli intent in attesa
Figura 2: il processo di avvio sicuro di un'attività utilizzando un PendingIntent

Questo diagramma illustra il processo di avvio sicuro di un'attività utilizzando un PendingIntent. Un lancio riuscito dipende da una catena di privilegi valida in cui almeno una delle app partecipanti concede i propri privilegi e ha la possibilità di avviare un'attività in background

  1. Creazione e delega (app A - Il creator)
    1. L'app Creator crea il PendingIntent
    2. Se l'SDK di targeting è la versione 35 o successive, il creator deve delegare esplicitamente i propri privilegi BAL utilizzando setPendingIntentCreatorBackgroundActivityStartMode() se vuole che i suoi privilegi vengano utilizzati. Per impostazione predefinita, non vengono delegati privilegi.
    3. PendingIntent viene quindi inviato a un'altra app (app B)
  2. Avvio e contributo (app B - Il mittente)
    1. In un secondo momento, l'app Mittente avvia l'avvio chiamando PendingIntent.send().
    2. Se l'SDK di targeting è 34 o versioni successive, il mittente deve contribuire esplicitamente con i propri privilegi utilizzando setPendingIntentBackgroundActivityStartMode() se vuole che i suoi privilegi vengano utilizzati. Per impostazione predefinita, non vengono delegati privilegi.
  3. Android System Security Validation
    1. Il sistema Android intercetta la richiesta di avvio ed esegue un controllo di sicurezza.
    2. Valuta due condizioni:
    3. Il creator ha delegato i propri privilegi E l'app del creator soddisfa attualmente una delle eccezioni generali del BAL?
    4. Il mittente ha contribuito con i propri privilegi E l'app mittente soddisfa attualmente una delle eccezioni generali del BAL?
  4. Risultato
    1. CONSENTITO: se almeno una delle due condizioni del passaggio 3 è soddisfatta, la catena di privilegi viene convalidata. Il sistema Android avvia l'attività di destinazione e il mittente riceve un risultato positivo.
    2. BLOCCATO: se nessuna condizione è soddisfatta, il sistema blocca l'avvio. L'app mittente non riceve un valore restituito diretto o un'eccezione che indica l'errore. Il sistema Android registra internamente un messaggio "Background activity launch blocked!" in Logcat, che gli sviluppatori devono controllare per il debug.

Prevenzione del dirottamento delle attività

Per prevenire attacchi di hijacking in-task (come l'"Activity Sandwich"), Android 15 introduce nuove regole per le app che hanno come target il livello API 37 o versioni successive.

  • Regola 1: all'interno di una singola attività, un'attività può essere avviata solo da un'altra attività che appartiene alla stessa applicazione (ovvero ha lo stesso UID) dell'attività in primo piano corrente nell'attività.
  • Regola 2: solo un'attività all'interno di un'attività in primo piano che corrisponde all'UID dell'attività più in alto può creare una nuova attività o portare in primo piano un'attività diversa esistente.

Attivazione da parte degli sviluppatori delle protezioni in-task

Questo comportamento può essere attivato a partire dall'SDK target 37. Devi attivarlo esplicitamente. È progettato per impedire l'hijacking in-task (o sandwiching dell'attività), in cui un'app dannosa potrebbe avviare un'attività nell'attività della tua app per impersonarla e rubare i dati degli utenti.

Attivazione delle protezioni

Per attivare e abilitare ASM per la tua applicazione, imposta l'attributo android:allowCrossUidActivitySwitchFromBelow su false nel file AndroidManifest.xml. Si tratta di un'impostazione a livello di applicazione che protegge per impostazione predefinita tutte le attività della tua app.

Creazione di eccezioni per attività specifiche

Se l'hai attivato per la tua app, ma devi consentire l'avvio di un'attività specifica e attendibile da parte di altre app, puoi creare un'eccezione mirata. Per escludere una singola attività da questa protezione, chiama setAllowCrossUidActivitySwitchFromBelow(true) all'interno del metodo onCreate() dell'attività. In questo modo, è possibile avviare un'attività mentre il resto dell'app rimane protetto.

Risoluzione dei problemi

Filtra Logcat per trovare i messaggi pertinenti utilizzando un'espressione regolare. Il tag ActivityTaskManager viene spesso utilizzato e il filtro per ActivityTaskManager può contribuire a isolare i log.

Comprendere i messaggi di log chiave

Avvio bloccato (errore): questo messaggio indica che l'avvio di un'attività è stato bloccato.

Quando analizzi i log, controlla questi campi:

  • realCallingPackage: l'app che ha inviato l'PendingIntent. Questo è il mittente.
  • callingPackage: l'app che ha creato il PendingIntent. Qui parla il creator.

Modalità StrictMode

A partire da Android 16, lo sviluppatore di app può attivare la modalità StrictMode per ricevere una notifica quando l'avvio di un'attività viene bloccato (o rischia di essere bloccato quando viene aumentato l'SDK di destinazione dell'app).

Codice di esempio per l'attivazione all'inizio del metodo Application.onCreate() dell'applicazione, dell'attività o di un altro componente dell'applicazione:

override fun onCreate(savedInstanceState: Bundle?) {
     super.onCreate(savedInstanceState)
     StrictMode.setVmPolicy(
         StrictMode.VmPolicy.Builder()
         .detectBlockedBackgroundActivityLaunch()
         .penaltyLog()
         .build());
     )
 }