lightbulb_outline Please take our October 2018 developer survey. Start survey

 보안 팁

Android는 보안 기능을 운영 체제에 내장하고 있어서, 애플리케이션 보안 문제의 영향과 발생 빈도를 상당히 줄여줍니다. 이 운영 체제는 여러분이 기본 시스템 및 파일 권한으로 자신의 앱을 빌드하고 보안에 관련된 어려운 결정을 피할 수 있도록 설계되었습니다.

안전한 앱을 빌드하도록 도와주는 핵심 보안 기능은 다음과 같습니다.

  • Android 애플리케이션 샌드박스 - 앱 데이터와 코드 실행을 다른 앱으로부터 격리합니다.
  • 암호화, 권한 및 보안 IPC와 같은 일반적인 보안 기능을 강력하게 구현한 애플리케이션 프레임워크.
  • 일반적인 메모리 관리 오류에 관련된 위험을 줄여주는 ASLR, NX, ProPolice, safe_iop, OpenBSD dlmalloc, OpenBSD calloc, Linux mmap_min_addr 등과 같은 기술.
  • 분실하거나 도난당한 기기의 데이터를 보호하기 위해 활성화되는 암호화된 파일 시스템.
  • 시스템 기능 및 사용자 데이터에 대한 액세스를 제한하기 위해 사용자가 부여하는 권한.
  • 앱별로 애플리케이션 데이터를 제어하기 위해 애플리케이션에서 정의하는 권한.

그럼에도 불구하고, 여러분은 이 문서의 Android 보안 모범 사례에 친숙해지는 것이 중요합니다. 이러한 모범 사례를 일반적인 코딩 습관으로 사용하면, 여러분의 사용자에게 악영향을 미치는 보안 문제가 실수로 발생할 가능성을 줄일 수 있습니다.

데이터 저장

Android상의 애플리케이션에서 가장 일반적인 보안 문제는 여러분이 기기에 저장한 데이터를 다른 앱에서 액세스할 수 있는지 여부입니다. 기기에 데이터를 저장하는 기본적인 세 가지 방법은 다음과 같습니다.

내부 저장소 사용

기본적으로, 여러분이 내부 저장소에 만드는 파일은 여러분의 앱에서만 액세스할 수 있습니다. 이러한 보호 기능은 Android에 의해 구현되며 대부분의 애플리케이션에서 충분한 성능을 발휘합니다.

일반적으로 여러분은 IPC 파일에 대해 MODE_WORLD_WRITEABLE 또는 MODE_WORLD_READABLE 모드의 사용을 피해야 하는데, 그 이유로 이들 모드는 특정 애플리케이션에 대해 데이터 액세스를 제한하는 기능을 제공하지 않으며 또한 데이터 형식을 전혀 제어하지 않기 때문입니다. 여러분의 데이터를 다른 앱 프로세스와 공유하려면, 그 대신 콘텐츠 제공자 사용을 고려할 수도 있습니다. 이 제공자는 다른 앱에 대한 읽기 및 쓰기 권한을 제공하며 사례별로 동적 권한 부여가 가능합니다.

중요한 데이터를 추가적으로 보호하려면, 애플리케이션에 직접 액세스가 불가능한 키를 사용하여 로컬 파일을 암호화하는 방법을 선택할 수도 있습니다. 예를 들어, 키를 KeyStore에 보관할 수 있으며, 기기에 저장되지 않은 사용자 암호로 이 키를 보호할 수 있습니다. 이 방법은 사용자의 암호 입력을 엿볼 수 있는 루트 손상으로부터 데이터를 보호하지는 못하지만, 파일 시스템 암호화가 없어도 분실한 기기의 데이터를 보호할 수는 있습니다.

외부 저장소 사용

SD 카드와 같은 외부 저장소에 만들어진 파일은 전역적으로 읽기와 쓰기가 가능합니다. 외부 저장소는 사용자에 의해 제거가 가능하고 임의 애플리케이션에 의해 수정도 가능하기 때문에, 중요한 정보를 외부 저장소에 저장해서는 안 됩니다.

신뢰할 수 없는 소스의 데이터와 마찬가지로, 외부 저장소의 데이터를 처리할 때도 입력 유효성 검사를 수행해야 합니다. 실행 파일이나 클래스 파일을 동적 로드하기 전에 외부 저장소에 저장하는 것은 전혀 바람직하지 않습니다. 여러분의 앱이 실행 파일을 외부 저장소에서 검색하는 경우, 이들 파일을 동적 로드하기 전에 서명을 하고 암호로 확인해야 합니다.

콘텐츠 제공자 사용

콘텐츠 제공자는 여러분 자신의 애플리케이션으로 제한될 수 있는(또는 다른 애플리케이션에 의한 액세스를 허용하기 위해 내보낼 수 있는) 구조적 저장소 메커니즘을 제공합니다. 여러분의 ContentProvider에 대한 액세스를 다른 애플리케이션에 제공하지 싶지 않으면, 애플리케이션 매니페스트에서 이 애플리케이션을 android:exported=false로 표시합니다. 그렇지 않으면, android:exported 특성을 "true"로 설정하여, 저장된 데이터에 다른 앱이 액세스하도록 허용합니다.

다른 애플리케이션이 사용하도록 내보내기 위해 ContentProvider를 만드는 경우, 읽기 및 쓰기를 위한 단일 권한을 지정할 수 있으며 또는 읽기 및 쓰기를 위한 별도의 권한을 매니페스트 내에 지정할 수 있습니다. 해당 작업을 수행하는 데 필요한 것으로만 여러분의 권한을 제한하는 것이 좋습니다. 명심할 점은, 권한을 빼앗고 기존 사용자를 차단하는 것보다는 새 기능을 노출시키기 위해 나중에 권한을 추가하는 것이 일반적으로 더 쉽다는 점입니다.

여러분 자신의 앱들 사이에만 데이터를 공유하기 위해 콘텐츠 제공자를 사용하는 경우에는, android:protectionLevel 특성을 "signature" 보호로 설정하여 사용하는 것이 바람직합니다. 서명 권한에는 사용자 확인이 필요치 않으므로, 데이터에 액세스하는 앱들을 동일 키로 서명하면, 사용자 환경이 더 향상되고 콘텐츠 제공자에 대한 액세스 제어가 더욱 향상됩니다.

콘텐츠 제공자는 또한 더욱 정교한 액세스를 제공하기 위해 android:grantUriPermissions 특성을 선언하고, Intent 객체에서 구성 요소를 활성화하는 FLAG_GRANT_READ_URI_PERMISSIONFLAG_GRANT_WRITE_URI_PERMISSION 플래그를 사용합니다. 이러한 권한의 범위는 <grant-uri-permission element>에 의해 더욱 제한될 수 있습니다.

콘텐츠 제공자에 액세스할 때 query(), update()delete()와 같은 매개변수화된 쿼리 메서드를 사용하면, 신뢰할 수 없는 소스로부터 오는 잠재적 SQL 주입을 피할 수 있습니다. 참고로, 메서드에 제출하기 전에 사용자 데이터를 연결하여 selection 인수를 작성한 경우에는, 매개변수화된 메서드를 사용하는 것으로 충분치 않습니다.

쓰기 권한에 관해서는 절대로 마음을 놓지 마십시오. 다음과 같은 경우를 생각해 보겠습니다. 즉, 쓰기 권한에서 창의적인 WHERE 절을 사용하고 그 결과를 분석하여 어떤 데이터를 확인할 수 있도록 해주는 SQL 문을 허용하는 경우를 생각해 보겠습니다. 예를 들어, 어떤 전화번호가 이미 존재하는 경우 공격자가 한 행만 수정하게 되면, 이 전화번호가 통화 기록에 존재하는지 여부를 알아낼 수도 있습니다. 콘텐츠 제공자 데이터에 예측 가능한 구조가 있는 경우, 쓰기 권한은 읽기 및 쓰기 권한이 둘 다 있는 것과 동일할 수 있습니다.

권한 사용

Android는 여러 애플리케이션을 샌드박스로 서로 격리하므로, 애플리케이션은 리소스와 데이터를 명시적으로 공유해야 합니다. 이를 위해 애플리케이션은 기본 샌드박스가 제공하지 않는 추가적인 기능(예: 카메라와 같은 기기 기능에 액세스)에 대해서는 필요한 권한을 선언합니다.

권한 요청

여러분의 앱이 요청하는 권한의 수를 최소한으로 줄이는 것이 좋습니다. 중요한 권한에 대한 액세스를 제공하지 않으면, 이러한 권한을 실수로 오용할 위험이 줄어들고, 사용자의 채택율을 개선할 수 있고, 공격에 대한 앱의 취약성을 줄일 수 있습니다. 일반적으로, 여러분의 앱이 작동하는 데 권한이 필요 없다면 권한을 요청하지 마십시오.

어떠한 권한도 필요 없는 방식으로 애플리케이션을 설계하는 것이 가능하며, 이것이 바람직합니다. 예를 들어, 고유 식별자를 만들기 위해 기기 정보에 대한 액세스를 요청하는 대신, 여러분의 애플리케이션에 대해 GUID를 만드세요(사용자 데이터 처리 섹션 참조). 또는 (권한이 필요한) 외부 저장소를 사용하는 대신, 내부 저장소에 데이터를 저장하세요.

권한 요청 외에도, 여러분의 애플리케이션은 보안에 민감하고 다른 애플리케이션(예: ContentProvider)에 노출되는 IPC를 보호하기 위해 <permissions>를 사용할 수 있습니다. 일반적으로, 권한 때문에 사용자가 혼란을 느낄 수 있으므로, 가능하면 사용자가 확인한 권한을 사용하는 것보다 액세스 제어를 사용하는 것이 좋습니다. 예를 들어, 단일 개발자가 제공하는 여러 애플리케이션 간의 IPC 통신을 위한 권한에는 서명 보호 레벨의 사용을 고려하세요.

권한 보호된 데이터는 유출시키지 마십시오. 이러한 경우는 여러분의 앱이 데이터에 액세스할 권한이 있을 때만 사용할 수 있는 데이터를 이 앱이 IPC상에 노출시키는 경우에 발생합니다. 이 앱의 IPC 인터페이스의 클라이언트는 이와 동일한 데이터 액세스 권한이 없을 수도 있습니다. 이 문제의 잠재적 영향과 발생 빈도에 대한 자세한 내용은 USENIX에 게시된 이 연구 논문에 나타나 있습니다.

권한 생성

일반적으로, 여러분은 자신의 보안 요구사항을 충족시키는 선에서 최대한 적은 권한을 정의하려고 애써야 합니다. 대부분의 애플리케이션에서 권한을 새로 생성하는 것은 비교적 드문 일입니다. 왜냐하면 시스템 정의 권한에는 많은 상황들이 포함되기 때문입니다. 적절한 경우, 기존 권한을 사용하여 액세스 검사를 수행합니다.

새 권한을 생성해야 하는 경우, "서명" 보호 레벨로 여러분의 작업을 수행할 수 있는지 여부를 고려하세요. 서명 권한은 사용자에게 투명합니다. 서명 권한은 또한 권한 검사를 수행하는 애플리케이션과 동일한 개발자에 의해 서명된 애플리케이션에만 액세스를 허용합니다.

"위험한" 보호 레벨로 권한을 생성하는 경우에는 다음과 같은 여러 가지 사항을 고려해야 합니다.

  • 사용자가 내려야 하는 보안 결정을 간결하게 사용자에게 표현해주는 문자열이 권한에 있어야 합니다.
  • 이 권한 문자열은 여러 가지 다른 언어로 현지화되어야 합니다.
  • 권한이 혼란스럽거나 위험한 것으로 인식되는 경우, 사용자가 애플리케이션을 설치하지 않을 수도 있습니다.
  • 권한 생성자가 설치되지 않은 경우 애플리케이션이 권한을 요청할 수도 있습니다.

이와 같은 각 사항은 개발자인 여러분에게 상당한 비기술적 부담을 줄 수 있으며 또한 사용자에게 혼란을 줄 수도 있습니다. 이 때문에 저희가 "위험한" 권한 레벨의 사용을 권장하지 않는 것입니다.

네트워킹 사용

네트워크 트랜잭션에서는 사용자의 잠재적인 개인 데이터를 전송하므로, 근본적으로 보안에 위험이 따릅니다. 점점 더 많은 사람들이 모바일 기기(특히 기기가 네트워크 트랜잭션을 수행하는 경우)에서 개인정보 보호의 문제를 인식하고 있습니다. 따라서 사용자의 데이터를 항상 안전하게 지켜주는 최선의 모범 사례를 여러분의 앱에 구현하는 것이 매우 중요합니다.

IP 네트워킹 사용

Android상의 네트워킹은 다른 Linux 환경과 크게 다르지 않습니다. 핵심적인 고려사항은 중요한 데이터에 적절한 프로토콜이 사용되도록 보장하는 것입니다(예: 보안 웹 트래픽의 경우 HttpsURLConnection). 저희는 서버에서 HTTPS가 지원되는 모든 곳에서 HTTP보다 HTTPS 사용을 선호합니다. 왜냐하면 모바일 기기는 공용 Wi-Fi 핫스팟과 같은 안전하지 않은 네트워크에 자주 연결되기 때문입니다.

인증되고 암호화된 소켓 레벨 통신은 SSLSocket 클래스를 사용하여 쉽게 구현이 가능합니다. Wi-Fi를 사용하는 비보안 무선 네트워크에 Android 기기가 연결되는 빈도를 감안할 때, 이 네트워크를 통해 통신하는 모든 애플리케이션에 보안 네트워킹을 사용하도록 적극 권장합니다.

일부 애플리케이션은 중요한 IPC를 처리할 때 localhost 네트워크 포트를 사용하기도 합니다. 그러나 이 인터페이스에는 기기상의 다른 애플리케이션이 액세스할 수 있기 때문에 이 접근방식은 권장되지 않습니다. 그 대신, Service와 같은 방식으로 인증이 가능한 Android IPC 메커니즘을 사용해야 합니다. (루프백을 사용하는 것보다 더 안 좋은 방법은 INADDR_ANY에 바인딩하는 것이며, 이 경우 여러분의 애플리케이션은 모든 곳으로부터 요청을 수신할 수도 있습니다.)

또한 반복을 보증하는 한 가지 일반적인 사항은 HTTP 또는 기타 비보안 프로토콜로부터 다운로드된 데이터를 신뢰하지 않도록 보장하는 것입니다. 여기에는 WebView의 입력에 대한 유효성 검사와 HTTP에 대해 발급된 인텐트의 모든 응답에 대한 유효성 검사가 포함됩니다.

전화통신 네트워킹 사용

SMS 프로토콜은 사용자 간의 통신을 위해 주로 설계되었으며 데이터를 전송하려는 앱에는 잘 맞지 않습니다. SMS의 이러한 제한으로 인해, 데이터 메시지를 웹 서버에서 사용자 기기상의 앱으로 전송하기 위해서는 GCM(Google Cloud Messaging) 및 IP 네트워킹을 사용하도록 적극 권장합니다.

SMS는 암호화되지도 않으며, 네트워크나 기기에서 강력한 인증이 되지 않음에 유의하세요. 특히 모든 SMS 리시버는 악성 사용자가 SMS를 여러분의 애플리케이션에 보냈을 수 있다고 예상해야 합니다. 따라서 인증되지 않은 SMS 데이터에 의존하여 중요한 명령을 수행해서는 안 됩니다. 또한 네트워크상에서 SMS가 스푸핑 및/또는 도청에 노출될 수 있음도 유의해야 합니다. Android 구동 기기에서는 SMS 메시지가 브로드캐스트 인텐트로서 전송되므로, READ_SMS 권한이 있는 다른 애플리케이션에 의해 메시지가 읽히거나 캡처될 수도 있습니다.

입력 유효성 검사 수행

어떤 플랫폼에서 실행되는지에 상관없이 애플리케이션에 영향을 미치는 가장 일반적인 보안 문제 중 하나가 바로 불충분한 입력 유효성 검사입니다. 입력 유효성 검사 문제에 애플리케이션이 노출될 가능성을 줄여주는 플랫폼 수준의 대책을 Android는 보유하고 있으므로, 이러한 기능을 최대한 사용해야 합니다. 또한 형식 변환으로부터 안전한(type-safe) 언어를 선택하면, 입력 유효성 검사 문제의 발생 가능성이 감소하는 경향이 있습니다.

네이티브 코드를 사용 중인 경우, 파일로부터 읽어들이는 데이터, 네트워크를 통해 수신되는 데이터 또는 IPC로부터 수신되는 데이터는 보안 문제를 일으킬 가능성이 있습니다. 가장 일반적인 문제는 버퍼 오버플로, UAF(Use-After-Free)OBO(Off-By-One) 오류입니다. Android에서는 이러한 오류의 악용을 막기 위해 ASLRDEP와 같은 여러 가지 기술을 제공하지만, 근본적인 문제는 해결하지 못합니다. 이러한 취약성을 예방하기 위해서는 여러분이 포인터를 세심하게 다루고 버퍼를 관리해야 합니다.

또한 동적인 문자열 기반 언어(예: JavaScript 및 SQL)도 이스케이프 문자 및 스크립트 주입으로 인해 입력 유효성 검사 문제에 노출됩니다.

SQL 데이터베이스나 콘텐츠 제공자에게 제출된 쿼리 내의 데이터를 사용 중인 경우에는 SQL 주입이 문제일 수도 있습니다. 최선의 방어책은 위의 섹션에서 콘텐츠 제공자에 관해 설명했듯이 매개변수화된 쿼리를 사용하는 것입니다. 또한 권한을 읽기 전용 또는 쓰기 전용으로 제한하기만 해도 SQL 주입에 관련된 잠재적 위험을 줄일 수 있습니다.

위의 보안 기능을 사용할 수 없는 경우에는, 구조화된 데이터 형식을 사용하고 이 데이터가 예상된 형식을 준수하는지 여부를 확인하도록 적극 권장합니다. 문자나 문자 대체값을 블랙리스트에 추가하는 것도 효과적인 전략일 수 있지만, 이 기술에서는 실제로 오류가 발생하기 쉬우므로 가능하면 피해야 합니다.

사용자 데이터 처리

일반적으로 사용자 데이터의 보안을 유지하기 위한 최선의 방법은 중요하거나 개인적인 사용자 데이터에 액세스하는 API의 사용을 최소화하는 것입니다. 사용자 데이터에 액세스할 때 정보 저장이나 전송을 피할 수만 있다면, 데이터를 저장하거나 전송하지 마십시오. 마지막으로, 해시 또는 비가역 형태의 데이터를 사용하여 애플리케이션 로직을 구현할 수 있는 방법에 대해 생각해 보겠습니다. 예를 들어, 이메일 주소의 전송이나 저장을 피하기 위해 여러분의 애플리케이션이 이메일 주소의 해시를 기본 키로 사용할 수도 있습니다. 이렇게 하면 실수로 데이터가 노출될 가능성이 줄어들고 공격자가 애플리케이션 악용을 시도할 가능성도 줄어듭니다.

여러분의 애플리케이션이 개인 정보(예: 암호 또는 사용자 이름)에 액세스할 경우, 일부 관할권에서는 여러분의 데이터 사용 및 저장을 설명하는 개인정보 보호정책을 제공하도록 요구할 수도 있습니다. 따라서 사용자 데이터에 대한 액세스를 최소화하는 보안 모범 사례를 따른다면 법률 준수도 단순해질 것입니다.

또한 여러분의 애플리케이션이 실수로 개인 정보를 다른 상대방(예: 광고를 위한 타사 구성 요소 또는 여러분의 애플리케이션이 사용하는 타사 서비스)에 노출할 경우를 고려해야 합니다. 어떤 구성 요소나 서비스에서 개인 정보를 요구하는데 여러분이 그 이유를 모르는 경우는 정보를 제공하지 마십시오. 일반적으로 여러분의 애플리케이션에 의한 개인 정보 액세스를 줄이면 여기에서 문제가 일어날 가능성도 줄어듭니다.

중요한 데이터에 액세스가 필요한 경우에는, 이 정보가 서버로 전송되어야 하는지 여부나 작업을 클라이언트에서 수행할 수 있는지 여부를 평가하세요. 사용자 데이터의 전송을 피하려면, 중요한 데이터를 사용하는 모든 코딩은 클라이언트에서 실행하도록 고려하세요.

또한 지나치게 관대한 IPC, 누구나 쓸 수 있는 파일 또는 네트워크 소켓을 통해 실수로 사용자 데이터를 기기상의 다른 애플리케이션에 노출하지 않도록 주의하세요. 이것은 권한 보호된 데이터를 유출시키는 특별한 사례입니다(권한 요청 섹션에서 논의).

GUID가 필요한 경우에는 큰 고유의 숫자를 만들어 저장하세요. 전화 번호, IMEI 등의 전화 식별자는 개인 정보와 연결될 수 있으므로 사용하지 마십시오. 이 항목은 Android 개발자 블로그에서 더 자세히 설명합니다.

온디바이스 로그에 쓸 때 주의하세요. Android에서 로그는 공유 리소스이며, READ_LOGS 권한이 있는 애플리케이션에서 이 로그를 사용할 수 있습니다. 전화 로그 데이터는 일시적이며 재부팅 시에 지워지지만, 사용자 정보에 대한 부적절한 로그는 실수로 사용자 데이터를 다른 애플리케이션에 유출시킬 수 있습니다.

WebView 사용

WebView에서 사용하는 웹 콘텐츠는 HTML 및 JavaScript를 포함할 수 있으므로, 이를 부적절하게 사용할 경우 XSS(Cross Site Scripting)(JavaScript 주입)와 같은 일반적인 웹 보안 문제를 일으킬 수 있습니다. Android에는 이러한 잠재적 문제의 범위를 줄여주는 다양한 메커니즘이 있으며, 애플리케이션에 요구되는 최소한의 기능으로 WebView 성능을 제한합니다.

애플리케이션이 JavaScript를 WebView 내에서 직접 사용하지 않는 경우, setJavaScriptEnabled()를 호출하지 마십시오. 일부 샘플 코드에서는 이 메서드를 사용하므로(프로덕션 애플리케이션에서는 이 메서드를 다른 용도로 사용할 수도 있음), 필요치 않은 경우 이 메서드 호출을 제거하세요. 기본적으로, WebView는 JavaScript를 실행하지 않으므로 XSS(Cross Site Scripting)가 불가능합니다.

addJavaScriptInterface()를 사용할 때는 특히 주의가 필요한데, 그 이유는 일반적으로 Android 애플리케이션용으로 예약되는 작업을 JavaScript가 호출하도록 허용할 수 있기 때문입니다. 이것을 사용하는 경우, 모든 입력을 신뢰할 수 있는 웹 페이지에만 addJavaScriptInterface()를 노출시키세요. 신뢰할 수 없는 입력을 허용하는 경우, 신뢰할 수 없는 JavaScript가 여러분 앱 내에서 Android 메서드를 호출할 수도 있습니다. 일반적으로, 여러분의 애플리케이션 APK 내에 포함되어 있는 JavaScript에만 addJavaScriptInterface()를 노출시키는 것이 좋습니다.

여러분의 애플리케이션이 WebView로 중요한 데이터에 액세스하는 경우에는, 로컬로 저장된 모든 파일을 삭제하기 위해 clearCache() 메서드를 사용할 수도 있습니다. 또한 no-cache와 같은 서버측 헤더를 사용하여, 애플리케이션이 특정 콘텐츠를 캐싱해서는 안 된다는 점을 나타낼 수도 있습니다.

Android 4.4(API 레벨 19) 이전의 플랫폼이 실행되는 기기에서는 다양한 보안 문제가 있는 webkit 버전을 사용합니다. 해결 방법은, 이들 기기에서 여러분의 앱이 실행 중인 경우, WebView 객체가 신뢰할 수 있는 콘텐츠만 표시하는지 확인해야 합니다. 또한 여러분의 앱이 SSL의 잠재적 취약성에 노출되지 않도록 보장하기 위해, 업데이트가 가능한 보안 Provider 객체를 사용해야 합니다(SSL 악용을 차단하기 위해 보안 제공자 업데이트에 설명). 여러분의 애플리케이션이 오픈 웹의 콘텐츠를 렌더링해야 하는 경우, 최신 보안 패치로 최신 상태를 유지할 수 있도록 여러분 자신의 렌더러를 제공해 보세요.

자격 증명 처리

일반적으로, 사용자 자격 증명의 요청 빈도를 최소한으로 유지하는 것이 좋습니다. 그래야만 피싱 공격이 더 쉽게 눈에 띄고 그만큼 성공 가능성이 줄어듭니다. 그 대신 인가 토큰을 사용하고 이 토큰을 새로 고치세요.

가능하면, 사용자 이름과 암호는 기기에 저장해서는 안 됩니다. 그 대신, 사용자가 제공한 사용자 이름과 암호를 사용하여 초기 인증을 수행한 다음, 일시적인 서비스별 인가 토큰을 사용하세요.

여러 애플리케이션에 의해 액세스될 수 있는 서비스는 AccountManager를 사용하여 액세스되어야 합니다. 가능하면 AccountManager 클래스를 사용하여 클라우드 기반 서비스를 호출하고, 암호를 기기에 저장하지 마십시오.

AccountManager를 사용하여 Account, CREATOR를 검색한 후 자격 증명을 전달하기 전에, 실수로 자격 증명을 잘못된 애플리케이션에 전달하지 않도록 하세요.

여러분이 만든 애플리케이션에 의해서만 자격 증명이 사용되어야 하는 경우, checkSignature()를 사용하여 AccountManager에 액세스하는 애플리케이션을 확인할 수 있습니다. 또는, 하나의 애플리케이션에서만 자격 증명을 사용하는 경우 KeyStore를 사용하여 저장할 수도 있습니다.

암호화 사용

Android에서는 데이터 격리를 제공하고, 전체 파일 시스템을 지원하고, 보안 통신 채널을 제공할 뿐만 아니라 암호화를 사용하여 데이터를 보호하기 위한 다양한 종류의 알고리즘을 제공합니다.

일반적으로, 여러분의 사용 사례를 지원할 수 있는 가장 높은 수준의 기존 프레임워크 구현을 사용해 보세요. 알려진 위치에서 안전하게 파일을 검색하는 경우에는, 단순 HTTPS URI가 충분할 수도 있으며 암호화에 대해 몰라도 됩니다. 보안 채널이 필요하신 경우에는, 자신만의 프로토콜을 작성하는 대신 HttpsURLConnection 또는 SSLSocket 사용을 고려하세요.

프로토콜을 직접 구현할 필요가 있는 경우, 암호화 알고리즘을 직접 구현하지 않는 것이 좋습니다. Cipher 클래스에 제공되는 AES 또는 RSA 구현의 알고리즘과 같은 기존의 암호화 알고리즘을 사용하세요.

안전한 난수 생성기인 SecureRandom을 사용하여 임의 암호화 키인 KeyGenerator를 초기화하세요. 안전한 난수 생성기로 생성되지 않은 키를 사용할 경우, 알고리즘의 성능이 상당히 약화되며 오프라인 공격을 허용할 수도 있습니다.

반복 사용을 위해 키를 저장해야 하는 경우, 암호화 키의 장기간 보관 및 검색을 위한 메커니즘인 KeyStore와 같은 메커니즘을 사용하세요.

프로세스 간 통신 사용

일부 앱에서는 네트워크 소켓 및 공유 파일과 같은 기존의 Linux 기술을 사용하여 IPC를 구현하려고 시도합니다. 저희는 그 대신 IPC용 Android 시스템 기능을 사용할 것을 적극 권장합니다(예: Intent, Binder 또는 Messenger, ServiceBroadcastReceiver). Android IPC 메커니즘에서 여러분은 자신의 IPC에 연결된 애플리케이션의 ID를 확인할 수 있으며 각 IPC 메커니즘의 보안 정책을 설정할 수 있습니다.

보안 요소의 상당수는 IPC 메커니즘 간에 공유됩니다. 여러분의 IPC 메커니즘을 다른 애플리케이션에 사용할 의도가 아닌 경우, 구성 요소의 매니페스트 요소(예: <service> 요소)에서 android:exported 특성을 "false"로 설정하세요. 이것은 동일 UID 내에 여러 프로세스가 있는 애플리케이션에 유용하며, 또는 여러분이 개발 막바지에 이 기능을 IPC로 노출시키지 않으려고 결정했지만 코드를 재작성하지 않으려는 경우에 유용합니다.

여러분의 IPC를 다른 애플리케이션에서 액세스하려는 경우, <permission> 요소를 사용하여 보안 정책을 적용할 수 있습니다. 동일 키로 서명된 여러분의 별도 앱 사이에 IPC가 있는 경우, android:protectionLevel에서 "signature" 레벨 권한을 사용하는 것이 바람직합니다.

인텐트 사용

인텐트는 Android에서 비동기 IPC에 선호되는 메커니즘입니다. 자신의 애플리케이션 요구사항에 따라, 특정 애플리케이션 구성 요소에 sendBroadcast(), sendOrderedBroadcast() 또는 명시적 인텐트를 사용할 수 있습니다.

참고로, 순차적 브로드캐스트는 수신자에 의해 “소비”될 수 있으므로, 이 브로드캐스트가 모든 애플리케이션에 전달되는 것은 아닙니다. 특정 리시버로 전달되어야 하는 인텐트를 발신하는 경우, nameintent로 리시버를 선언하는 명시적 인텐트를 사용해야 합니다.

인텐트 발신자는 null이 아닌 권한을 지정할 권한이 수신자에게 있는지를 확인할 수 있습니다. 이 권한이 있는 애플리케이션만 인텐트를 수신합니다. 브로드캐스트 인텐트 내의 데이터가 중요한 경우에는, 적절한 권한이 없이는 악성 애플리케이션이 이러한 메시지를 수신할 수 없도록 보장하는 권한 적용을 고려해야 합니다. 이 경우 브로드캐스트를 생성하는 대신, 리시버를 직접 호출하는 것을 고려할 수도 있습니다.

참고: 인텐트 필터는 보안 기능으로 고려되어서는 안 됩니다. 구성 요소는 명시적 인텐트로 호출될 수 있으며, 인텐트 필터를 준수하는 데이터가 구성 요소에 없을 수도 있습니다. 호출된 리시버, 서비스 또는 액티비티에 대해 서식이 올바로 지정되었는지 확인하기 위해 여러분의 인텐트 리시버 내에서 입력 유효성 검사를 수행해야 합니다.

서비스 사용

다른 애플리케이션이 사용할 기능을 제공하기 위해 Service가 종종 사용됩니다. 각 서비스 클래스는 매니페스트 파일에 해당 <service> 선언이 있어야 합니다.

기본적으로, 서비스는 내보내기가 수행되지 않으며 다른 애플리케이션에 의해 호출될 수 없습니다. 그러나, 인텐트 필터를 서비스 선언에 추가하면 기본적으로 내보내집니다. 여러분이 원하는 방식으로 동작하게 하려면 android:exported 특성을 명시적으로 선언하는 것이 가장 좋습니다. 또한 android:permission 특성을 사용하여 서비스를 보호할 수도 있습니다. 이 경우 다른 애플리케이션이 서비스를 시작, 중지 또는 바인딩할 수 있기 위해 해당 <uses-permission> 요소를 자체 매니페스트에 선언해야 합니다.

서비스에 대한 개별 IPC 호출을 보호할 수 있는 권한이 서비스에 있으며, 이를 위해 해당 호출 구현을 실행하기에 앞서 checkCallingPermission()을 호출합니다. 일반적으로 선언적 권한을 매니페스트에 사용하는 것이 좋습니다. 이러한 선언적 권한은 간과될 가능성이 적습니다.

바인더 및 메신저 인터페이스 사용

Binder 또는 Messenger는 Android에서 RPC 스타일 IPC에 선호되는 메커니즘입니다. 이들 메커니즘은 필요한 경우 엔드포인트의 상호 인증을 가능케 하는 명확한 인터페이스입니다.

인터페이스별 권한 검사가 필요없는 방식으로 인터페이스를 설계하도록 적극 권장합니다. BinderMessenger 객체는 애플리케이션 매니페스트 내에 선언되지 않으므로, 여러분이 선언적 권한을 객체에 직접 적용할 수 없습니다. 일반적으로 객체는 이 객체가 구현되어 있는 Service 또는 Activity에 대해 애플리케이션 매니페스트에 선언된 권한을 상속합니다. 인증 및/또는 액세스 제어가 요구되는 인터페이스를 만드는 경우, 이러한 제어는 Binder 또는 Messenger 인터페이스에 코드로 명시적으로 추가되어야 합니다.

액세스 제어가 요구되는 인터페이스를 제공하는 경우, 요구되는 권한이 호출자에게 있는지 확인하기 위해 checkCallingPermission()을 사용해야 합니다. 이것은 호출자를 대신하여 서비스에 액세스하기 전에 특히 중요합니다. 왜냐하면 애플리케이션의 ID가 다른 인터페이스로 전달되기 때문입니다. Service에 의해 제공되는 인터페이스를 호출하는 경우, 해당 서비스에 액세스하는 권한이 없다면 bindService() 호출이 실패할 수도 있습니다. 여러분의 애플리케이션에 의해 로컬로 제공되는 인터페이스를 호출하는 경우, 내부 보안 검사를 충족시키기 위해 clearCallingIdentity()를 사용하는 것이 유용할 수도 있습니다.

서비스와 함께 IPC를 수행하는 방법에 대한 자세한 내용은 바인드된 서비스를 참조하세요.

브로드캐스트 리시버 사용

BroadcastReceiverIntent에 의해 시작된 비동기 요청을 처리합니다.

기본적으로, 리시버는 내보내지며 다른 애플리케이션에 의해 호출될 수 있습니다. BroadcastReceiver를 다른 애플리케이션에서 사용하려는 경우, <receiver> 요소를 애플리케이션 매니페스트 내에 사용하여 보안 권한을 리시버에 적용할 수 있습니다. 이 경우 적절한 권한이 없는 애플리케이션은 인텐트를 BroadcastReceiver에 보낼 수 없습니다.

동적으로 코드 로드

애플리케이션 APK 밖에서는 코드를 로드하지 않는 것이 좋습니다. 그럴 경우 코드 주입 또는 코드 변조로 인해 애플리케이션 손상의 가능성이 상당히 증가합니다. 또한 버전 관리와 애플리케이션 테스트가 더욱 복잡해집니다. 마지막으로, 애플리케이션 동작을 확인하는 것이 불가능할 수 있으므로, 일부 환경에서는 금지될 수도 있습니다.

애플리케이션이 코드를 동적으로 로드하는 경우 명심할 가장 중요한 점은, 동적으로 로드되는 코드는 애플리케이션 APK와 동일한 보안 권한을 가지고 실행된다는 점입니다. 사용자는 여러분의 신원에 근거하여 애플리케이션을 설치하기로 결정했으며, 애플리케이션 내에서 실행되는 모든 코드(동적으로 로드되는 코드 포함)를 여러분이 제공해주기를 기대합니다.

동적으로 로드되는 코드와 관련된 중대한 보안 위험은 이 코드가 확인 가능한 소스로부터 와야 한다는 것입니다. 모듈이 APK 내에 직접 포함된 경우 이 모듈은 다른 애플리케이션에 의해 수정될 수 없습니다. 이는 코드가 네이티브 라이브러리이거나 DexClassLoader를 사용하여 로드 중인 클래스인 경우에 사실입니다. 저희는 애플리케이션이 비보안 위치에서 코드를 로드하려고 시도하는 경우를 많이 보았습니다(예: 암호화되지 않은 프로토콜을 통해 네트워크에서 다운로드 또는 외부 저장소와 같이 누구나 쓸 수 있는 위치에서 다운로드). 이러한 위치에서는 전송 중인 콘텐츠를 네트워크상의 누군가가 수정할 수 있으며, 사용자 기기상의 또 다른 애플리케이션이 기기상의 콘텐츠를 수정할 수 있습니다.

가상 머신에서의 보안

Dalvik은 Android의 런타임 가상 머신(VM)입니다. Dalvik은 Android용으로 특별히 구축되었지만, 다른 가상 머신에서 보안 코드와 관련된 많은 우려가 Android에도 그대로 적용됩니다. 일반적으로, 여러분은 가상 머신과 관련된 보안 문제는 우려하지 않아도 됩니다. 여러분의 애플리케이션은 안전한 샌드박스 환경에서 실행되므로, 시스템상의 다른 프로세스가 여러분의 코드나 개인 데이터에 액세스할 수 없습니다.

가상 머신 보안에 관해 더 자세히 알고 싶으면 이 주제에 관한 기존의 몇몇 자료를 살펴보도록 권장합니다. 다음은 가장 인기있는 두 가지 리소스입니다.

이 문서에서는 Android에 특정한 분야나 다른 VM 환경과 차이가 나는 분야에 초점을 맞춥니다. 다른 VM 환경에서 프로그래밍을 경험했던 개발자는 Android용 앱을 작성할 때와는 다른 일반적인 두 가지 문제를 겪을 수 있습니다.

  • 일부 가상 머신(예: JVM 또는 .NET 런타임)은 보안 경계로 작동하면서, 기본 운영 체제 기능에서 코드를 분리합니다. Android에서는 Dalvik VM이 보안 경계가 아니므로(애플리케이션 샌드박스는 OS 수준에서 구현됨), Dalvik은 어떠한 보안 제약 조건도 없이 동일한 애플리케이션의 네이티브 코드와 상호 연동할 수 있습니다.
  • 모바일 기기의 제한된 저장소를 감안할 때, 개발자가 모듈식 애플리케이션을 빌드하고 동적 클래스 로딩을 사용하고 싶어하는 것은 당연합니다. 이 작업을 수행할 때, 애플리케이션 로직을 검색하는 소스 위치와 이것을 로컬로 저장하는 위치를 둘 다 고려하세요. 확인되지 않은 소스(예: 비보안 네트워크 소스 또는 외부 저장소)에서는 동적 클래스 로딩을 사용하지 마세요. 악성 동작을 포함하도록 소스가 수정되었을 수 있습니다.

네이티브 코드에서의 보안

일반적으로, 애플리케이션 개발을 위해 개발자는 Android NDK와 함께 네이티브 코드를 사용하는 것보다 Android SDK를 사용하는 것이 좋습니다. 네이티브 코드로 빌드된 애플리케이션은 더 복잡하고, 이식성이 떨어지며, 일반적인 메모리 손상 오류(예: 버퍼 오버플로)가 포함될 가능성이 더 높습니다.

Android는 Linux 커널을 사용하여 빌드되며, 여러분이 Linux 개발 보안 모범 사례에 익숙해진다면 네이티브 코드를 사용할 때 특히 유용할 것입니다. Linux 보안 모범 사례는 이 문서의 범위를 벗어나는 것이지만, 가장 인기있는 리소스 중 하나가 바로 “Secure Programming for Linux and Unix HOWTO”이며 http://www.dwheeler.com/secure-programs에서 제공됩니다.

Android와 대부분의 Linux 환경에서 중요한 차이점 중 하나는 애플리케이션 샌드박스입니다. Android에서는 네이티브 코드로 작성된 애플리케이션을 비롯하여 모든 애플리케이션이 애플리케이션 샌드박스에서 실행됩니다. Linux에 익숙한 개발자가 가장 기본적인 수준에서 이에 관해 생각해볼 수 있는 좋은 방법은, 매우 제한된 권한으로 모든 애플리케이션에 고유 UID가 지정된다는 사실을 아는 것입니다. 이에 대해서는 Android 보안 개요에서 자세히 설명합니다. 여러분이 네이티브 코드를 사용 중이더라도 애플리케이션 권한에 익숙해야 합니다.