Etkinlik güvenliği

Android, kullanıcıları kötü amaçlı uygulamalardan korur ve güvenilir bir kullanıcı arayüzü deneyimi sunar. Etkinlik güvenliği çerçevesi, kuralları ve platform kısıtlamalarını kapsar. Bu kurallar ve kısıtlamalar, istenmeyen kullanıcı arayüzü kesintilerini, görev ele geçirmeyi ve diğer güvenlik tehditlerini önler. Bu tehditler, uygulama bileşenlerinin ekranda ne zaman ve nasıl göründüğüyle ilgilidir. Bu çerçevenin temel bileşenlerinden biri, arka planda etkinlik başlatılmasını kısıtlar.

Arka plan etkinliği başlatma kısıtlamaları

Arka plan etkinliği başlatma (BAL), ön planda olmayan, görünür etkinliği olmayan veya farklı bir uygulamadan PendingIntent alan bir uygulama yeni bir etkinlik başlatmaya çalıştığında gerçekleşir. Bu, Arka Plan Etkinliği Başlatma (BAL) işlemidir. Çalar saat uygulamasının başlatılması gibi meşru kullanım alanları olsa da sınırsız BAL'ler, kullanıcı deneyimini olumsuz etkiler ve güvenlik açıkları oluşturur.

Neden kısıtlanıyorlar?

Android 10'dan (API düzeyi 29) itibaren platform, uygulamaların arka planda etkinlik başlatabileceği zamanlarla ilgili kısıtlamalar getirmiştir. Bu korumalar, kötü amaçlı uygulama davranışlarını önlemeye yardımcı olur ve aşağıdakiler de dahil olmak üzere yaygın kötüye kullanımları azaltarak kullanıcı deneyimini iyileştirir:

  • Kullanıcı Arayüzünü Ele Geçirme ve Pop-up Reklamlar: Arka planda çalışan bir uygulama, kullanıcının o anda etkileşimde bulunduğu uygulamanın üzerinde beklenmedik bir şekilde etkinlik (genellikle reklam) başlatarak kullanıcının oturumunu ele geçirir.
  • Kimlik Avı ve Kimliğe Bürünme: Arka planda çalışan bir uygulama, kullanıcı kimlik bilgilerini çalmak için başka bir uygulamayı taklit eden bir etkinlik başlatır (örneğin, meşru bir uygulama için sahte bir giriş ekranı). Bu genellikle, kötü amaçlı bir etkinliğin meşru bir uygulamanın görev yığınına yerleştirildiği bir "Etkinlik Sandviçi" saldırısıyla gerçekleştirilir.
  • Tıklama hırsızlığı: Arka plandaki bir uygulama, kullanıcının dokunmalarını engellemek ve onları istenmeyen işlemler yapmaya ikna etmek için başka bir uygulamanın üzerinde şeffaf veya gizlenmiş bir etkinlik gösterir.
  • Uygulama Uyandırma: Bir uygulamanın arka plan bileşeni, günlük etkin kullanıcı metriklerini yasa dışı bir şekilde artırmak için başka bir uygulamanın ön plan bileşenlerini uyandırır.

Ön plan hizmetleri (devam eden görevler için)

Uygulamanızın arka planda, kullanıcının farkında olması gereken uzun süreli bir görev (ör. müzik çalma veya antrenman takibi) gerçekleştirmesi gerekiyorsa ön plan hizmeti kullanmanız gerekir. Ön plan hizmeti, kullanıcı tarafından kapatılamayan kalıcı bir bildirim göstermelidir. Bu bildirim, etkileşimli kontroller (örneğin, müzik uygulaması için oynat/duraklat düğmeleri) sağlayabilir. Bu sayede kullanıcı bilgilendirilir ve kontrolü elinde tutar ancak tam ekran etkinlikle kesintiye uğramaz.

Standart bildirimlerle başlayıp yalnızca gerektiğinde daha rahatsız edici seçeneklere geçerek bu hiyerarşiyi izlediğinizde kullanıcılarınız için daha iyi ve daha öngörülebilir bir deneyim oluşturursunuz.

Arka planda başlayan işlemlere izin verildiğinde (istisnalar)

Aşağıdaki koşullardan biri karşılanırsa bir uygulama arka planda etkinlik başlatabilir:

  • Uygulamanın, ön plandaki bir etkinlik gibi görünür bir penceresi vardır.
  • Uygulama, mevcut Giriş Yöntemi Düzenleyici'dir (IME).
  • Etkinlik, sistem tarafından gönderilen bir PendingIntent ile başlatılır (ör. bildirime dokunarak).
  • Uygulamaya, kullanıcı tarafından SYSTEM_ALERT_WINDOW izni verilmiş olmalıdır.
  • Uygulamaya START_ACTIVITIES_FROM_BACKGROUND izni verilmiş olmalıdır.
  • Uygulama, arka plan etkinliklerini başlatma izni verilmiş bir hizmete bağlıdır.
  • Başlatma, cihazın başlatıcı uygulaması tarafından başlatılır. Örneğin, kullanıcı bir uygulama simgesine dokunduğunda veya bir widget ile etkileşimde bulunduğunda başlatma işlemi gerçekleşir.
  • Başlatma, işletim sisteminin her zaman çalışması gereken temel bir bölümünden (ör. gelen arama ekranını başlatan Telefon hizmeti) yapılıyordur.

Yeni sağlamlaştırma ve gerekli etkinleştirmeler

Android, güvenliği daha da artırmak için PendingIntent ve IntentSender kullanarak etkinlik başlatan uygulamalarda açıkça izin verilmesini zorunlu kılan daha katı kurallar getirmiştir. Yalnızca PendingIntent oluşturan veya gönderen uygulama, arka planda başlatma ayrıcalıkları vermek için etkinleştirme yaparsa başlatmaya izin verilir.

Çoğu durumda, kullanıcının doğrudan etkileşimde bulunduğu uygulama (örneğin, bir düğmeye dokunma) olduğu için PendingIntent gönderen uygulamanın dahil olması gerekir.

Gönderenler, PendingIntent için özelliği etkinleştirmelidir.

Uygulamanız Android 14'ü (API düzeyi 34) veya sonraki sürümleri hedeflediğinde PendingIntent gönderirken BAL ayrıcalıklarını varsayılan olarak vermez. Açıkça kabul etmediğiniz takdirde, PendingIntent içerik üreticisi kendi ayrıcalıklarını daha önce vermediyse etkinlik başlatma engellenebilir.

Lansmanın başarılı olması için gönderenin, ActivityOptions.setPendingIntentBackgroundActivityStartMode() işlevini çağırarak ayrıcalıklarını vermeyi kabul etmesi gerekir. Önerilen mod ise ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE'dır (SDK 36'da eklenmiştir).

Bu, daha katı ve daha güvenli bir moddur. Bu izin, yalnızca PendingIntent gönderildiği sırada gönderen uygulama ekranda görünürse verilir. Bu, etkinliğin başlatılmasının kullanıcının uygulamanızla etkileşiminin doğrudan bir sonucu olmasını daha güçlü bir şekilde sağlar.

Beklemedeki amaçlar tablosu
Şekil 1: Arka plan etkinliği başlatma için karar akışı.

Ayrıcalık vermek için ActivityOptions.setPendingIntentBackgroundActivityStartMode() yöntemini kullanın.

// 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)
}

İçerik üreticiler, PendingIntent'i etkinleştirmelidir.

Uygulamanız Android 15'i (API düzeyi 35) veya sonraki sürümleri hedeflediğinde PendingIntent oluşturan bir uygulama, arka planda başlatma ayrıcalıklarını varsayılan olarak vermez. Gönderenin uygulamanızın BAL ayrıcalıklarını kullanmasına izin vermek için açıkça etkinleştirmeniz gerekir.

Ayrıcalık vermek için ActivityOptions.setPendingIntentCreatorBackgroundActivityStartMode() yöntemini kullanın.

// 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())

IntentSender ile başlatma

Aynı BAL kısıtlamaları, IntentSender kullanılarak etkinlik başlatılırken de geçerlidir. IntentSender, PendingIntent.getIntentSender aracılığıyla alındığından benzer etkinleştirme şartlarına tabidir.

  • Android 14'ten (API 34) itibaren Context.startIntentSender() kullanmak için gönderen tarafında izin verilmesi gerekir. ActivityOptions paketini de buraya eklemeniz gerekir.
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())

Adım Sırası Diyagramı: BAL Kısıtlamaları

Beklemedeki amaçlar tablosu
Şekil 2: PendingIntent kullanarak bir etkinliği güvenli bir şekilde başlatma süreci

Bu şema, PendingIntent kullanarak bir etkinliği güvenli bir şekilde başlatma sürecini gösterir. Başarılı bir lansman, geçerli bir ayrıcalık zincirine bağlıdır. Bu zincirde, katılan uygulamalardan en az biri hem ayrıcalıklarını verir hem de arka planda bir etkinlik başlatma özelliğine sahiptir.

  1. Oluşturma ve Yetki Verme (A Uygulaması - İçerik Üretici)
    1. Creator uygulaması PendingIntent oluşturur.
    2. Hedef SDK 35 veya üzeri ise ayrıcalıklarının kullanılmasını isteyen oluşturucu, setPendingIntentCreatorBackgroundActivityStartMode() kullanarak BAL ayrıcalıklarını açıkça devretmelidir. Varsayılan olarak hiçbir ayrıcalık devredilmez.
    3. PendingIntent daha sonra başka bir uygulamaya (B uygulaması) teslim edilir.
  2. Lansman ve Katkı (B Uygulaması - Gönderen)
    1. Daha sonra, Gönderen uygulaması PendingIntent.send() işlevini çağırarak başlatma işlemini gerçekleştirir.
    2. SDK 34 ve sonraki sürümler hedefleniyorsa gönderen, ayrıcalıklarının kullanılmasını istiyorsa setPendingIntentBackgroundActivityStartMode() kullanarak kendi ayrıcalıklarını açıkça katkıda bulunmalıdır. Varsayılan olarak hiçbir ayrıcalık devredilmez.
  3. Android System Security Validation
    1. Android Sistemi, başlatma isteğini engeller ve güvenlik kontrolü gerçekleştirir.
    2. İki koşulu değerlendirir:
    3. İçerik üretici, ayrıcalıklarını devretti mi VE içerik üretici uygulaması şu anda genel BAL istisnalarından birini karşılıyor mu?
    4. Gönderen, ayrıcalıklarını paylaştı mı VE Gönderen uygulaması şu anda genel BAL istisnalarından birini karşılıyor mu?
  4. Sonuç
    1. İzin verilir: 3. adımdaki iki koşuldan en az biri karşılanırsa ayrıcalık zinciri doğrulanır. Android sistemi hedef etkinliği başlatır ve gönderen, başarılı bir sonuç alır.
    2. ENGELENDİ: İki koşul da karşılanmazsa sistem başlatmayı engeller. Gönderen uygulama, doğrudan bir dönüş değeri veya hatayı belirten bir istisna almıyor. Bunun yerine Android Sistemi, Logcat'e dahili olarak "Arka plan etkinliği başlatma engellendi!" mesajını kaydeder. Geliştiriciler, hata ayıklama için bu mesajı kontrol etmelidir.

Görev ele geçirmeyi önleme

Android 15, görev içi ele geçirme saldırılarını (ör. "Etkinlik Sandviçi") önlemek için API düzeyi 37 veya sonraki sürümleri hedefleyen uygulamalara yönelik yeni kurallar getiriyor.

  • 1. Kural: Bir görev içinde bir etkinlik yalnızca aynı uygulamaya ait (yani aynı UID'ye sahip) başka bir etkinlik tarafından başlatılabilir. Bu etkinlik, görevdeki en üstteki mevcut etkinlik olmalıdır.
  • 2. Kural: Yalnızca en üstteki etkinliğin UID'siyle eşleşen bir ön plan görevi içindeki etkinliğin yeni bir görev oluşturmasına veya farklı bir mevcut görevi ön plana getirmesine izin verilir.

Geliştiriciler, görev içi korumaları etkinleştirebilir

Bu davranış, hedef SDK 37'den itibaren etkinleştirilebilir. Etkinleştirmek için açıkça kabul etmeniz gerekir. Kötü amaçlı bir uygulamanın, kimliğine bürünmek ve kullanıcı verilerini çalmak için uygulamanızın görevinde bir etkinlik başlatabileceği görev ele geçirme (veya etkinlik sandviçi) saldırılarını önlemek için tasarlanmıştır.

Korumaları etkinleştirme

Uygulamanızda ASM'yi etkinleştirmek için AndroidManifest.xml dosyanızda android:allowCrossUidActivitySwitchFromBelow özelliğini false olarak ayarlayın. Bu, uygulamanızdaki tüm etkinlikleri varsayılan olarak koruyan bir uygulama düzeyinde ayardır.

Belirli etkinlikler için istisnalar oluşturma

Uygulamanız için etkinleştirdiyseniz ancak belirli ve güvenilir bir etkinliğin diğer uygulamalar tarafından başlatılmasına izin vermeniz gerekiyorsa hedeflenmiş bir istisna oluşturabilirsiniz. Tek bir etkinliği bu korumadan muaf tutmak için söz konusu etkinliğin onCreate() yönteminde setAllowCrossUidActivitySwitchFromBelow(true) yöntemini çağırın. Bu sayede, uygulamanızın geri kalanı korunmaya devam ederken söz konusu etkinliğin başlatılmasına izin verilir.

Sorun giderme

Normal ifade kullanarak Logcat'i filtreleyip alakalı mesajları bulabilirsiniz. Etiket ActivityTaskManager genellikle kullanılır ve ActivityTaskManager ile filtreleme, günlükleri ayırmaya yardımcı olabilir.

Önemli günlük mesajlarını anlama

Engellenen Başlatma (Hata): Bu mesaj, bir etkinlik başlatmanın engellendiğini gösterir.

Günlükleri analiz ederken şu alanları kontrol edin:

  • realCallingPackage: PendingIntent'i gönderen uygulama. Bu, gönderendir.
  • callingPackage: PendingIntent'i oluşturan uygulama. Bu, içerik üreticidir.

Yüksek düzey modu

Android 16'dan itibaren uygulama geliştirici, etkinlik başlatma işlemi engellendiğinde (veya uygulamanın hedef SDK'sı yükseltildiğinde engellenme riski olduğunda) bildirim almak için yüksek düzey modu etkinleştirebilir.

Uygulamanızın, etkinliğinizin veya diğer uygulama bileşeninizin Application.onCreate() yönteminde erken aşamada etkinleştirmek için örnek kod:

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