본문 바로가기
Development/Android

[Android] Android exported 속성 설정 방법과 보안 취약점 해결

by 은스타 2022. 10. 4.
반응형
Android exported 속성 설정 방법과 보안 취약점 해결

Android exported 속성 설정 방법과 보안 취약점 해결

Android 개발 시 AndroidManifest.xml에서 설정하는 'android:exported' 속성은 앱의 보안을 결정하는 핵심 요소입니다. 특히 Android 12(API 31) 이상을 타겟팅하는 앱에서는 이 속성을 명시적으로 선언하지 않으면 설치조차 되지 않습니다. 이 글에서는 exported 속성이 무엇인지, 언제 true 또는 false로 설정해야 하는지, 그리고 부적절한 설정으로 인한 보안 취약점 사례까지 실전 예제와 함께 상세히 알아보겠습니다.

목차
1. exported 속성의 개념과 Android 12 변화
2. exported 속성 설정 방법과 실전 예제
3. true/false 설정 기준과 권장사항
4. 보안 취약점 사례와 컴포넌트별 권장 설정
5. 자주 묻는 질문 (FAQ)

#1. exported 속성의 개념과 Android 12 변화
Android의 exported 속성은 앱의 컴포넌트(Activity, Service, BroadcastReceiver, ContentProvider)가 다른 앱에서 접근 가능한지 여부를 결정하는 보안 설정입니다. AndroidManifest.xml 파일에서 각 컴포넌트에 대해 이 속성을 정의할 수 있으며, 앱의 보안에 직접적인 영향을 미칩니다.
1) exported 속성 값의 의미
exported 속성은 두 가지 값을 가질 수 있습니다.
true: 다른 앱에서 이 컴포넌트에 접근할 수 있음
false: 오직 같은 앱 또는 같은 사용자 ID를 가진 앱만 이 컴포넌트에 접근할 수 있음
<activity
    android:name=".MainActivity"
    android:exported="true">
    <!-- 다른 앱에서 이 Activity를 실행할 수 있음 -->
</activity>
이 속성은 특히 앱 간 통신(Inter-Process Communication, IPC)이 필요한 상황에서 중요하며, 부적절하게 설정될 경우 앱의 보안 취약점이 될 수 있습니다.
. . . . .
2) Android 12(API 31) 이상의 필수 변경사항
Android 12(API 레벨 31)부터는 exported 속성이 필수가 되었습니다. 이는 안드로이드 플랫폼의 보안을 강화하기 위한 중요한 변화입니다.
(1) 주요 변경사항
① Android 12를 타겟팅하는 앱은 인텐트 필터(Intent Filter)가 있는 모든 컴포넌트에 대해 반드시 exported 속성을 명시적으로 선언해야 합니다.
② 이전 버전에서는 인텐트 필터가 있으면 자동으로 exported=true로 간주되었지만, 이제는 명시적 선언이 필요합니다.
③ 이 요구사항을 지키지 않으면 앱 설치 또는 업데이트가 실패합니다.
(2) 에러 메시지 예시
Android 12 이상을 타겟팅하는 앱에서 exported 속성을 명시하지 않으면 다음과 같은 오류가 발생합니다.
Manifest merger failed : Apps targeting Android 12 and higher are required to specify
an explicit value for android:exported when the corresponding component has an intent
filter defined.

#2. exported 속성 설정 방법과 실전 예제
AndroidManifest.xml 파일에서 각 안드로이드 컴포넌트에 대해 exported 속성을 설정할 수 있습니다. 컴포넌트별 실전 예제를 살펴보겠습니다.
1) Activity 설정 예제
<!-- 외부 앱에서 실행 가능한 Activity -->
<activity
    android:name=".PublicActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

<!-- 앱 내부에서만 접근 가능한 Activity -->
<activity
    android:name=".PrivateActivity"
    android:exported="false" />
. . . . .
2) Service 설정 예제
<!-- 외부 앱에서 시작할 수 있는 Service -->
<service
    android:name=".PublicService"
    android:exported="true" />

<!-- 앱 내부에서만 사용하는 Service -->
<service
    android:name=".PrivateService"
    android:exported="false" />
. . . . .
3) BroadcastReceiver 설정 예제
<!-- 시스템 이벤트를 수신하는 Receiver -->
<receiver
    android:name=".SystemEventReceiver"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

<!-- 앱 내부 이벤트만 처리하는 Receiver -->
<receiver
    android:name=".InternalEventReceiver"
    android:exported="false" />
. . . . .
4) ContentProvider 설정 예제
<!-- 다른 앱과 데이터를 공유하는 ContentProvider -->
<provider
    android:name=".PublicDataProvider"
    android:authorities="com.example.app.provider"
    android:exported="true" />

<!-- 앱 내부에서만 사용하는 ContentProvider -->
<provider
    android:name=".PrivateDataProvider"
    android:authorities="com.example.app.private.provider"
    android:exported="false" />

#3. true/false 설정 기준과 권장사항
exported 속성을 올바르게 설정하려면 각 컴포넌트의 용도와 접근 범위를 정확히 파악해야 합니다. 상황별 설정 기준을 살펴보겠습니다.
1) exported="true"로 설정해야 하는 경우
(1) 앱의 런처 Activity
앱을 시작하는 메인 화면은 사용자가 시스템 런처에서 접근할 수 있어야 하므로 exported="true"로 설정해야 합니다.
<activity
    android:name=".MainActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
(2) 딥 링크를 처리하는 Activity
웹 링크나 외부 앱에서 특정 화면으로 직접 이동하는 딥 링크를 구현할 때 해당 Activity는 exported="true"로 설정해야 합니다.
<activity
    android:name=".DeepLinkActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="https" android:host="example.com" />
    </intent-filter>
</activity>
(3) 시스템 이벤트를 수신하는 BroadcastReceiver
시스템 이벤트(예: 부팅 완료, 네트워크 변경 등)를 수신해야 하는 BroadcastReceiver는 exported="true"로 설정해야 합니다.
① 다른 앱에 제공하는 Service
② 데이터를 공유하는 ContentProvider
. . . . .
2) exported="false"로 설정해야 하는 경우
다음과 같은 상황에서는 exported 속성을 false로 설정하는 것이 적절합니다.
(1) 앱 내부에서만 사용하는 Activity
다른 앱에서 접근할 필요가 없는 내부 화면들은 모두 exported="false"로 설정하여 보안을 강화해야 합니다.
<activity
    android:name=".InternalSettingsActivity"
    android:exported="false" />
(2) 앱 내부 로직을 처리하는 Service
백그라운드 작업이나 앱 내부 로직을 처리하는 Service는 외부 접근을 차단해야 합니다.
① 앱 내부 이벤트만 처리하는 BroadcastReceiver
② 앱 내부 데이터만 관리하는 ContentProvider

#4. 보안 취약점 사례와 컴포넌트별 권장 설정
부적절한 exported 설정으로 인해 발생할 수 있는 실제 보안 취약점 사례를 살펴보고, 컴포넌트별 권장 설정을 정리하겠습니다.
1) 보안 취약점 실제 사례
(1) 사례 1: 권한 없는 Activity 노출
<!-- 취약한 설정 -->
<activity
    android:name=".UserDataActivity"
    android:exported="true">
    <!-- 인텐트 필터가 없지만 exported="true"로 설정됨 -->
</activity>
취약점: 다른 앱에서 다음과 같은 코드로 사용자 데이터에 접근할 수 있습니다.
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.vulnerable.app",
    "com.vulnerable.app.UserDataActivity"));
startActivity(intent);
해결방법: 앱 내부 기능만 제공하는 Activity는 반드시 exported="false"로 설정해야 합니다.
(2) 사례 2: 보호되지 않은 Service 악용
<!-- 취약한 설정 -->
<service
    android:name=".PaymentService"
    android:exported="true" />
취약점: 악의적인 앱이 결제 서비스를 직접 호출할 수 있습니다.
해결방법: 중요한 기능을 수행하는 Service는 exported="false"로 설정하고, 필요한 경우 커스텀 퍼미션을 통해 접근을 제한해야 합니다.
. . . . .
2) 컴포넌트별 권장 설정
안드로이드 앱의 각 컴포넌트 유형별 권장 exported 설정은 다음과 같습니다.
(1) Activity 권장 설정
사용 목적 권장 설정 추가 보안 조치
앱 런처 화면 true -
딥 링크 대상 화면 true Intent 데이터 검증
앱 내부 화면 false -
설정 화면 false -
결제 관련 화면 false -
(2) Service 권장 설정
사용 목적 권장 설정 추가 보안 조치
앱 내부 백그라운드 작업 false -
외부 앱 제공 기능 true 커스텀 퍼미션
데이터 동기화 false -
푸시 메시지 처리 false -
(3) ContentProvider 권장 설정
주의: ContentProvider의 기본 exported 값은 true입니다. 명시적으로 exported="false"로 설정하지 않으면 다른 앱에서 접근할 수 있으므로 특히 주의해야 합니다.

#5. 자주 묻는 질문 (FAQ)
1) Q: exported="true"와 인텐트 필터의 관계는 무엇인가요?
A: Android 11(API 30) 이하에서는 인텐트 필터가 있는 컴포넌트는 자동으로 exported="true"로 간주됩니다. 그러나 Android 12(API 31) 이상에서는 인텐트 필터의 유무와 관계없이 명시적으로 exported 속성을 설정해야 합니다.
. . . . .
2) Q: exported="false"로 설정된 컴포넌트는 어떻게 접근할 수 있나요?
A: exported="false"로 설정된 컴포넌트는 동일한 앱 내부 또는 동일한 서명과 사용자 ID를 가진 앱에서만 접근할 수 있습니다. 다른 앱에서는 접근이 불가능합니다.
. . . . .
3) Q: 앱에 필요한 최소한의 exported="true" 컴포넌트는 무엇인가요?
A: 일반적으로 다음 컴포넌트만 exported="true"로 설정하면 됩니다.
① 앱 런처 Activity
② 딥 링크를 처리하는 Activity
③ 시스템 이벤트를 수신하는 BroadcastReceiver
④ 다른 앱에 기능을 제공하는 Service 또는 ContentProvider
. . . . .
4) Q: exported="true"로 설정해야 하는 컴포넌트에 대한 추가 보안 조치는?
A: 다음과 같은 추가 보안 조치를 고려할 수 있습니다.
① 커스텀 퍼미션 정의 및 적용
② 인텐트 데이터의 유효성 검증
③ 발신자 패키지 또는 서명 확인
④ ContentProvider의 경우 URI 퍼미션 사용
. . . . .
5) Q: ContentProvider의 기본 exported 값은 무엇인가요?
A: ContentProvider의 기본 exported 값은 true입니다. 즉, 명시적으로 exported="false"로 설정하지 않으면 다른 앱에서 접근할 수 있습니다. 이는 다른 컴포넌트의 기본값과 다르므로 특히 주의해야 합니다.

마무리
Android 앱 개발에서 exported 속성은 앱 보안의 핵심 요소입니다. 특히 Android 12 이상에서는 모든 인텐트 필터가 있는 컴포넌트에 대해 exported 속성을 명시적으로 선언해야 하므로, 개발자는 이에 대한 정확한 이해가 필요합니다.
기본 원칙은 간단합니다: 필요한 경우에만 exported="true"로 설정하고, 나머지는 모두 false로 설정하세요. 이러한 최소 권한 원칙을 따르면 앱의 공격 표면을 최소화하고 보안 취약점을 크게 줄일 수 있습니다.
보안은 개발 과정의 마지막 단계가 아니라 처음부터 고려해야 하는 중요한 요소임을 기억하세요. exported 속성을 올바르게 설정하는 것은 안전한 안드로이드 앱을 개발하기 위한 첫 번째 단계입니다.
긴 글 읽어주셔서 감사합니다.

끝.
반응형