Android는 악성 앱으로부터 사용자를 보호하고 신뢰할 수 있는 UI 환경을 제공합니다. 활동 보안 프레임워크는 규칙과 플랫폼 제한사항을 포함합니다. 이러한 규칙과 제한사항은 원치 않는 UI 중단, 작업 도용, 기타 보안 위협을 방지합니다. 이러한 위협은 앱 구성요소가 화면에 표시되는 시점 및 방식과 관련이 있습니다. 이 프레임워크의 주요 구성요소는 백그라운드에서 활동을 시작하는 것을 제한합니다.
백그라운드 활동 실행 제한사항
백그라운드 활동 실행 (BAL)은 포그라운드에 있지 않고 표시되는 활동이 없는 앱 또는 다른 앱에서 수신한 PendingIntent가 새 활동을 시작하려고 할 때 발생합니다. 이는 백그라운드 활동 실행 (BAL)입니다.
알람 시계 앱이 시작될 때와 같은 합법적인 사용 사례가 있지만, 무제한 BAL은 사용자 환경을 저하시키고 보안 취약점을 만듭니다.
제한되는 이유는 무엇인가요?
Android 10 (API 수준 29)부터 플랫폼은 앱이 백그라운드에서 활동을 시작할 수 있는 시점을 제한했습니다. 이러한 보호 기능은 다음과 같은 일반적인 악용을 완화하여 악성 앱 동작을 방지하고 사용자 환경을 개선하는 데 도움이 됩니다.
- UI 도용 및 팝업 광고: 백그라운드 앱이 사용자가 현재 상호작용 중인 앱 위에 활동 (대부분 광고)을 예기치 않게 실행하여 세션을 도용합니다.
- 피싱 및 사칭: 백그라운드 앱이 다른 앱을 사칭하는 활동 (예: 합법적인 앱의 가짜 로그인 화면)을 실행하여 사용자 인증 정보를 도용합니다. 이는 악성 활동이 합법적인 앱의 작업 스택에 삽입되는 '활동 샌드위치' 공격을 통해 이루어지는 경우가 많습니다.
- 탭재킹: 백그라운드 앱이 다른 앱 위에 투명하거나 가려진 활동을 표시하여 사용자의 탭을 가로채고 의도하지 않은 작업을 하도록 속입니다.
- 앱 깨우기: 한 앱의 백그라운드 구성요소가 다른 앱의 포그라운드 구성요소를 깨워 일일 활성 사용자 측정항목을 불법적으로 늘립니다.
포그라운드 서비스 (진행 중인 작업용)
앱에서 음악 재생 또는 운동 추적과 같이 사용자가 인식해야 하는 장기 실행 작업을 백그라운드에서 실행해야 하는 경우 포그라운드 서비스를 사용해야 합니다. 포그라운드 서비스는 사용자가 닫을 수 없는 지속적인 알림을 표시해야 합니다. 이 알림은 대화형 컨트롤 (예: 음악 앱의 재생/일시중지 버튼)을 제공할 수 있습니다. 이렇게 하면 사용자에게 정보를 제공하고 제어할 수 있지만 전체 화면 활동으로 사용자를 방해하지는 않습니다.
표준 알림으로 시작하고 필요한 경우에만 더 침입적인 옵션으로 에스컬레이션하는 이 계층 구조를 따르면 사용자에게 더 나은 예측 가능한 환경을 만들 수 있습니다.
백그라운드 활동 시작이 허용되는 경우 (예외)
다음 조건 중 하나를 충족하는 경우 앱은 백그라운드에서 활동을 시작할 수 있습니다.
- 앱에 포그라운드의 활동과 같은 보이는 창이 있습니다.
- 앱이 현재 입력 방식 편집기 (IME)입니다.
- 활동이 시스템에서 전송한
PendingIntent(예: 알림 탭에서)에서 시작됩니다. - 앱에 사용자가 부여한
SYSTEM_ALERT_WINDOW권한이 있습니다. - 앱에
START_ACTIVITIES_FROM_BACKGROUND권한이 부여되었습니다. - 앱은 백그라운드 활동을 시작할 권한이 부여된 서비스에 의해 바인딩됩니다.
- 실행은 사용자가 앱 아이콘을 탭하거나 위젯과 상호작용할 때와 같이 기기의 런처 앱에서 시작됩니다.
- 실행은 항상 실행되어야 하는 운영체제의 핵심 부분에서 이루어집니다(예: 수신 전화 화면을 시작하는 전화 통신 서비스).
새로운 강화 및 필수 선택
보안을 더욱 강화하기 위해 Android는 PendingIntent 및 IntentSender를 사용하여 활동을 실행하는 앱에 명시적 선택을 요구하는 더 엄격한 규칙을 도입했습니다. PendingIntent를 만든 앱 또는 이를 전송하는 앱이 백그라운드 실행 권한을 부여하도록 선택한 경우에만 실행이 허용됩니다.
대부분의 경우 PendingIntent를 전송 하는 앱이 선택해야 합니다. 일반적으로 사용자가 직접 상호작용하는 앱 (예: 버튼 탭)이기 때문입니다.
발신자는 PendingIntent를 선택해야 함
앱이 Android 14 (API 수준 34) 이상을 타겟팅하는 경우 PendingIntent를 전송할 때 더 이상 기본적으로 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)
}
BroadcastReceiver
생성자는 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로 실행
IntentSender를 사용하여 활동을 실행할 때도 동일한 BAL 제한사항이 적용됩니다. 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<IntentSenderRequest> 사용: 이 AndroidX API는 내부적으로 Context.startIntentSender()를 사용하므로 BAL 제한사항의 영향을 받습니다.
시퀀스 다이어그램: BAL 제한사항
이 다이어그램은 PendingIntent를 사용하여 활동을 안전하게 실행하는 프로세스를 보여줍니다. 실행이 성공하려면 참여 앱 중 하나 이상이 권한을 부여하고 백그라운드에서 활동을 실행할 수 있는 유효한 권한 체인 이 있어야 합니다.
- 생성 및 위임 (앱 A - 생성자)
- 생성자 앱이
PendingIntent를 빌드합니다. - SDK 35 이상을 타겟팅하는 경우 생성자는 권한을 사용하려면 setPendingIntentCreatorBackgroundActivityStartMode()를 사용하여 BAL 권한을 명시적으로 위임해야 합니다. 기본적으로 권한은 위임되지 않습니다.
- 그런 다음
PendingIntent가 다른 앱 (앱 B)에 전달됩니다.
- 생성자 앱이
- 실행 및 기여 (앱 B - 발신자)
- 나중에 발신자 앱이
PendingIntent.send()를 호출하여 실행을 시작합니다. - SDK 34 이상을 타겟팅하는 경우 발신자는 권한을 사용하려면 setPendingIntentBackgroundActivityStartMode()를 사용하여 자체 권한을 명시적으로 기여해야 합니다. 기본적으로 권한은 위임되지 않습니다.
- 나중에 발신자 앱이
- Android 시스템 보안 검증
- Android 시스템이 실행 요청을 가로채고 보안 검사를 실행합니다.
- 두 가지 조건을 평가합니다.
- 생성자가 권한을 위임했나요? 그리고 생성자 앱이 현재 일반적인 BAL 예외 중 하나를 충족하나요?
- 발신자가 권한을 기여했나요? 그리고 발신자 앱이 현재 일반적인 BAL 예외 중 하나를 충족하나요?
- 결과
- 허용됨: 3단계의 두 조건 중 하나 이상이 충족되면 권한 체인이 검증됩니다. Android 시스템이 타겟 활동을 시작하고 발신자가 성공 결과를 수신합니다.
- 차단됨: 두 조건이 모두 충족되지 않으면 시스템이 실행을 차단합니다. 발신자 앱은 실패를 나타내는 직접 반환 값 또는 예외를 수신하지 않습니다. 대신 Android 시스템은 내부적으로 Logcat에 "Background activity launch blocked!" 메시지를 로깅합니다. 개발자는 디버깅을 위해 이 메시지를 확인해야 합니다.
작업 도용 방지
작업 내 도용 공격 (예: '활동 샌드위치')을 방지하기 위해 Android 15는 API 수준 37 이상을 타겟팅하는 앱에 새로운 규칙을 도입합니다.
- 규칙 1: 단일 작업 내에서 활동은 작업의 현재 최상위 활동과 동일한 애플리케이션에 속한 (즉, 동일한 UID를 가진) 다른 활동에 의해서만 실행될 수 있습니다.
- 규칙 2: 최상위 활동의 UID와 일치하는 포그라운드 작업 내의 활동만 새 작업을 만들거나 다른 기존 작업을 포그라운드로 가져올 수 있습니다.
작업 내 보호를 위한 개발자 선택
이 동작은 타겟 SDK 37부터 사용 설정할 수 있으며 사용 설정하려면 명시적으로 선택해야 합니다. 이는 악성 앱이 앱의 작업에 활동을 실행하여 사칭하고 사용자 데이터를 도용할 수 있는 작업 내 도용 (또는 활동 샌드위치)을 방지하도록 설계되었습니다.
보호 사용 설정
애플리케이션에 ASM을 선택하고 사용 설정하려면 AndroidManifest.xml 파일에서 android:allowCrossUidActivitySwitchFromBelow 속성을 false로 설정합니다. 이는 기본적으로 앱의 모든 활동을 보호하는 애플리케이션 수준 설정입니다.
특정 활동에 대한 예외 만들기
앱에 사용 설정했지만 다른 앱에서 특정 신뢰할 수 있는 활동을 실행하도록 허용해야 하는 경우 타겟 예외를 만들 수 있습니다. 이 보호에서 단일 Activity를 제외하려면 해당 Activity의 onCreate() 메서드 내에서 setAllowCrossUidActivitySwitchFromBelow(true)를 호출합니다. 이렇게 하면 앱의 나머지 부분은 보호된 상태로 유지되면서 하나의 활동을 실행할 수 있습니다.
문제 해결
정규 표현식을 사용하여 관련 메시지를 찾도록 Logcat을 필터링합니다. ActivityTaskManager 태그가 자주 사용되며 ActivityTaskManager로 필터링하면 로그를 격리하는 데 도움이 될 수 있습니다.
주요 로그 메시지 이해
차단된 실행 (오류): 이 메시지는 활동 시작이 차단되었음을 나타냅니다.
- 의미: 발신자 (SDK 34 이상 타겟팅) 또는 생성자 (SDK 35 이상 타겟팅)에 필요한 PendingIntent 선택이 누락되어 활동 시작이 거부되었습니다.
- 작업: 올바른 ActivityOptions 선택을 포함하도록 코드를 업데이트해야 합니다.
로그를 분석할 때는 다음 필드를 확인하세요.
- realCallingPackage: PendingIntent 를 전송한 앱입니다. 이는 발신자 입니다.
- callingPackage: PendingIntent 를 만든 앱입니다. 이는 생성자 입니다.
엄격 모드
Android 16부터 앱 개발자는 엄격 모드를 사용 설정하여 활동 실행이 차단될 때 (또는 앱의 타겟 SDK가 올라갈 때 차단될 위험이 있을 때) 알림을 받을 수 있습니다.
애플리케이션, 활동 또는 기타 애플리케이션 구성요소의 Application.onCreate() 메서드에서 초기에 사용 설정하는 코드 예:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder()
.detectBlockedBackgroundActivityLaunch()
.penaltyLog()
.build());
)
}