Android Security Tips
Android에는 애플리케이션 보안 문제의 빈도와 영향을 크게 줄이는 보안 기능이 내장되어 있습니다. 이 시스템은 사용자가 기본 시스템과 파일 권한으로 앱을 빌드하고 보안과 관련된 까다로운 결정을 피할 수 있도록 설계되었습니다.
안전한 앱을 빌드하는 데 도움이 되는 핵심 보안 기능은 다음과 같습니다.
▶ 사용자의 앱 데이터와 코드 실행을 다른 앱으로부터 격리하는 Android 애플리케이션 샌드박스
▶ 암호화, 권한, 안전한 IPC와 같은 일반적인 보안 기능이 강력하게 구현된 애플리케이션 프레임워크
▶ ASLR, NX, ProPolice, safe_iop, OpenBSD dlmalloc, OpenBSD calloc, Linux mmap_min_addr과 같은 기술로 일반적인 메모리 관리 오류와 연관된 위험 완화
▶ 암호화된 파일 시스템으로 분실하거나 도난당한 기기의 데이터 보호
▶ 시스템 기능 및 사용자 데이터에 대한 액세스를 제한하기 위해 사용자가 부여하는 권한
▶ 앱별로 애플리케이션 데이터를 제어하기 위해 애플리케이션에서 정의하는 권한
이 문서의 Android 보안 권장사항을 숙지하는 것이 좋습니다. 이러한 권장사항을 일반적인 코딩 습관으로 사용하면 사용자에게 부정적인 영향을 미치는 보안 문제가 발생할 가능성이 줄어듭니다.
#. 데이터 저장
Android 애플리케이션의 가장 일반적인 보안 관련 우려사항은 기기에 저장한 데이터에 다른 앱이 액세스 할 수 있는가입니다. 기기에 데이터를 저장하는 기본적인 방법 3가지는 다음과 같습니다.
▶ 내부 저장소
▶ 외부 저장소
▶ 콘텐츠 제공업체
다음 단락에서는 각 접근 방식과 관련된 보안 문제를 설명합니다.
#. 내부 저장소 사용
기본적으로 내부 저장소에 생성한 파일은 내 앱에서만 액세스할 수 있습니다. Android에는 이러한 보호 장치가 구현되어 있으며, 대부분의 애플리케이션에서는 이 기능으로 충분합니다.
일반적으로 IPC 파일은 특정 애플리케이션에 대한 데이터 액세스를 제한하는 기능을 제공하지 않으며 데이터 형식을 제어하지 않으므로 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_PERMISSION
과 FLAG_GRANT_WRITE_URI_PERMISSION
플래그를 사용하여 액세스를 세분화할 수 있습니다. 이러한 권한의 범위는 <grant-uri-permission>
요소로 더 제한할 수 있습니다.
콘텐츠 제공업체에 액세스할 때 query()
, update()
, delete()
와 같은 매개변수화된 쿼리 메서드를 사용하여 신뢰할 수 없는 소스의 SQL 삽입 가능성을 방지할 수 있습니다. 메서드에 제출하기 전에 사용자 데이터를 연결하여 selection
인수가 빌드된 경우 매개변수화된 메서드를 사용하는 것만으로는 충분하지 않습니다.
쓰기 권한에 대해 잘못된 보안 인식을 가지면 안 됩니다. 쓰기 권한은 창의적인 WHERE
절을 사용하고 결과를 파싱하여 일부 데이터를 확인할 수 있는 SQL 문을 허용합니다. 예를 들어 공격자는 특정 전화번호가 이미 존재하고 있을 때만 행을 수정하여 통화 기록에 해당 전화번호가 존재하는지 찾을 수 있습니다. 콘텐츠 제공업체 데이터에 예측 가능한 구조가 있다면 쓰기 권한은 읽기 및 쓰기 권한을 모두 제공하는 것과 같을 수 있습니다.
#. 권한 사용
Android는 애플리케이션을 각각 샌드박스화하므로 애플리케이션에서는 리소스와 데이터를 명시적으로 공유해야 합니다. 이를 위해 애플리케이션에서는 기본 샌드박스에서 제공하지 않는 추가 기능(예: 카메라와 같은 기기 기능에 액세스)을 실행하기 위해 필요한 권한을 선언합니다.
#. 권한 요청
앱에서 요청하는 권한의 수를 최소화해야 합니다. 민감한 정보에 대한 액세스 권한을 제한하면 이러한 권한을 오용하는 위험이 감소하여 사용자 채택률을 높이고 공격자에 대한 앱의 취약점을 줄일 수 있습니다. 일반적으로 앱이 기능하는 데 필요한 권한이 아니라면 요청해서는 안 됩니다. 없으면 앱이 실행되지 않는 기능이 있는 경우 매니페스트 파일에 <uses-feature>
요소를 사용해 그 기능을 선언하세요.
어떠한 권한도 요청하지 않도록 애플리케이션을 설계할 수 있다면 그렇게 하는 것이 좋습니다. 예를 들어, 고유 식별자를 생성하기 위해 기기 정보에 대한 액세스를 요청하기보다는 애플리케이션의 GUID를 생성하세요(사용자 데이터 처리에 관한 섹션 참조). 또는 권한이 필요한 외부 저장소를 사용하는 대신 내부 저장소에 데이터를 저장하시기 바랍니다.
권한 요청 외에도 애플리케이션에서 <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 메커니즘을 사용하세요. INADDR_ANY에 결합하면 애플리케이션에서 어디서나 요청을 받을 수 있으므로 루프백 사용보다 좋지 않습니다.
HTTP 또는 기타 안전하지 않은 프로토콜에서 다운로드한 데이터는 신뢰하면 안 됩니다. 여기에는 WebView
의 입력 유효성 검사와 HTTP에서 발행한 인텐트에 대한 응답이 포함됩니다.
#. 전화 네트워킹 사용
SMS
프로토콜은 주로 사용자 간 통신을 위해 설계되었으며 데이터를 전송하려는 앱에는 적합하지 않습니다. SMS의 제한 사항으로 인해 데이터 메시지를 웹 서버에서 사용자 기기의 앱으로 전송하려면 Google Cloud Messaging(GCM) 및 IP 네트워크를 사용해야 합니다.
SMS는 네트워크나 기기에서 암호화되거나 강력하게 인증되지 않으니 주의해야 합니다. 특히 모든 SMS 수신자는 악의적인 의도를 가진 사용자가 자신의 애플리케이션에 SMS를 전송했을 수도 있다는 사실을 인지해야 합니다. 따라서 인증되지 않은 SMS 데이터에 의존해 민감한 명령어를 실행해서는 안 됩니다. 또한 SMS는 네트워크에서 위장되거나 가로채기 될 수 있으니 주의해야 합니다. Android 지원 기기에서 SMS 메시지는 브로드캐스트 인텐트로 전송되기 때문에 READ_SMS
권한이 있는 다른 애플리케이션에서 읽거나 캡처할 수 있습니다.
#. 입력 유효성 검사 시행
충분하지 않은 입력 유효성 검사는 실행 중인 플랫폼과 관계없이 애플리케이션에 영향을 주는 가장 일반적인 보안 문제 중 하나입니다. Android는 애플리케이션이 입력 유효성 검사 문제에 노출되는 것을 줄이는 플랫폼 수준의 대책을 보유하고 있으므로 가능한 한 이러한 기능을 사용해야 합니다. 또한 안전한 형식의 언어를 선택하면 입력 유효성 검사 문제가 발생할 가능성을 줄일 수 있습니다.
네이티브 코드를 사용하면 파일에서 읽거나, 네트워크를 통해 수신되거나, IPC로부터 수신된 데이터에 보안 문제가 발생할 수 있습니다. 가장 일반적인 문제는 버퍼 오버플로우, UAF(Use-After-Free), OBO(Off-By-One)입니다. Android에서는 이러한 오류의 악용을 막기 위해 ASLR 및 DEP와 같은 여러 기술을 제공하지만, 근본적인 문제는 해결되지 않습니다. 포인터 처리와 버퍼 관리에 주의하면 이러한 취약점을 방지할 수 있습니다.
자바스크립트 및 SQL과 같은 동적인 문자열 기반 언어 역시 이스케이프 문자 및 스크립트 삽입으로 인해 입력 유효성 검사 문제가 발생할 수 있습니다.
SQL 데이터베이스나 콘텐츠 제공업체에 제출된 쿼리 내에 있는 데이터를 사용하는 경우 SQL 삽입이 문제가 될 수 있습니다. 최선의 방어는 위의 콘텐츠 제공업체 관련 섹션에서 설명한 것처럼 매개변수화된 쿼리를 사용하는 것입니다. 읽기 전용 또는 쓰기 전용으로 권한을 제한해도 SQL 삽입과 관련된 잠재적인 위험을 줄일 수 있습니다.
위의 보안 기능을 사용할 수 없는 경우 잘 구조화된 데이터 형식을 사용하고 데이터가 올바른 형식을 준수하는지 확인해야 합니다. 문자 차단이나 문자 교체가 효과적인 전략이 될 수 있지만 이러한 기술은 실제 적용 시 오류가 발생하기 쉽기 때문에 가능한 한 피해야 합니다.
#. 사용자 데이터 처리
일반적으로 사용자 데이터 보안을 위한 최선의 접근 방식은 민감한 정보나 사용자 개인정보에 액세스 하는 API의 사용을 최소화하는 것입니다. 사용자 데이터에 액세스 하지만 그 데이터를 저장하거나 전송하지 않아도 된다면 데이터를 저장하거나 전송하면 안 됩니다. 해시 또는 비가역 형태의 데이터를 사용하여 애플리케이션 로직을 구현할 수 있는 방법이 있는지 고려해 보세요. 예를 들어 애플리케이션에서 이메일 주소의 해시를 기본 키로 사용하여 이메일 주소의 전송이나 저장을 방지할 수도 있습니다. 이렇게 하면 실수로 데이터를 노출할 가능성이 줄어들고 공격자가 애플리케이션 악용을 시도할 가능성도 줄어듭니다.
애플리케이션에서 비밀번호나 사용자 이름과 같은 개인정보에 액세스하는 경우, 관할권에 따라 데이터 사용 및 저장에 대해 설명하는 개인정보처리 방침을 제공해야 할 수 있습니다. 또한 사용자 데이터에 대한 액세스를 최소화하는 보안 권장사항을 따르면 규정 준수를 간소화할 수 있습니다.
애플리케이션이 다른 타사(예: 광고용 타사 구성요소나 애플리케이션에서 사용하는 타사 서비스)에 개인정보를 실수로 노출할 수 있는지도 고려해야 합니다. 구성요소나 서비스에서 개인정보를 요구하는데 그 이유를 모르는 경우 정보를 제공해서는 안 됩니다. 일반적으로 애플리케이션의 개인정보 액세스를 줄이면 여기에서 문제가 일어날 가능성도 줄어듭니다.
앱에서 민감한 정보에 액세스해야 하는 경우 이 정보를 서버에 전송해야 하는지 또는 클라이언트에서 작업을 실행할 수 있는지 평가해야 합니다. 사용자 데이터 전송을 방지하려면 민감한 정보를 사용하는 코드는 클라이언트에서 실행하는 것을 고려해 보세요. 또한 지나치게 관대한 IPC, 누구나 쓸 수 있는 파일, 네트워크 소켓을 통해 기기의 다른 애플리케이션에 사용자 데이터를 실수로 노출하지 않도록 해야 합니다. 지나치게 관대한 IPC는 권한 요청 섹션에서 논의된 것처럼 권한으로 보호되는 데이터 유출의 특수한 사례입니다.
GUID가 필요한 경우 크고 고유한 숫자를 만들어 저장하세요. 전화번호나 IMEI와 같은 전화 식별자는 개인정보와 연결될 수 있으므로 사용하면 안 됩니다. 이 주제는 Android 개발자 블로그에 자세하게 설명되어 있습니다.
기기 내 로그에 쓸 때 주의하세요. Android에서는 로그가 공유 리소스이며 READ_LOGS
권한이 있는 애플리케이션에서 사용할 수 있습니다. 전화 로그 데이터는 일시적이며 재부팅 시 삭제된다고 해도 사용자 정보의 부적절한 기록으로 인해 사용자 데이터를 실수로 다른 애플리케이션에 유출할 수 있습니다. PII 로깅을 하지 않는 것뿐만 아니라 프로덕션 앱에서는 로그 사용을 제한해야 합니다. 이를 간단히 구현하려면 디버그 플래그와 맞춤 Log
클래스를 쉽게 구성할 수 있는 로깅 수준으로 사용하세요.
#. WebView 사용
WebView
는 HTML과 자바스크립트를 포함할 수 있는 웹 콘텐츠를 사용하기 때문에, 부적절한 사용 시 교차 사이트 스크립팅(자바스크립트 삽입)과 같은 일반적인 웹 보안 문제를 일으킬 수 있습니다. Android에는 WebView
의 기능을 애플리케이션에서 필요로 하는 최소한의 기능으로 제한하여 이러한 문제가 발생할 가능성을 줄이는 다양한 메커니즘이 포함되어 있습니다.
애플리케이션이 WebView
에서 자바스크립트를 직접 사용하지 않는 경우 setJavaScriptEnabled()
를 호출하면 안 됩니다. 일부 샘플 코드에서 이 메서드를 사용하며, 이를 프로덕션 애플리케이션에서 변형하여 활용할 수 있으므로 필요하지 않다면 해당 메서드 호출을 삭제하세요. 기본적으로 WebView
는 자바스크립트를 실행하지 않기 때문에 교차 사이트 스크립팅이 불가능합니다.
addJavaScriptInterface()
를 사용할 경우 자바스크립트에서 일반적으로 Android 애플리케이션에 예약된 작업을 호출할 수 있기 때문에 특별히 주의해야 합니다. 사용할 때는 모든 입력을 신뢰할 수 있는 웹페이지에만 addJavaScriptInterface()
를 노출하세요. 신뢰할 수 없는 입력이 허용되면 신뢰할 수 없는 자바스크립트가 앱에서 Android 메서드를 호출할 수 있습니다. 일반적으로 애플리케이션 APK에 포함된 자바스크립트에만 addJavaScriptInterface()
를 노출하는 것이 좋습니다.
애플리케이션에서 WebView
를 사용하여 민감한 정보에 액세스하는 경우 로컬에 저장된 파일을 삭제하기 위해 clearCache()
메서드를 사용할 수 있습니다. no-cache
와 같은 서버 측 헤더를 사용해 애플리케이션이 특정 콘텐츠를 캐시하면 안 된다는 것을 나타낼 수도 있습니다.
Android 4.4(API 레벨 19) 이전의 플랫폼을 실행하는 기기에서는 다양한 보안 문제가 있는 webkit
버전을 사용합니다. 이를 해결하려면 이러한 기기에서 앱이 실행 중인 경우 WebView
개체가 신뢰할 수 있는 콘텐츠만 표시하는지 확인해야 합니다. 앱에서 SSL의 잠재적 취약점에 노출되지 않도록 하려면 SSL 악용을 차단하기 위한 보안 제공자 업데이트에 설명된 대로 업데이트 가능한 보안 Provider
개체를 사용합니다. 애플리케이션에서 오픈 웹의 콘텐츠를 렌더링해야 하는 경우 최신 보안 패치로 최신 상태를 유지할 수 있도록 자체 렌더러를 제공해 보세요.
#. 사용자 인증 정보 처리
피싱 공격을 더 눈에 띄게 하고 성공 가능성을 낮추려면 사용자 인증 정보를 묻는 빈도를 최소화합니다. 그 대신 승인 토큰을 사용하고 새로고침 하세요.
가능한 경우 사용자 이름과 비밀번호를 기기에 저장하면 안 됩니다. 대신 사용자가 제공한 사용자 이름과 비밀번호를 사용하여 초기 인증을 시행한 다음 서비스별 단기 승인 토큰을 사용하세요.
여러 애플리케이션에서 액세스 할 수 있는 서비스는 AccountManager
를 사용해 액세스해야 합니다. 가능하면 AccountManager
클래스를 사용해 클라우드 기반 서비스를 호출하며, 기기에 비밀번호를 저장하면 안 됩니다.
AccountManager
를 통해 Account
를 검색한 후에는 사용자 인증 정보를 실수로 잘못된 애플리케이션에 전달하지 않도록 사용자 인증 정보에 전달하기 전에 CREATOR
를 사용합니다.
자신이 만든 애플리케이션에서만 사용자 인증 정보를 사용하는 경우 checkSignature()
를 사용해 AccountManager
에 액세스 하는 애플리케이션을 확인할 수 있습니다. 또는 하나의 애플리케이션에서만 사용자 인증 정보를 사용하는 경우 KeyStore
를 사용하여 저장할 수도 있습니다.
#. 암호화 사용
Android에서는 데이터 격리 제공, 전체 파일 시스템 암호화 지원, 안전한 통신 채널 제공 외에도 암호화를 사용하여 데이터를 보호하기 위한 다양한 알고리즘을 제공합니다.
일반적으로 소프트웨어에서 어떤 자바 암호화 아키텍처(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
및 BroadcastReceiver
와 함께 Intent
, Binder
또는 Messenger
와 같은 IPC용 Android 시스템 기능을 대신 사용해야 합니다. Android IPC 메커니즘을 사용하면 IPC에 연결하는 애플리케이션의 ID를 확인하고 각 IPC 메커니즘에 보안 정책을 설정할 수 있습니다.
보안 요소의 상당수는 IPC 메커니즘 간에 공유됩니다. 사용 중인 IPC 메커니즘을 다른 애플리케이션에서 사용하려는 경우가 아니라면 <service>
요소와 같은 구성요소의 매니페스트 요소에 android:exported
속성을 false
로 설정합니다. 이는 동일한 UID 내 여러 프로세스로 구성된 애플리케이션 또는 개발 막바지에 실제로 이 기능을 IPC로 노출하지 않으려고 했으나 코드를 재작성하지 않으려는 경우에 유용합니다.
다른 애플리케이션에서 IPC에 액세스 할 수 있는 경우 <permission>
요소를 사용해 보안 정책을 적용할 수 있습니다. 동일한 키로 서명된 별도의 자체 앱 간에 IPC가 있는 경우 android:protectionLevel
에 signature
수준 권한을 사용하는 것이 좋습니다.
#. 인텐트 사용
Activity 및 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
를 사용해 백그라운드 서비스를 실행해야 합니다. JobScheduler
에 관한 자세한 내용은 API-reference documentation
을 참조하세요.
서비스는 호출 구현을 실행하기 전에 checkCallingPermission()
을 호출하여 서비스로의 개별 IPC 호출을 권한으로 보호할 수 있습니다. 매니페스트의 선언적 권한은 실수 가능성이 적으므로 이러한 권한을 사용해야 합니다.
★ 주의: 클라이언트와 서버 권한을 혼동하면 안 됩니다. 호출된 앱에 적절한 권한이 있고 호출하는 앱에 동일한 권한을 부여했는지 확인하세요.
#. Binder 및 Messenger 인터페이스 사용
Android에서 RPC 스타일 IPC에는 Binder
나 Messenger
사용이 선호됩니다. 이 메커니즘은 필요한 경우 엔드포인트 간의 상호 인증을 가능하게 하는 잘 정의된 인터페이스를 제공합니다.
앱 인터페이스는 인터페이스별 권한 확인이 필요 없는 방식으로 설계해야 합니다. 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은 보안 제약 없이 동일한 애플리케이션의 네이티브 코드와 상호 운용될 수 있습니다.
▶ 휴대기기의 제한된 저장용량을 고려할 때 개발자는 일반적으로 모듈식 애플리케이션을 빌드하고 동적 클래스 로드를 사용하고자 합니다. 이렇게 하는 경우 애플리케이션 로직을 검색하는 소스와 이를 로컬로 저장하는 위치를 모두 고려하시기 바랍니다. 안전하지 않은 네트워크 소스나 외부 저장소와 같이 확인되지 않은 소스에서는 동적 클래스를 로드하면 안 됩니다. 악의적인 동작을 포함하도록 코드가 수정될 수 있기 때문입니다.
#. 네이티브 코드의 보안
일반적으로 애플리케이션 개발에는 Android NDK와 함께 네이티브 코드를 사용하는 것이 아니라 Android SDK를 사용해야 합니다. 네이티브 코드로 빌드된 애플리케이션은 더 복잡하고, 이동성이 떨어지며, 버퍼 오버플로우와 같은 일반적인 메모리 손상 오류가 포함될 가능성이 높습니다.
Android는 Linux 커널을 사용해 빌드되었으며, Linux 개발 보안 권장사항에 익숙하면 네이티브 코드를 사용하는 경우 특히 유용합니다. Linux 보안 권장사항으로는 이 문서의 범위를 벗어나지만, 널리 사용되는 리소스 중 하나인 안전한 프로그래밍 방법 - 안전한 소프트웨어 만들기가 있습니다.
Android와 대부분의 Linux 환경 간의 중요한 차이점은 애플리케이션 샌드박스입니다. Android에서는 네이티브 코드로 작성된 애플리케이션을 비롯한 모든 애플리케이션이 샌드박스에서 실행됩니다. 가장 기본적인 수준에서 Linux에 익숙한 개발자가 이에 대해 생각해 볼 수 있는 좋은 방법은 모든 애플리케이션에 매우 제한된 권한이 있는 고유 UID가 부여된다는 사실을 아는 것입니다. 이 내용은 Android 보안 개요에서 자세히 다루며, 개발자라면 네이티브 코드를 사용하는 경우에도 애플리케이션 권한에 익숙해져야 합니다.
원본 링크 : https://developer.android.com/training/articles/security-tips
'Development > Security' 카테고리의 다른 글
[Security] 대칭키 암호화 (0) | 2019.08.29 |
---|---|
[Security] PKCS 정의 (0) | 2019.08.29 |
[Security] 중요한(민감한) 데이터 Access permission (0) | 2019.08.28 |
[Security] Android Keystore 시스템 (0) | 2019.08.27 |
[Security] Android Securely 데이터 저장 (0) | 2019.08.26 |