Android에는 애플리케이션 보안 문제의 빈도와 영향을 크게 줄이는 보안 기능이 내장되어 있습니다. 이 시스템은 사용자가 기본 시스템과 파일 권한으로 앱을 빌드하고 보안과 관련된 까다로운 결정을 피할 수 있도록 설계되었습니다.
안전한 앱을 빌드하는 데 도움이 되는 핵심 보안 기능은 다음과 같습니다.
- 사용자의 앱 데이터와 코드 실행을 다른 앱으로부터 격리하는 Android 애플리케이션 샌드박스
- 암호화, 권한, 보안 프로세스 간 통신(IPC)과 같은 일반적인 보안 기능을 강력하게 구현한 애플리케이션 프레임워크
- 일반적인 메모리 관리 오류와 연관된 위험을 줄여주는 ASLR(주소 공간 레이아웃 무작위 순서 지정),
no-execute(NX), ProPolice, safe_iop, OpenBSD
dlmalloc
및calloc
, Linuxmmap_min_addr
같은 기술 - 시스템 기능 및 사용자 데이터에 대한 액세스를 제한하기 위해 사용자가 부여하는 권한
- 앱별로 애플리케이션 데이터를 제어하기 위해 애플리케이션에서 정의하는 권한
이 페이지의 Android 보안 권장사항을 숙지하는 것이 중요합니다. 이러한 권장사항을 일반적인 코딩 습관으로 만들면 사용자에게 부정적인 영향을 미치는 보안 문제를 의도치 않게 야기하는 일을 방지할 수 있습니다.
인증
인증은 많은 키 보안 작업의 기본 요건입니다. 사용자 데이터, 앱 기능, 기타 리소스와 같은 보호된 애셋에 대한 액세스를 제어하려면 Android 앱에 인증을 추가해야 합니다.
앱을 인증 관리자와 통합하여 사용자의 인증 환경을 개선할 수 있습니다. 인증 관리자는 패스키, 비밀번호, 제휴 로그인 솔루션(예: Google 계정으로 로그인) 등 대부분의 주요 인증 방법에 관한 API 지원을 통합하는 Android Jetpack 라이브러리입니다.
앱의 보안을 더욱 강화하려면 지문 스캔 또는 얼굴 인식과 같은 생체 인식 인증 방법을 추가하는 것이 좋습니다. 생체 인식 인증을 추가하는 데 적합한 후보에는 금융, 의료 또는 ID 관리 앱이 포함될 수 있습니다.
Android의 자동 완성 프레임워크는 가입 및 로그인 과정을 간소화하여 오류율과 사용자 불편을 줄여줍니다. 자동 완성은 비밀번호 관리자와 통합되므로 쉽고 안전하게 저장하고 검색할 수 있는 무작위의 복잡한 비밀번호를 사용자가 선택할 수 있습니다.
데이터 보관
Android 애플리케이션의 가장 일반적인 보안 관련 우려사항은 기기에 저장한 데이터에 다른 앱이 액세스할 수 있는지의 여부입니다. 기기에 데이터를 저장하는 기본적인 세 가지 방법은 다음과 같습니다.
- 내부 저장소
- 외부 저장소
- 콘텐츠 제공자
다음 섹션에서는 각 저장 방식과 관련된 보안 문제를 설명합니다.
내부 저장소
기본적으로 내부 저장소에 만드는 파일은 여러분의 앱에서만 액세스할 수 있습니다. Android에는 이러한 보호 기능이 구현되어 있으며 대부분의 애플리케이션에서는 이 기능으로 충분합니다.
IPC 파일에 지원 중단된 MODE_WORLD_WRITEABLE
및 MODE_WORLD_READABLE
모드를 사용하지 않도록 합니다. 이러한 모드는 데이터 액세스를 특정 애플리케이션으로 제한하는 기능을 제공하지 않으며 데이터 형식을 제어할 수도 없기 때문입니다. 데이터를 다른 앱 프로세스와 공유하려면 콘텐츠 제공자를 대신 사용하는 것이 좋습니다. 콘텐츠 제공자는 다른 앱 읽기 및 쓰기 권한을 제공하며 사례별로 권한을 동적으로 부여할 수 있습니다.
외부 저장소
SD 카드와 같은 외부 저장소에 만들어진 파일은 누구나 읽고 쓸 수 있습니다. 외부 저장소는 사용자가 삭제할 수 있고 모든 애플리케이션에서 수정할 수 있으므로 민감하지 않은 정보만 외부 저장소에 저장합니다.
외부 저장소의 데이터를 처리할 때는 신뢰할 수 없는 소스의 데이터와 마찬가지로 입력 검증을 시행합니다. 동적 로드 이전에 실행 파일이나 클래스 파일을 외부 저장소에 저장하지 않도록 합니다. 앱이 외부 저장소에서 실행 파일을 가져오는 경우 동적으로 로드하기 전에 파일에 서명하고 암호로 인증해야 합니다.
콘텐츠 제공자
콘텐츠 제공자는 개발자의 자체 애플리케이션으로 제한하거나 내보내기를 통해 다른 애플리케이션이 액세스할 수 있는 구조화된 저장 메커니즘을 제공합니다. 다른 애플리케이션에 ContentProvider
액세스 권한을 제공하지 않으려면 애플리케이션 매니페스트에서 애플리케이션을 android:exported=false
로 표시하세요. 그 외의 경우에는 android:exported
속성을 true
로 설정하여 저장된 데이터에 다른 앱이 액세스하도록 허용합니다.
다른 애플리케이션에서 사용하도록 내보내는 ContentProvider
를 만드는 경우 읽기 및 쓰기에 관한 단일 권한을 지정하거나 읽기 및 쓰기에 관한 별도 권한을 지정할 수 있습니다. 현재 작업을 실행하는 데 필요한 권한으로 제한하는 것이 좋습니다. 일반적으로 권한을 삭제하여 기존 사용자에게 영향을 주기보다는 나중에 권한을 추가하여 새로운 기능을 노출하기가 더 쉽습니다.
자체 앱 간에만 데이터를 공유하는 데 콘텐츠 제공자를 사용하는 경우 android:protectionLevel
속성을 signature
보호로 설정하여 사용하는 것이 좋습니다. 서명 권한에는 사용자 확인이 필요하지 않으므로 콘텐츠 제공자 데이터에 액세스하는 앱이 동일한 키로 서명되면 사용자 환경이 향상되고 데이터 액세스에 관한 제어도 향상됩니다.
또한 콘텐츠 제공자는 android:grantUriPermissions
속성을 선언하고 구성요소를 활성화하는 Intent
객체에 FLAG_GRANT_READ_URI_PERMISSION
및 FLAG_GRANT_WRITE_URI_PERMISSION
플래그를 사용하여 더욱 세분화된 액세스를 제공할 수 있습니다. 이러한 권한의 범위는 <grant-uri-permission>
요소로 더욱 제한할 수 있습니다.
콘텐츠 제공자에 액세스할 때 query
, update
, delete()
와 같은 매개변수화된 쿼리 메서드를 사용하면 신뢰할 수 없는 소스로부터 오는 잠재적 SQL 삽입을 방지할 수 있습니다. 사용자 데이터를 매개변수화된 메서드에 제출하기 전에 데이터를 연결하여 selection
인수를 빌드한 경우 해당 메서드를 사용하는 것만으로는 충분하지 않습니다.
쓰기 권한에 관해 잘못된 보안 인식을 가지면 안 됩니다. 쓰기 권한은 창의적인 WHERE
절을 사용하고 결과를 파싱하여 일부 데이터를 확인할 수 있는 SQL 문을 허용합니다. 예를 들어 공격자는 특정 전화번호가 이미 존재하고 있을 때만 행을 수정하여 통화 기록에 같은 전화번호가 존재하는지 찾을 수 있습니다. 콘텐츠 제공자 데이터에 예측 가능한 구조가 있다면 쓰기 권한은 읽기 및 쓰기 권한을 모두 제공하는 것과 같을 수 있습니다.
권한
Android는 애플리케이션을 각각 샌드박스화하므로 애플리케이션에서는 리소스와 데이터를 명시적으로 공유해야 합니다. 이를 위해 애플리케이션에서는 기본 샌드박스에서 제공하지 않는 추가 기능(예: 카메라와 같은 기기 기능에 액세스)을 실행하기 위해 필요한 권한을 선언합니다.
권한 요청
앱이 요청하는 권한 수를 최소화합니다. 민감한 권한에 대한 액세스 권한을 제한하면 이러한 권한을 오용할 위험이 감소하여 사용자 채택률을 높이고 공격자가 공략할 수 있는 앱의 취약점을 줄일 수 있습니다. 일반적으로 앱이 작동하는 데 필요하지 않은 권한은 요청해서는 안 됩니다. 앱에서 권한을 선언해야 하는지 평가하기 가이드를 참고하세요.
가능하면 권한이 필요하지 않은 방식으로 애플리케이션을 설계합니다. 예를 들어 고유 식별자를 만들기 위해 기기 정보 액세스 권한을 요청하는 대신 애플리케이션의 UUID를 만드세요. 자세한 내용은 사용자 데이터 섹션을 참고하세요. 또는 권한이 필요한 외부 저장소를 사용하는 대신 내부 저장소에 데이터를 저장하기 바랍니다.
권한 요청 외에도 애플리케이션은 <permission>
요소를 사용하여 보안에 민감하고 ContentProvider
와 같은 다른 애플리케이션에 노출되는 IPC를 보호할 수 있습니다. 일반적으로 권한은 사용자에게 혼란을 줄 수 있으므로 가능하다면 사용자 확인 권한이 아니라 액세스 제어를 사용하는 것이 좋습니다. 예를 들어 한 명의 개발자가 제공한 애플리케이션 간 IPC 통신의 경우 권한에 서명 보호 수준을 사용해 보시기 바랍니다.
권한으로 보호되는 데이터를 유출하지 않도록 합니다. 이러한 유출은 앱이 데이터에 액세스할 권한이 있을 때만 사용할 수 있는 데이터를 이 IPC를 통해 노출하는 경우에 발생합니다. 앱 IPC 인터페이스의 클라이언트에 이와 동일한 데이터 액세스 권한이 없을 수도 있습니다. 이 문제의 발생 빈도와 잠재적 영향에 관한 자세한 내용은 USENIX에 게시된 권한 재위임: 공격과 방어 연구 논문을 참고하세요.
권한 정의
보안 요구사항을 충족하는 최소 권한 집합을 정의합니다. 애플리케이션은 주로 시스템 정의 권한이 많은 상황을 해결하기 때문에 새로운 권한을 생성하는 것이 상대적으로 드문 편입니다. 필요한 경우 기존 권한을 사용하여 액세스 검사를 시행하시기 바랍니다.
새 권한이 필요하다면 서명 보호 수준을 사용하여 작업할 수 있는지 고려하세요. 서명 권한은 사용자에게 투명하게 공개되며, 권한을 검사하는 애플리케이션과 동일한 개발자가 서명한 애플리케이션에서만 액세스할 수 있습니다.
그래도 새 권한을 만들어야 한다면 <permission>
요소를 사용하여 앱 매니페스트에서 권한을 선언합니다. 새 권한을 사용하는 앱은 매니페스트 파일에 <uses-permission>
요소를 추가하여 이 권한을 참조할 수 있습니다. addPermission()
메서드를 사용하여 권한을 동적으로 추가할 수도 있습니다.
위험한 보호 수준으로 권한을 만드는 경우에는 다음과 같은 여러 복잡성을 고려해야 합니다.
- 권한에는 사용자에게 필요한 보안 결정을 간결하게 설명하는 문자열이 있어야 합니다.
- 이 권한 문자열은 여러 다른 언어로 현지화되어야 합니다.
- 사용자는 권한이 혼동을 주거나 위험한 것으로 인지되면 애플리케이션을 설치하지 않을 수 있습니다.
- 권한 작성자가 설치되지 않은 경우 애플리케이션에서 권한을 요청할 수 있습니다.
이와 같은 각 사항은 개발자인 여러분에게 상당한 비기술적 부담을 줄 수 있으며 또한 사용자에게 혼란을 줄 수도 있습니다. 이 때문에 저희가 위험한 권한 수준의 사용을 권장하지 않는 것입니다.
네트워킹
네트워크 트랜잭션은 사용자의 비공개 데이터를 전송할 가능성이 있으므로 근본적으로 보안에 위협이 됩니다. 사람들은 특히 기기에서 네트워크 트랜잭션을 실행하는 경우 휴대기기의 개인 정보 보호 문제를 점차 인식하고 있으므로, 앱에서 항상 사용자의 데이터를 안전하게 유지할 수 있는 방향으로 모든 권장사항을 구현하는 것이 중요합니다.
IP 네트워킹
Android에서의 네트워킹은 다른 Linux 환경과 크게 다르지 않습니다. 핵심적인 고려사항은 보안 웹 트래픽을 위해 민감한 정보에 HttpsURLConnection
과 같은 적절한 프로토콜이 사용되도록 해야 한다는 것입니다. 휴대기기는 공용 Wi-Fi 핫스팟과 같은 안전하지 않은 네트워크에 자주 연결되기 때문에 서버에서 HTTPS가 지원되는 경우에는 항상 HTTP보다 HTTPS를 사용해야 합니다.
인증되고 암호화된 소켓 수준 통신은 SSLSocket
클래스를 사용하여 간단하게 구현할 수 있습니다. Android 기기가 Wi-Fi를 사용하여 안전하지 않은 무선 네트워크에 연결되는 빈도를 고려할 때, 네트워크를 통해 통신하는 모든 애플리케이션에 안전한 네트워크 사용이 적극 권장됩니다.
일부 애플리케이션에서는 민감한 IPC를 처리하는 데 localhost 네트워크 포트를 사용합니다. 이러한 인터페이스는 기기의 다른 애플리케이션에서 액세스할 수 있으므로 이 접근 방식을 사용해서는 안 됩니다. 대신 Service
에서와 같이 인증이 가능한 경우 Android IPC 메커니즘을 사용하세요. 특정되지 않은 IP 주소 INADDR_ANY
에 바인딩하면 애플리케이션이 모든 IP 주소에서 요청을 받을 수 있으므로 루프백을 사용하는 것보다도 좋지 않습니다.
HTTP 또는 기타 안전하지 않은 프로토콜에서 다운로드한 데이터는 신뢰하면 안 됩니다. 이러한 데이터에는 WebView
의 입력 유효성 검사와 HTTP를 통해 실행된 인텐트에 관한 응답이 포함됩니다.
전화 통신 네트워킹
단문 메시지 서비스(SMS) 프로토콜은 주로 사용자 간 통신을 위해 설계되었으며 데이터를 전송하려는 앱에는 적합하지 않습니다. SMS 제한사항으로 인해 데이터 메시지를 웹 서버에서 사용자 기기의 앱으로 전송하려면 Firebase 클라우드 메시징(FCM) 및 IP 네트워킹을 사용하는 것이 좋습니다.
SMS는 네트워크나 기기에서 암호화되거나 강력하게 인증되지 않으니 주의해야 합니다. 특히 모든 SMS 수신자는 악의적인 의도를 가진 사용자가 자신의 애플리케이션에 SMS를 전송했을 수도 있다는 사실을 인지해야 합니다. 따라서 인증되지 않은 SMS 데이터에 의존해 민감한 명령어를 실행해서는 안 됩니다. 또한 SMS가 네트워크에서 스푸핑되거나 가로채기될 수 있다는 점에 유의해야 합니다. Android 지원 기기에서 SMS 메시지는 브로드캐스트 인텐트로 전송되기 때문에 READ_SMS
권한이 있는 다른 애플리케이션에서 읽거나 캡처할 수 있습니다.
입력 검증
충분하지 않은 입력 검증은 실행 중인 플랫폼과 관계없이 애플리케이션에 영향을 주는 가장 일반적인 보안 문제 중 하나입니다. Android는 애플리케이션이 입력 검증 문제에 노출되는 것을 줄이는 플랫폼 수준의 대책을 보유하고 있으므로 가능한 한 이러한 기능을 사용하는 것이 좋습니다. 또한 유형 안전 언어를 선택하면 입력 검증 문제가 발생할 가능성을 줄일 수 있습니다.
네이티브 코드를 사용하면 파일에서 읽거나, 네트워크를 통해 수신되거나, IPC로부터 수신된 데이터에 보안 문제가 발생할 수 있습니다. 가장 일반적인 문제는 버퍼 오버플로우, UAF(Use-After-Free), OBO(Off-By-One) 오류입니다. Android는 이러한 오류의 악용을 줄이기 위해 ASLR 및 DEP(데이터 실행 방지) 같은 여러 기술을 제공하지만 이러한 기술로는 근본적인 문제가 해결되지 않습니다. 포인터 처리와 버퍼 관리에 주의하면 이러한 취약점을 방지할 수 있습니다.
JavaScript 및 SQL과 같은 동적인 문자열 기반 언어 역시 이스케이프 문자 및 스크립트 삽입으로 인해 입력 검증 문제가 발생할 수 있습니다.
SQL 데이터베이스나 콘텐츠 제공자에 제출된 쿼리 내에 있는 데이터를 사용하는 경우 SQL 삽입이 문제가 될 수 있습니다. 최선의 방어는 콘텐츠 제공자 관련 섹션에서 설명한 것처럼 매개변수화된 쿼리를 사용하는 것입니다. 읽기 전용 또는 쓰기 전용으로 권한을 제한해도 SQL 삽입과 관련된 잠재적인 위험을 줄일 수 있습니다.
이 섹션에서 설명한 보안 기능을 사용할 수 없는 경우 잘 구조화된 데이터 형식을 사용하고 데이터가 올바른 형식을 준수하는지 확인해야 합니다. 특정 문자 차단이나 문자 교체가 효과적인 전략이 될 수 있지만 이러한 기술은 실제 적용 시 오류가 발생하기 쉽기 때문에 가능한 한 사용하지 않는 것이 좋습니다.
사용자 데이터
사용자 데이터 보안을 위한 최선의 방법은 민감한 정보나 개인 정보에 액세스하는 API의 사용을 최소화하는 것입니다. 사용자 데이터에 대한 액세스 권한이 있더라도 가능한 한 그러한 데이터를 저장하거나 전송해서는 안 됩니다. 해시 또는 비가역 형태의 데이터를 사용하여 애플리케이션 로직을 구현할 수 있는지 고려해 보세요. 예를 들어 앱에서 이메일 주소의 해시를 기본 키로 사용하여 이메일 주소의 전송이나 저장을 방지할 수도 있습니다. 이렇게 하면 실수로 데이터를 노출할 가능성이 줄어들고 앱을 악용하려는 공격자의 시도도 줄일 수 있습니다.
비공개 데이터에 액세스해야 할 때마다 사용자를 인증하고 패스키, 인증 관리자와 같은 최신 인증 방법을 사용하세요. 앱에서 개인 정보에 액세스해야 하는 경우 일부 관할권에서는 해당 데이터의 사용 및 저장을 설명하는 개인정보처리방침을 제공하도록 요구할 수 있습니다. 사용자 데이터에 대한 액세스를 최소화하는 보안 권장사항을 준수하여 규정 준수를 간소화합니다.
또한 애플리케이션이 서드 파티(예: 광고용 서드 파티 구성요소나 애플리케이션에서 사용하는 서드 파티 서비스)에 개인 정보를 실수로 노출할 수 있는지도 고려해야 합니다. 구성요소나 서비스에서 개인 정보를 요구하는데 그 이유를 모르는 경우 정보를 제공해서는 안 됩니다. 일반적으로 애플리케이션의 개인 정보 액세스를 줄이면 여기에서 문제가 일어날 가능성도 줄어듭니다.
앱에서 민감한 정보에 액세스해야 하는 경우 이 정보를 서버에 전송해야 하는지 또는 클라이언트에서 작업을 실행할 수 있는지 평가해야 합니다. 사용자 데이터 전송을 방지하려면 민감한 정보를 사용하는 코드는 클라이언트에서 실행하는 것을 고려해 보세요. 또한 지나치게 관대한 IPC, 누구나 쓸 수 있는 파일, 네트워크 소켓을 통해 기기의 다른 애플리케이션에 사용자 데이터를 실수로 노출하지 않도록 해야 합니다. 지나치게 관대한 IPC는 권한 요청 섹션에서 논의된 것처럼 권한으로 보호되는 데이터 유출의 특수한 사례입니다.
전역 고유 식별자(GUID)가 필요한 경우 큰 고유 숫자를 만들어 저장하세요. 전화번호나 IMEI와 같은 전화 식별자는 개인 정보와 연결될 수 있으므로 사용하면 안 됩니다. 이 주제는 고유 식별자 권장사항 페이지에 자세히 설명되어 있습니다.
기기 내 로그에 쓸 때 주의하세요. Android에서 로그는 공유 리소스이며 READ_LOGS
권한이 있는 애플리케이션에서 사용할 수 있습니다. 전화 로그 데이터는 일시적이며 재부팅 시 삭제된다고 해도 사용자 정보의 부적절한 로깅으로 인해 사용자 데이터를 실수로 다른 애플리케이션에 유출할 수 있습니다. PII를 로깅하지 않는 것 외에도 프로덕션 앱에서 로그 사용을 제한해야 합니다. 이를 간단히 구현하려면 디버그 플래그와 맞춤 Log
클래스를 쉽게 구성할 수 있는 로깅 수준으로 사용하세요.
WebView
WebView
에서 사용하는 웹 콘텐츠는 HTML 및 JavaScript를 포함할 수 있으므로, 이를 부적절하게 사용할 경우 교차 사이트 스크립팅(JavaScript 삽입) 같은 일반적인 웹 보안 문제를 일으킬 수 있습니다. Android에는 애플리케이션에 요구되는 최소한의 기능으로 WebView
의 기능을 제한하여 이러한 문제가 발생할 가능성을 줄이는 다양한 메커니즘이 포함되어 있습니다.
애플리케이션이 WebView
내에서 직접 JavaScript를 사용하지 않는다면 setJavaScriptEnabled
를 호출하지 마세요. 일부 샘플 코드에서 이 메서드를 사용합니다. 프로덕션 애플리케이션에서 이 메서드를 사용하는 샘플 코드를 다른 용도로 변경하는 경우 필요하지 않다면 해당 메서드 호출을 삭제합니다. 기본적으로 WebView
는 JavaScript를 실행하지 않기 때문에 교차 사이트 스크립팅이 불가능합니다.
addJavaScriptInterface()
를 사용할 때는 특히 주의가 필요한데 그 이유는 일반적으로 Android 애플리케이션용으로 예약되는 작업을 JavaScript가 호출할 수 있도록 허용하기 때문입니다. addJavaScriptInterface()
를 사용하는 경우 모든 입력을 신뢰할 수 있는 웹페이지에만 노출하세요. 신뢰할 수 없는 입력이 허용되면 신뢰할 수 없는 JavaScript가 앱에서 Android 메서드를 호출할 수 있습니다. 일반적으로 애플리케이션 APK에 포함된 JavaScript에만 addJavaScriptInterface()
를 노출하는 것이 좋습니다.
애플리케이션에서 WebView
를 사용하여 민감한 정보에 액세스하는 경우 로컬에 저장된 모든 파일을 삭제하기 위해 clearCache()
메서드를 사용하는 것이 좋습니다. no-store
와 같은 서버 측 헤더를 사용하여 애플리케이션이 특정 콘텐츠를 캐시하면 안 된다는 것을 나타낼 수도 있습니다.
Android 4.4(API 수준 19) 이전의 플랫폼이 실행되는 기기에서는 여러 보안 문제가 있는 webkit
버전을 사용합니다. 문제를 해결하려면 이러한 기기에서 앱이 실행 중인 경우 WebView
객체가 신뢰할 수 있는 콘텐츠만 표시하는지 확인해야 합니다. 앱이 SSL의 잠재적 취약점에 노출되지 않도록 하려면 보안 프로바이더를 업데이트하여 SSL 악용으로부터 기기 보호에 설명된 대로 업데이트 가능한 보안 Provider
객체를 사용하세요. 애플리케이션에서 오픈 웹의 콘텐츠를 렌더링해야 하는 경우 최신 보안 패치로 최신 상태를 유지할 수 있도록 자체 렌더기를 제공해 보세요.
사용자 인증 정보 요청
사용자 인증 정보 요청은 공격을 위한 벡터입니다. 다음은 Android 앱에서 사용자 인증 정보 요청을 더욱 안전하게 하는 데 도움이 되는 몇 가지 도움말입니다.
사용자 인증 정보 노출 최소화
- 불필요한 사용자 인증 정보 요청을 피합니다. 피싱 공격을 알아보기 쉽게 하고 성공 가능성을 낮추려면 사용자 인증 정보를 요청하는 빈도를 최소화합니다. 그 대신 승인 토큰을 사용하고 새로고침하세요. 인증 및 승인에 필요한 최소한의 사용자 인증 정보만 요청합니다.
- 사용자 인증 정보를 안전하게 저장합니다. 인증 관리자를 사용하여 패스키를 사용하는 비밀번호 없는 인증을 사용 설정하거나 Google 계정으로 로그인과 같은 스키마로 제휴 로그인을 구현합니다. 기존 비밀번호 인증을 사용해야 한다면 기기에 사용자 ID와 비밀번호를 저장하지 마세요. 대신 사용자가 제공한 사용자 이름과 비밀번호를 사용하여 초기 인증을 시행한 다음 서비스별 단기 승인 토큰을 사용하세요.
- 권한 범위를 제한합니다. 더 좁은 범위만 필요한 작업에는 광범위한 권한을 요청하지 마세요.
- 액세스 토큰을 제한합니다. 단기 토큰 작업 및 API 호출을 사용합니다.
- 인증 비율을 제한합니다. 빠르고 연속적인 인증 또는 승인 요청은 무차별 대입 공격의 징후일 수 있습니다. 기능적이고 사용자 친화적인 앱 환경을 제공하면서도 이러한 비율을 합리적인 빈도로 제한합니다.
보안 인증 사용
- 패스키를 구현합니다. 더 안전하고 사용자 친화적인 비밀번호 업그레이드로 패스키를 사용 설정하세요.
- 생체 인식을 추가합니다. 보안을 강화하기 위해 지문 또는 얼굴 인식과 같은 생체 인식 인증 기능을 제공합니다.
- 제휴 ID 공급업체를 사용합니다. 인증 관리자는 Google 계정으로 로그인과 같은 제휴 인증 공급업체를 지원합니다.
- 통신 암호화. HTTPS 및 유사한 기술을 사용하여 앱에서 네트워크를 통해 전송하는 데이터를 보호합니다.
안전한 계정 관리 실천
AccountManager
를 사용하여 여러 애플리케이션이 액세스할 수 있는 서비스에 연결합니다.AccountManager
클래스를 사용하여 클라우드 기반 서비스를 호출하고 비밀번호를 기기에 저장하지 마세요.AccountManager
를 사용하여Account
를 검색한 후 사용자 인증 정보를 전달하기 전에CREATOR
를 사용하여 잘못된 애플리케이션에 실수로 사용자 인증 정보를 전달하지 않도록 하세요.- 자신이 만든 애플리케이션에서만 사용자 인증 정보를 사용하는 경우
checkSignatures
를 사용하여AccountManager
에 액세스하는 애플리케이션을 확인할 수 있습니다. 또는 하나의 애플리케이션에서만 사용자 인증 정보를 사용하는 경우KeyStore
를 사용하여 저장할 수도 있습니다.
항상 경계하기
- 코드를 최신 상태로 유지합니다. 최신 취약점으로부터 보호되도록 서드 파티 라이브러리 및 종속 항목을 비롯하여 소스 코드를 업데이트해야 합니다.
- 의심스러운 활동을 모니터링합니다. 승인 오용 패턴과 같은 잠재적인 오용을 찾습니다.
- 코드를 감사합니다. 코드베이스에 대한 정기적인 보안 검사를 실행하여 잠재적인 사용자 인증 정보 요청 문제를 찾습니다.
API 키 관리
API 키는 많은 Android 앱의 중요한 구성요소로, 이를 통해 외부 서비스에 액세스하고 필수 기능을 실행할 수 있습니다(예: 매핑 서비스 연결, 인증, 날시 서비스). 그러나 이러한 민감한 키가 노출되면 정보 유출, 무단 액세스, 재정적 손실 등 심각한 결과를 초래할 수 있습니다. 이러한 시나리오를 방지하기 위해 개발자는 개발 프로세스 전반에 걸쳐 API 키를 처리하는 보안 전략을 구현해야 합니다.
서비스의 오용을 방지하려면 API 키를 신중하게 보호해야 합니다. 앱과 API 키를 사용하는 서비스 간의 연결을 보호하려면 API에 대한 액세스를 보호해야 합니다. 앱이 컴파일되고 앱의 소스 코드에 API 키가 포함되어 있으면 공격자가 앱을 디컴파일하여 이러한 리소스를 찾을 수 있습니다.
이 섹션은 지속적 배포 파이프라인에 관해 인프라팀과 협력하는 Android 개발자와 Play 스토어에서 독립형 앱을 배포하는 Android 개발자, 이렇게 두 그룹의 Android 개발자를 대상으로 합니다. 이 섹션에서는 앱이 서비스와 안전하게 통신할 수 있도록 API 키를 처리하는 방법에 관한 권장사항을 간략히 설명합니다.
생성 및 저장소
개발자는 심층 방어 접근 방식을 사용하여 API 키 저장소를 데이터 보호 및 사용자 개인 정보 보호의 중요한 구성요소로 취급해야 합니다.
강력한 키 저장소
최적의 키 관리 보안을 위해 Android 키 저장소를 사용하고 저장된 키를 security-crypto Jetpack 라이브러리나 Tink Java와 같은 강력한 도구를 사용하여 암호화하세요.
다음 예에서는 Jetpack security-crypto 라이브러리를 사용하여 암호화된 공유 환경설정을 만듭니다.
Kotlin
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
val encryptedSharedPreferences = EncryptedSharedPreferences.create(
context,
"secret_shared_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
// use the shared preferences and editor as you normally would
encryptedSharedPreferences.edit()
Java
MasterKey masterKey = new MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build();
SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
context,
"secret_shared_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
// use the shared preferences and editor as you normally would
SharedPreferences.Editor editor = sharedPreferences.edit();
소스 제어 제외
소스 코드 저장소에 API 키를 커밋하지 마세요. API 키를 소스 코드에 추가하면 키가 공개 저장소, 공유 코드 예시, 실수로 공유된 파일에 노출될 위험이 있습니다. 대신 secrets-gradle-plugin과 같은 Gradle 플러그인을 사용하여 프로젝트에서 API 키를 사용하세요.
환경별 키
가능하면 별도의 API 키 개발, 테스트, 프로덕션 환경을 사용하세요. 환경별 키를 사용하여 각 환경을 격리하면 프로덕션 데이터가 노출될 위험을 줄이고 프로덕션 환경에 영향을 주지 않고 손상된 키를 사용 중지할 수 있습니다.
사용 및 액세스 제어
안전한 API 키 관행은 API와 사용자를 보호하는 데 필수적입니다. 최적의 보안을 위해 키를 준비하는 방법은 다음과 같습니다.
- 앱마다 고유 키 생성: 앱마다 별도의 API 키를 사용하여 손상된 액세스를 식별하고 격리할 수 있습니다.
- IP 제한사항 구현: 가능하면 API 키 사용을 특정 IP 주소나 범위로 제한합니다.
- 모바일 앱 키 사용 제한: 키와 함께 번들로 묶거나 앱 인증서를 사용하여 특정 모바일 앱으로 API 키 사용을 제한합니다.
- 의심스러운 활동 로깅 및 모니터링: API 사용 로깅 및 모니터링 메커니즘을 구현하여 의심스러운 활동을 감지하고 잠재적 악용을 방지합니다.
참고: 서비스에서는 키를 특정 패키지나 플랫폼으로 제한하는 기능을 제공해야 합니다. 예를 들어 Google 지도 API는 패키지 이름과 서명 키를 기준으로 키 액세스를 제한합니다.
OAuth 2.0은 리소스에 대한 액세스를 승인하기 위한 프레임워크를 제공합니다. 클라이언트와 서버의 상호작용 방식에 관한 표준을 정의하며 안전한 승인을 허용합니다. OAuth 2.0을 사용하여 API 키 사용을 특정 클라이언트로 제한하고, 액세스 범위를 정의하여 각 API 키에 의도한 목적에 필요한 최소 수준의 액세스만 있도록 할 수 있습니다.
키 순환 및 만료
발견되지 않은 API 취약점으로 인한 무단 액세스의 위험을 줄이려면 API 키를 정기적으로 순환하는 것이 중요합니다. ISO 27001 표준에서는 키 순환 실행 빈도에 관한 규정 준수 프레임워크를 정의합니다. 대부분의 경우 90일에서 6개월 사이의 키 순환 기간이 적절합니다. 강력한 키 관리 시스템을 구현하면 이러한 프로세스를 간소화하여 키 순환 및 만료 요구사항의 효율성을 개선할 수 있습니다.
일반 권장사항
- SSL/HTTPS 사용: 항상 HTTPS 통신을 사용하여 API 요청을 암호화합니다.
- 인증서 고정: 보안 강화를 위해 인증서 고정을 구현하여 유효한 것으로 간주되는 인증서를 확인할 수 있습니다.
- 사용자 입력 검증 및 정리: 사용자 입력을 검증하고 정리하여 API 키를 노출할 수 있는 인젝션 공격을 방지합니다.
- 보안 권장사항 준수: 개발 프로세스에서 보안 코딩 기법, 코드 검토, 취약점 스캔 등 일반적인 보안 권장사항을 구현합니다.
- 최신 정보 확인: API 키 관리에 관한 최신 보안 위협 및 권장사항에 관한 정보를 받습니다.
- SDK를 최신 상태로 업데이트: SDK와 라이브러리를 최신 버전으로 업데이트합니다.
암호화
Android에서는 데이터 격리 제공, 전체 파일 시스템 암호화 지원, 안전한 통신 채널 제공 외에도 암호화를 사용하여 데이터를 보호하기 위한 다양한 알고리즘을 제공합니다.
소프트웨어에서 어떤 Java 암호화 아키텍처(JCA) 보안 제공업체를 사용하는지 알아야 합니다. 사용 사례를 지원할 수 있는 기존 프레임워크 구현 중 최고 수준을 사용해 보세요. 가능한 경우 Google 지원 제공업체를 Google에서 지정한 순서대로 사용하세요.
알려진 네트워크 위치에서 파일을 안전하게 검색해야 하는 경우 간단한 HTTPS URI로 충분하며 암호화 지식은 필요하지 않습니다. 보안 터널이 필요한 경우 자체 프로토콜을 작성하는 것보다 HttpsURLConnection
또는 SSLSocket
을 사용하는 것이 좋습니다. SSLSocket
을 사용하는 경우 호스트 이름 확인을 실행하지 않는다는 점에 유의하세요. SSLSocket
직접 사용에 관한 경고를 참고하세요.
자체 프로토콜을 구현해야 하는 경우 암호화 알고리즘을 직접 구현해서는 안 됩니다. Cipher
클래스에 제공되는 AES 및 RSA 구현과 같은 기존 암호화 알고리즘을 사용하세요.
또한 다음 권장사항을 따르세요.
- 상업적 목적에는 256비트 AES를 사용합니다. 사용할 수 없는 경우 128비트 AES를 사용하세요.
- 타원 곡선(EC) 암호화에는 224비트 또는 256비트 공개 키 크기를 사용합니다.
- CBC, CTR 또는 GCM 차단 모드를 언제 사용해야 하는지 파악합니다.
- CTR 모드에서 IV/카운터를 재사용하면 안 됩니다. 암호가 무작위로 생성되는지 확인해야 합니다.
- 암호화를 사용할 때는 다음 함수 중 하나로 CBC 또는 CTR 모드를 사용하여 무결성을 구현합니다.
- HMAC-SHA1
- HMAC-SHA-256
- HMAC-SHA-512
- GCM 모드
안전한 랜덤 숫자 생성기인 SecureRandom
을 사용하여 KeyGenerator
에서 생성된 암호화 키를 초기화하세요. 안전한 랜덤 숫자 생성기로 생성되지 않은 키를 사용하면 알고리즘의 강도가 크게 약해지고 오프라인 공격을 허용할 수 있습니다.
반복적으로 사용하기 위해 키를 저장해야 하는 경우 KeyStore
와 같이 암호화 키의 장기간 보관 및 검색이 가능한 메커니즘을 사용하세요.
프로세스 간 통신
일부 앱에서는 네트워크 소켓 및 공유 파일과 같은 기존 Linux 기술을 사용하여 IPC를 구현하려고 합니다. 하지만 Service
의 Intent
, Binder
또는 Messenger
그리고 BroadcastReceiver
같은 IPC용 Android 시스템 기능을 대신 사용하는 것이 좋습니다. Android IPC 메커니즘을 사용하면 IPC에 연결되는 애플리케이션의 ID를 확인하고 각 IPC 메커니즘에 보안 정책을 설정할 수 있습니다.
보안 요소의 상당수는 IPC 메커니즘 간에 공유됩니다. IPC 메커니즘이 다른 애플리케이션에서 사용되지 않아야 하는 경우 구성요소의 매니페스트 요소(예: <service>
요소)에서 android:exported
속성을 false
로 설정합니다. 이는 동일한 UID 내에 여러 프로세스로 구성된 애플리케이션을 사용하려는 경우 또는 개발 막바지에 실제로 이 기능을 IPC로 노출하지 않기로 결정했으나 코드를 재작성하지 않으려는 경우에 유용합니다.
다른 애플리케이션에서 IPC에 액세스할 수 있는 경우 <permission>
요소를 사용하여 보안 정책을 적용할 수 있습니다. IPC가 개발자 자체 앱과 동일한 키로 서명된 앱 사이에 일어난다면 android:protectionLevel
에서 signature-level
권한을 사용합니다.
인텐트
활동 및 broadcast receiver의 경우 인텐트는 Android의 비동기 IPC에 선호되는 메커니즘입니다. 애플리케이션 요구사항에 따라 특정 애플리케이션 구성요소에 sendBroadcast
, sendOrderedBroadcast
또는 명시적 인텐트를 사용할 수 있습니다. 보안을 위해 명시적 인텐트가 선호됩니다.
주의: 인텐트를 사용하여 **Service**
에 바인딩하는 경우 명시적 인텐트를 사용하여 앱의 보안을 유지합니다. 암시적 인텐트를 사용하여 서비스를 시작하면 어떤 서비스가 인텐트에 응답할지 확신할 수 없고 어떤 서비스가 시작하는지 사용자가 알 수 없으므로 보안 위험이 있습니다. Android 5.0(API 수준 21)부터는 암시적 인텐트로 **bindService()**
를 호출하면 시스템에서 예외가 발생합니다.
순서가 정해진 브로드캐스트는 수신자가 소비할 수 있으므로 일부 애플리케이션에 전달되지 않을 수 있습니다. 특정 수신자에게 전달되어야 하는 인텐트를 전송하는 경우 이름을 기준으로 수신자를 선언하는 명시적 인텐트를 사용해야 합니다.
인텐트 전송자는 메서드 호출로 null이 아닌 권한을 지정하여 수신자가 권한을 보유하고 있음을 확인할 수 있습니다. 이 권한이 있는 애플리케이션만 인텐트를 수신합니다. 브로드캐스트 인텐트 내의 데이터가 민감한 정보인 경우 악성 애플리케이션이 적절한 권한 없이 이 메시지를 수신하도록 등록하지 못하게 보장하는 권한 적용을 고려해야 합니다. 이러한 상황에서는 브로드캐스트를 제기하는 대신 수신자를 직접 호출해 보는 것도 좋습니다.
참고: 인텐트 필터는 보안 기능이 아닙니다. 구성요소는 명시적 인텐트로 호출될 수 있으며, 구성요소에 인텐트 필터를 준수하는 데이터가 없을 수 있습니다. 호출된 수신자, 서비스 또는 활동에 맞춰 적절하게 형식이 지정되어 있는지 확인하려면 인텐트 수신자에서 입력 검증을 실행하세요.
서비스
Service
는 다른 애플리케이션에서 사용할 기능을 제공하는 데 주로 사용됩니다. 각 서비스 클래스에는 관련 <service>
선언이 매니페스트 파일에 있어야 합니다.
기본적으로 서비스는 내보내지지 않으며 다른 애플리케이션에 의해 호출될 수 없습니다. 하지만 서비스 선언에 인텐트 필터를 추가하는 경우 기본적으로 내보내집니다. 원하는 대로 작동하게 하려면 android:exported
속성을 명시적으로 선언하는 것이 가장 좋습니다. 또한 android:permission
속성을 사용하여 서비스를 보호할 수 있습니다. 이 속성을 사용하면 다른 애플리케이션에서 관련 <uses-permission>
요소를 자체 매니페스트에 선언해야 서비스를 시작하거나 서비스를 중지하거나 서비스에 결합할 수 있습니다.
참고: 앱에서 Android 5.0(API 수준 21) 이상을 타겟팅하는 경우 **JobScheduler**
를 사용하여 백그라운드 서비스를 실행해야 합니다.
서비스는 실행된 개별 IPC 호출을 권한을 사용하여 보호할 수 있습니다. 이는 호출 구현을 실행하기 전에 checkCallingPermission()
을 호출하여 이루어집니다. Google은 실수를 줄이기 위하여 매니페스트에 선언적 권한을 사용할 것을 권장합니다.
주의: 클라이언트와 서버 권한을 혼동하면 안 됩니다. 호출된 앱에 적절한 권한이 있고 호출하는 앱에 동일한 권한을 부여했는지 확인하세요.
바인더 및 메신저 인터페이스
Binder
또는 Messenger
는 Android의 RPC 스타일 IPC에 선호되는 메커니즘입니다. 이러한 메커니즘은 필요한 경우 엔드포인트 간의 상호 인증을 가능하게 하는 잘 정의된 인터페이스를 제공합니다.
인터페이스별 권한 확인이 필요 없는 방식으로 앱 인터페이스를 설계하는 것이 좋습니다. Binder
및 Messenger
객체는 애플리케이션 매니페스트에서 선언되지 않기 때문에 이러한 객체에 선언적 권한을 직접 적용할 수 없습니다. 일반적으로 이러한 객체는 권한이 구현된 Service
또는 Activity
의 애플리케이션 매니페스트에 선언된 권한을 상속합니다. 인증이나 액세스 제어가 필요한 인터페이스를 만드는 경우 Binder
또는 Messenger
인터페이스에 명시적으로 이러한 제어를 코드로 추가해야 합니다.
액세스 제어가 필요한 인터페이스를 제공하는 경우 checkCallingPermission()
을 사용하여 호출자에게 필요한 권한이 있는지 확인합니다. 애플리케이션의 ID가 다른 인터페이스에 전달되므로 호출자를 대신하여 서비스에 액세스하기 전에 이 과정이 특히 중요합니다.
Service
에서 제공한 인터페이스를 호출하는 경우 주어진 서비스에 액세스할 권한이 없다면 bindService()
를 호출하지 못할 수 있습니다. 앱과 상호작용하도록 외부 프로세스에 허용해야 하지만 외부 프로세스에 필요한 권한이 없는 경우 clearCallingIdentity()
메서드를 사용할 수 있습니다. 이 메서드는 마치 앱이 외부 호출자가 아니라 자체 호출을 하는 것처럼 앱 인터페이스를 호출합니다. 나중에 개발자는 restoreCallingIdentity()
메서드를 사용하여 호출자 권한을 복원할 수 있습니다.
서비스로 IPC를 실행하는 방법에 관한 자세한 내용은 바인드된 서비스를 참고하세요.
broadcast receiver
BroadcastReceiver
는 Intent
에 의해 시작된 비동기식 요청을 처리합니다.
기본적으로, 수신자는 내보내지며 다른 애플리케이션에 의해 호출될 수 있습니다.
다른 애플리케이션에서 BroadcastReceiver
를 사용하도록 하려는 경우 애플리케이션 매니페스트 내에 <receiver>
요소를 사용하여 수신자에 보안 권한을 적용하는 것이 좋습니다. 이렇게 하면 적절한 권한이 없는 애플리케이션이 BroadcastReceiver
에 인텐트를 보낼 수 없습니다.
동적으로 로드되는 코드를 사용한 보안
애플리케이션 APK 밖에서는 코드를 로드하지 않는 것이 좋습니다. 외부에서 코드를 로드하면 코드 삽입이나 코드 조작으로 인해 애플리케이션이 손상될 가능성이 크게 높아집니다. 또한 버전 관리 및 애플리케이션 테스트가 더 복잡해지고 애플리케이션 동작 확인을 불가능하게 만들 수 있으므로 일부 환경에서는 금지될 수 있습니다.
애플리케이션에서 코드를 동적으로 로드하는 경우 명심할 가장 중요한 점은, 동적으로 로드되는 코드는 애플리케이션 APK와 동일한 보안 권한을 가지고 실행된다는 점입니다. 사용자는 개발자의 ID에 근거하여 애플리케이션 설치를 결정하며, 동적으로 로드되는 코드를 비롯하여 애플리케이션에서 실행되는 모든 코드를 개발자가 제공하리라 기대합니다.
많은 애플리케이션에서는 암호화되지 않은 프로토콜을 통한 네트워크나 외부 저장소와 같이 누구나 쓸 수 있는 위치에서 다운로드된 코드 등 안전하지 않은 위치에서 코드를 로드하려고 시도합니다. 이러한 위치에서는 네트워크 내 사용자가 전송 중인 콘텐츠를 수정하거나 사용자 기기의 다른 애플리케이션이 기기 콘텐츠를 수정할 수 있습니다. 반면 APK에 직접 포함된 모듈은 다른 애플리케이션에서 수정할 수 없습니다. 코드가 네이티브 라이브러리이든 DexClassLoader
를 사용하여 로드 중인 클래스이든 마찬가지입니다.
가상 머신의 보안
Dalvik은 Android의 런타임 가상 머신(VM)입니다. Dalvik은 특별히 Android용으로 제작되었지만 다른 가상 머신의 보안 코드와 관련한 우려사항이 Android에도 상당 부분 적용됩니다. 일반적으로는 가상 머신과 관련된 보안 문제는 신경 쓰지 않아도 됩니다. 애플리케이션은 안전한 샌드박스 환경에서 실행되므로 시스템의 다른 프로세스에서 코드나 비공개 데이터에 액세스할 수 없습니다.
가상 머신 보안에 관해 자세히 알아보려면 이 주제에 관한 기존의 몇 가지 자료를 확인하세요. 다음은 가장 인기 있는 두 가지 리소스입니다.
이 문서에서는 Android와 관련이 있거나 기타 VM 환경과는 다른 영역에 관해 주로 다룹니다. 다른 환경에서 VM 프로그래밍을 경험한 개발자의 경우 Android용 앱 작성과 관련하여 크게 두 가지 문제가 있을 수 있습니다.
- JVM이나 .NET 런타임과 같은 일부 가상 머신은 코드를 기본 운영체제 기능과 분리하는 보안 경계 역할을 합니다. Android의 경우 Dalvik VM은 보안 경계가 아닙니다. 애플리케이션 샌드박스가 OS 수준에서 구현되기 때문에 Dalvik은 보안 제약 없이 동일한 애플리케이션의 네이티브 코드와 상호 운용될 수 있습니다.
- 휴대기기의 제한된 저장용량을 고려할 때 개발자는 일반적으로 모듈식 애플리케이션을 빌드하고 동적 클래스 로드를 사용하고자 합니다. 이렇게 하는 경우 애플리케이션 로직을 검색하는 소스와 이를 로컬로 저장하는 위치를 모두 고려하시기 바랍니다. 안전하지 않은 네트워크 소스나 외부 저장소와 같이 확인되지 않은 소스에서는 동적 클래스를 로드하면 안 됩니다. 악의적인 동작을 포함하도록 코드가 수정될 수 있기 때문입니다.
네이티브 코드의 보안
Google은 애플리케이션 개발 시 Android NDK의 네이티브 코드를 사용하기보다 Android SDK를 사용할 것을 권장합니다. 기본 코드로 작성한 애플리케이션은 더 복잡하고 이식성이 떨어지며 버퍼 오버플로우와 같은 일반적인 메모리 손상 오류가 발생하기 쉽습니다.
Android는 Linux 커널을 사용해 빌드되었으며, Linux 개발 보안 권장사항에 익숙하면 네이티브 코드를 사용하는 경우 특히 유용합니다. Linux 보안 권장사항은 이 문서의 범위를 벗어나는 것이지만 널리 사용되는 리소스 중 하나로 안전한 프로그래밍 방법 - 안전한 소프트웨어 만들기가 있습니다.
Android와 대부분의 Linux 환경 간의 중요한 차이점은 애플리케이션 샌드박스입니다. Android에서는 네이티브 코드로 작성된 애플리케이션을 비롯한 모든 애플리케이션이 샌드박스에서 실행됩니다. Linux에 익숙한 개발자는 모든 애플리케이션에 매우 제한된 권한이 있는 고유 UID가 부여된다는 사실을 기억하면 됩니다. 이 내용은 Android 보안 개요에서 자세히 다루며 개발자라면 네이티브 코드를 사용하는 경우에도 애플리케이션 권한에 익숙해져야 합니다.