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
PendingIntentinviato dal sistema (ad esempio, da un tocco di notifica). - L'app dispone dell'autorizzazione
SYSTEM_ALERT_WINDOWconcessa 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.
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())
- A partire da Android 17 (API 37+), l'utilizzo di IntentSender.sendIntent() richiede l'attivazione da parte del mittente.
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())
- Utilizzo di ActivityResultLauncher<IntentSenderRequest>: questa API AndroidX utilizza internamente Context.startIntentSender() ed è pertanto interessata dalle limitazioni BAL.
Diagramma di sequenza: limitazioni BAL
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
- Creazione e delega (app A - Il creator)
- L'app Creator crea il
PendingIntent - 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.
PendingIntentviene quindi inviato a un'altra app (app B)
- L'app Creator crea il
- Avvio e contributo (app B - Il mittente)
- In un secondo momento, l'app Mittente avvia l'avvio chiamando
PendingIntent.send(). - 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.
- In un secondo momento, l'app Mittente avvia l'avvio chiamando
- Android System Security Validation
- Il sistema Android intercetta la richiesta di avvio ed esegue un controllo di sicurezza.
- Valuta due condizioni:
- Il creator ha delegato i propri privilegi E l'app del creator soddisfa attualmente una delle eccezioni generali del BAL?
- Il mittente ha contribuito con i propri privilegi E l'app mittente soddisfa attualmente una delle eccezioni generali del BAL?
- Risultato
- 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.
- 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.
- Significato: l'avvio di un'attività è stato negato perché l'attivazione di PendingIntent necessaria non era presente nel mittente (SDK di targeting 34+) o nel creatore (SDK di targeting 35+).
- Azione: devi aggiornare il codice per includere l'attivazione corretta di ActivityOptions.
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());
)
}