Android מגן על המשתמשים מפני אפליקציות זדוניות ומספק חוויית משתמש מהימנה. מסגרת האבטחה של הפעילות כוללת כללים והגבלות על הפלטפורמה. הכללים וההגבלות האלה מונעים הפרעות לא רצויות בממשק המשתמש, חטיפת משימות ואיומי אבטחה אחרים. האיומים האלה קשורים לזמן שבו רכיבי האפליקציה מופיעים במסך ולאופן שבו הם מופיעים. רכיב מרכזי במסגרת הזו מגביל את התחלת הפעילויות מהרקע.
הגבלות על הפעלת פעילות ברקע
הפעלה של פעילות ברקע (BAL) מתרחשת כשאפליקציה שלא נמצאת בחזית, ללא פעילויות גלויות, או PendingIntent שהתקבל מאפליקציה אחרת, מנסה להתחיל פעילות חדשה. זוהי הפעלה של פעילות ברקע (BAL).
יש תרחישי שימוש לגיטימיים, למשל כשמפעילים אפליקציית שעון מעורר, אבל שימוש לא מוגבל ב-BALs מוביל לחוויית משתמש גרועה ויוצר נקודות חולשה באבטחה.
למה הן מוגבלות?
החל מ-Android 10 (רמת API 29), הפלטפורמה מטילה הגבלות על המקרים שבהם אפליקציות יכולות להתחיל פעילויות מהרקע. ההגנות האלה עוזרות למנוע התנהגות זדונית של אפליקציות ולשפר את חוויית המשתמש על ידי צמצום מקרים נפוצים של שימוש לרעה, כולל:
- חטיפת ממשק משתמש ומודעות קופצות: אפליקציה ברקע מפעילה באופן לא צפוי פעילות (לרוב מודעה) מעל האפליקציה שהמשתמש מקיים איתה אינטראקציה, ובכך חוטפת את הסשן שלו.
- פישינג והתחזות: אפליקציה שפועלת ברקע מפעילה פעילות שמתחזה לאפליקציה אחרת (לדוגמה, מסך כניסה מזויף לאפליקציה לגיטימית) כדי לגנוב את פרטי הכניסה של המשתמש. לרוב, הפעולה הזו מתבצעת באמצעות מתקפת 'כריך פעילות', שבה פעילות זדונית מוחדרת למחסנית המשימות של אפליקציה לגיטימית.
- השתלטות על הקשה: אפליקציה שפועלת ברקע מציגה פעילות שקופה או מוסתרת מעל אפליקציה אחרת כדי ליירט את ההקשות של המשתמש ולהטעות אותו כך שיבצע פעולות לא מכוונות.
- הפעלת אפליקציה: רכיב ברקע של אפליקציה אחת מפעיל רכיבים בחזית של אפליקציה אחרת כדי להגדיל באופן לא לגיטימי את מדדי המשתמשים הפעילים היומיים.
שירותים שפועלים בחזית (למשימות שוטפות)
אם האפליקציה צריכה לבצע ברקע משימה ממושכת שהמשתמש צריך להיות מודע לה, כמו הפעלת מוזיקה או מעקב אחר אימון, צריך להשתמש בשירות שפועל בחזית. שירות שפועל בחזית חייב להציג התראה קבועה שהמשתמש לא יכול לסגור. ההתראה הזו יכולה לספק אמצעי בקרה אינטראקטיביים (לדוגמה, לחצני הפעלה/השהיה לאפליקציית מוזיקה). כך המשתמשים מקבלים מידע ויכולים לשלוט בפעולות, אבל לא מפריעים להם עם פעילות במסך מלא.
אם תפעלו לפי ההיררכיה הזו – תתחילו בהצגת הודעות רגילות ותעברו לאפשרויות פולשניות יותר רק כשצריך – תוכלו ליצור חוויה טובה וצפויה יותר למשתמשים.
מתי מותר להפעיל התחלות של פעילות ברקע (חריגים)
אפליקציה יכולה להתחיל פעילות מהרקע אם מתקיים אחד מהתנאים הבאים:
- לאפליקציה יש חלון גלוי, כמו פעילות בחזית.
- האפליקציה היא עורך שיטות הקלט (IME) הנוכחי.
- הפעילות מתחילה מ-
PendingIntentשנשלח על ידי המערכת (לדוגמה, מלחיצה על התראה). - המשתמש העניק לאפליקציה את ההרשאה
SYSTEM_ALERT_WINDOW. - ההרשאה
START_ACTIVITIES_FROM_BACKGROUNDהוענקה לאפליקציה. - האפליקציה קשורה לשירות שקיבל הרשאה להפעיל פעילויות ברקע.
- ההפעלה מתחילה באפליקציית מרכז האפליקציות של המכשיר, למשל כשמשתמש מקיש על סמל של אפליקציה או מקיים אינטראקציה עם ווידג'ט.
- ההפעלה מתבצעת מחלק ליבה של מערכת ההפעלה שצריך לפעול בכל זמן, כמו שירות הטלפוניה שמפעיל את מסך השיחה הנכנסת.
אבטחה משופרת והסכמה להצטרפות
כדי לשפר עוד יותר את האבטחה, ב-Android הוספנו כללים מחמירים יותר שמחייבים קבלת הסכמה מפורשת מאפליקציות שמשתמשות ב-PendingIntent וב-IntentSender כדי להפעיל פעילויות. הפעלת האפליקציה מותרת רק אם האפליקציה שיצרה את PendingIntent או האפליקציה ששולחת אותו הביעו הסכמה להענקת הרשאות להפעלה ברקע.
ברוב המקרים, האפליקציה ששולחת את PendingIntent צריכה להיות האפליקציה שהמשתמש מקיים איתה אינטראקציה ישירה (למשל, לחיצה על לחצן).
השולחים צריכים להביע הסכמה ל-PendingIntent
כשהאפליקציה מטרגטת ל-Android 14 (רמת API 34) ומעלה, היא כבר לא מעניקה את הרשאות ה-BAL שלה כברירת מחדל כששולחים PendingIntent. אם לא תביעו הסכמה מפורשת, יכול להיות שהפעלת הפעילות תיחסם, אלא אם היוצר של PendingIntent כבר העניק הרשאות משלו.
כדי לוודא שההפעלה תצליח, השולח צריך להביע הסכמה להענקת ההרשאות שלו על ידי קריאה ל-ActivityOptions.setPendingIntentBackgroundActivityStartMode(). המצב המומלץ הוא ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE (נוסף ב-SDK 36).
זהו מצב מחמיר ובטוח יותר. ההרשאה ניתנת רק אם האפליקציה השולחת גלויה במסך ברגע ששולחים את PendingIntent. כך אפשר לוודא שהפעלת הפעילות היא תוצאה ישירה של אינטראקציה של המשתמש עם האפליקציה.
משתמשים ב-ActivityOptions.setPendingIntentBackgroundActivityStartMode() כדי להעניק הרשאות.
// 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)
}
יוצרים חייבים להביע הסכמה לשימוש ב-PendingIntent
כשהאפליקציה מטרגטת ל-Android 15 (רמת API 35) ומעלה, אפליקציה שיוצרת PendingIntent כבר לא מעניקה הרשאות להפעלה ברקע כברירת מחדל. כדי לאפשר לשולח להשתמש בהרשאות ה-BAL של האפליקציה, צריך להביע הסכמה מפורשת.
משתמשים ב-ActivityOptions.setPendingIntentCreatorBackgroundActivityStartMode() כדי להעניק הרשאות.
// 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
אותן הגבלות של BAL חלות גם כשמפעילים פעילויות באמצעות IntentSender. מאחר ש-IntentSender מתקבל באמצעות PendingIntent.getIntentSender, הוא כפוף לדרישות דומות להסכמה.
- החל מ-Android 14 (API 34), כדי להשתמש ב-Context.startIntentSender() צריך להביע הסכמה בצד השולח. עליכם לספק כאן גם את חבילת
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())
- החל מ-Android 17 (API 37 ואילך), כדי להשתמש ב-IntentSender.sendIntent(), צריך להביע הסכמה בצד השולח.
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())
- באמצעות ActivityResultLauncher
: ממשק ה-API הזה של AndroidX משתמש ב-Context.startIntentSender() באופן פנימי ולכן מושפע מההגבלות על הפעלת פעילויות ברקע.
תרשים רצף: הגבלות על BAL
בתרשים הזה מוצג תהליך ההפעלה המאובטחת של פעילות באמצעות PendingIntent. השקה מוצלחת תלויה בשרשרת הרשאות תקינה, שבה לפחות אחת מהאפליקציות המשתתפות מעניקה את ההרשאות שלה ויש לה את היכולת להפעיל פעילות מהרקע
- יצירה והקצאת הרשאות (אפליקציה א' – היוצר)
- אפליקציית Creator יוצרת את
PendingIntent - אם מטרגטים SDK בגרסה 35 ומעלה, היוצר צריך להעביר במפורש את ההרשאות של BAL באמצעות setPendingIntentCreatorBackgroundActivityStartMode() אם הוא רוצה להשתמש בהרשאות האלה. כברירת מחדל, לא מוקצות הרשאות.
- ה-
PendingIntentמועבר לאפליקציה אחרת (אפליקציה ב')
- אפליקציית Creator יוצרת את
- הפעלה ותרומה (אפליקציה ב' – השולח)
- בשלב מאוחר יותר, אפליקציית השולח מפעילה את ההפעלה על ידי קריאה ל-
PendingIntent.send(). - אם טירגוטים לגרסה 34 ומעלה של SDK, השולח צריך לתרום באופן מפורש את ההרשאות שלו באמצעות setPendingIntentBackgroundActivityStartMode() אם הוא רוצה שההרשאות שלו ישמשו לטירגוט. כברירת מחדל, לא מוקצות הרשאות.
- בשלב מאוחר יותר, אפליקציית השולח מפעילה את ההפעלה על ידי קריאה ל-
- Android System Security Validation
- מערכת Android מיירטת את בקשת ההפעלה ומבצעת בדיקת אבטחה.
- הפונקציה בודקת שני תנאים:
- האם היוצר העביר את ההרשאות שלו, והאם האפליקציה של היוצר עומדת כרגע באחד מהחריגים הכלליים לכלל האיזון בין אינטרסים?
- האם השולח תרם את ההרשאות שלו, והאם אפליקציית השולח עומדת כרגע באחד מהחריגים הכלליים ל-BAL?
- תוצאה
- מותר: אם לפחות אחד משני התנאים בשלב 3 מתקיים, שרשרת ההרשאות מאומתת. מערכת Android מפעילה את פעילות היעד, והשולח מקבל תוצאה של הצלחה.
- BLOCKED: אם אף אחד מהתנאים לא מתקיים, המערכת חוסמת את ההפעלה. האפליקציה השולחת לא מקבלת ערך החזרה ישיר או חריגה שמציינים את הכשל. במקום זאת, מערכת Android מתעדת באופן פנימי הודעה של Background activity launch blocked! ב-Logcat, ומפתחים צריכים לבדוק את זה כדי לבצע ניפוי באגים.
מניעת חטיפת משימות
כדי למנוע מתקפות של חטיפת משימות (כמו 'כריך פעילות'), ב-Android 15 מוצגים כללים חדשים לאפליקציות שמטרגטות רמת API 37 ומעלה.
- כלל 1: במסגרת משימה אחת, אפשר להפעיל פעילות רק על ידי פעילות אחרת ששייכת לאותה אפליקציה (כלומר, יש לה את אותו UID) כמו הפעילות הנוכחית שמוצגת בחלק העליון של המשימה.
- כלל 2: רק פעילות במשימה שפועלת ברקע שתואמת ל-UID של הפעילות העליונה ביותר יכולה ליצור משימה חדשה או להעביר משימה קיימת אחרת לרקע.
הבעת הסכמה של מפתחים להפעלת אמצעי הגנה במהלך משימות
אפשר להפעיל את ההתנהגות הזו החל מגרסת SDK 37. כדי להפעיל אותה, צריך להביע הסכמה באופן מפורש. היא נועדה למנוע חטיפת משימות (או הוספת פעילות בין פעילויות), שבה אפליקציה זדונית יכולה להפעיל פעילות במשימה של האפליקציה שלכם כדי להתחזות לה ולגנוב נתוני משתמשים.
הפעלת אמצעי ההגנה
כדי להביע הסכמה ולהפעיל את ASM באפליקציה, צריך להגדיר את המאפיין android:allowCrossUidActivitySwitchFromBelow כ-false בקובץ AndroidManifest.xml. זו הגדרה ברמת האפליקציה שמגנה על כל הפעילויות באפליקציה כברירת מחדל.
יצירת חריגים לפעילויות ספציפיות
אם הפעלתם את התכונה הזו באפליקציה שלכם, אבל אתם צריכים לאפשר לאפליקציות אחרות להפעיל פעילות ספציפית ומהימנה, אתם יכולים ליצור חריג ממוקד. כדי להחריג פעילות אחת מההגנה הזו, צריך להפעיל את setAllowCrossUidActivitySwitchFromBelow(true) בתוך השיטה onCreate() של הפעילות הזו. כך אפשר להפעיל פעילות אחת בזמן ששאר האפליקציה נשארת מוגנת.
פתרון בעיות
מסננים את Logcat כדי למצוא הודעות רלוונטיות באמצעות ביטוי רגולרי. התג
ActivityTaskManager נמצא בשימוש לעיתים קרובות, וסינון לפי
ActivityTaskManager יכול לעזור לבודד את היומנים.
הסבר על הודעות חשובות ביומן
הפעלה חסומה (שגיאה): ההודעה הזו מציינת שהפעלה של פעילות נחסמה.
- משמעות: נדחתה התחלה של פעילות כי לא הייתה הסכמה ל-PendingIntent הנדרש, אצל השולח (טירגוט SDK 34 ומעלה) או אצל היוצר (טירגוט SDK 35 ומעלה).
- פעולה: עליך לעדכן את הקוד כדי לכלול את בקשת ההסכמה הנכונה של ActivityOptions.
כשמנתחים את היומנים, כדאי לבדוק את השדות הבאים:
- realCallingPackage: האפליקציה ששלחה את PendingIntent. זהו השולח.
- callingPackage: האפליקציה שיצרה את PendingIntent. זהו היוצר.
מצב קפדני
החל מ-Android 16, מפתחי האפליקציות יכולים להפעיל את מצב קפדני כדי לקבל הודעה כשפעילות מופעלת נחסמת (או שיש סיכון שהיא תיחסם כשה-SDK של יעד האפליקציה מועלה).
קוד לדוגמה להפעלה מוקדמת באפליקציה, בפעילות או ברכיב אחר של האפליקציה, ב-method Application.onCreate():
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder()
.detectBlockedBackgroundActivityLaunch()
.penaltyLog()
.build());
)
}