반응형

이번 시간은 Android Keystore 시스템에 대하여 알아보도록 하겠습니다.

 

#. Android Keystore System

Android Keystore 시스템을 사용하면 암호화 키를 컨테이너에 저장하여 기기에서 추출하기 어렵게 할 수 있습니다. 키 저장소에 키가 저장되면, 키 자료는 내보낼 수 없는 상태로 유지하면서 키를 암호화 작업에 사용할 수 있습니다. 이 시스템에서는 키 사용 시기와 사용 방법을 제한하는 기능도 제공합니다. 예를 들어 키 사용을 위해 사용자 인증을 요구하거나, 특정 암호화 모드에서만 키를 사용하도록 제한할 수 있습니다. 자세한 내용은 보안 기능 섹션을 참조하세요.

 

Keystore 시스템은 Android 4.3(API 수준 18)에서 도입된 Android Keystore 제공자 기능뿐 아니라 KeyChain API에서도 사용됩니다. 이 문서에서는 Android Keystore 제공자의 사용 시기와 사용 방법을 설명합니다.

 

#. 보안 기능

 

Android Keystore 시스템은 키 자료의 무단 사용을 방지합니다. 

첫째, Android Keystore는 애플리케이션 프로세스와 Android 기기 전체에서 키 자료의 추출을 차단하여 Android 기기 외부에서 키 자료의 무단 사용을 줄입니다. 

둘째, Android Keystore는 앱에서 승인된 키 사용처를 지정하도록 하여 앱 프로세스 외부에 적용하는 방식으로 Android 기기에서 키 자료의 무단 사용을 줄입니다.

 

#. 추출 차단

Android Keystore 키의 키 자료 추출을 차단하기 위해 두 가지 보안 조치가 사용됩니다.

키 자료는 애플리케이션 프로세스에 포함되지 않습니다. 애플리케이션에서 Android Keystore 키를 사용하여 암호화 작업을 수행하는 경우, 백그라운드 작업을 통해 서명하거나 확인할 일반 텍스트, 암호 텍스트 및 메시지가 암호화 작업을 수행하는 시스템 프로세스로 공급됩니다. 앱 프로세스가 손상된 경우 공격자가 앱 키를 사용할 수 있지만 Android 기기 외부에서 사용하기 위해 키 자료를 추출할 수는 없습니다.

▶ 키 자료를 Android 기기의 보안 하드웨어(예: TEE(신뢰할 수 있는 실행 환경), SE(보안 요소))에 바인딩할 수 있습니다. 이 기능을 키에 사용하면, 키 자료가 보안 하드웨어 외부에 노출되지 않습니다. Android OS가 손상되거나 공격자가 기기의 내부 저장소를 읽을 수 있는 경우 Android 기기에 있는 모든 앱의 Android Keystore 키를 사용할 수 있지만 기기에서 키를 추출할 수는 없습니다. 이 기능은 기기의 보안 하드웨어에서 키 사용이 승인된 키 알고리즘, 차단 모드, 패딩 스키마, 다이제스트 등의 특정한 조합을 지원하는 경우에만 사용할 수 있습니다. 키에 기능을 사용할 수 있는지 확인하려면 키에 대한 KeyInfo를 가져와 KeyInfo.isInsideSecurityHardware()의 반환 값을 검사합니다.

 

#. 하드웨어 보안 모듈

Android 9(API 수준 28) 이상이 설치되어 실행되는 지원 기기에는 하드웨어 보안 모듈에 상주하는 Keymaster HAL 구현인 StrongBox Keymaster가 있을 수 있습니다. 모듈에 포함된 구성 요소는 다음과 같습니다.

▶ 자체 CPU

▶ 보안 저장소

▶ 순수 난수 생성기

▶ 패키지 변조와 앱의 무단 사이드로드를 방지하는 추가 메커니즘

시스템은 StrongBox Keymaster에 저장된 키를 검사할 때 TEE(신뢰할 수 있는 실행 환경)를 사용하여 키의 무결성을 입증합니다.

저전력 StrongBox 구현을 지원하기 위해 다음과 같은 일부 알고리즘과 키 크기가 지원됩니다.

▶ RSA 2048

▶ AES 128 및 256

▶ ECDSA P-256

▶ HMAC-SHA256(8~64바이트의 키 크기 지원)

▶ Triple DES 168

KeyStore 클래스를 사용하여 키를 생성하거나 가져올 때 setIsStrongBoxBacked() 메서드에 true를 전달하여 StrongBox Keymaster에 키를 저장하도록 지정합니다. 이 메서드는 KeyGenParameterSpec.Builder 클래스 또는 KeyProtection.Builder 클래스에 있습니다.

참고: 키와 관련해서 지정된 알고리즘과 키 크기에 대해 StrongBox Keymaster를 사용할 수 없는 경우 프레임워크에서 StrongBoxUnavailableException이 throw됩니다.

 

#. 키 사용 승인

Android 기기에서 키 무단 사용을 줄이기 위해, Android Keystore에서는 키를 생성하거나 가져올 때 앱에서 승인된 키 사용처를 지정할 수 있습니다. 키를 생성하거나 가져온 후에는 승인을 변경할 수 없습니다. 그런 다음, 키가 사용될 때마다 Android Keystore에서 승인을 실행합니다. 이 기능은 고급 보안 기능이며, 일반적으로 키 생성/가져오기 이후(이전이나 도중은 아님) 애플리케이션 프로세스가 손상되더라도 키 무단 사용으로 이어져서는 안 된다는 요구사항이 있는 경우에만 유용합니다.

지원되는 키 사용 승인은 다음과 같은 카테고리로 구분됩니다.

▶ 암호화: 키 사용이 승인된 키 알고리즘, 작업 또는 용도(암호화, 복호화, 서명, 확인), 패딩 스키마, 차단 모드, 다이제스트

▶ 시간적 유효 기간: 키 사용이 승인된 시간 간격

▶ 사용자 인증: 최근에 사용자가 인증된 경우에만 키를 사용할 수 있습니다. 키 사용을 위한 사용자 인증 요구를 참조하세요

추가적인 보안 조치로, 키 자료가 보안 하드웨어 안에 있는 키의 경우(KeyInfo.isInsideSecurityHardware() 참조) Android 기기에 따라 보안 하드웨어에서 일부 키 사용 승인을 실행할 수 있습니다. 일반적으로 보안 하드웨어는 암호화 승인 및 사용자 인증 승인을 실행합니다. 보안 하드웨어에는 대체로 독립적인 실시간 보안 클록이 없기 때문에 시간적 유효성 간격 승인을 실행할 가능성은 적습니다.

보안 하드웨어에서 키의 사용자 인증 승인을 실행하는지 여부는 KeyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware()를 사용하여 쿼리 할 수 있습니다.

 

#. KeyChain 또는 AndroidKeystore Provider 중에 선택

시스템 수준의 사용자 인증 정보를 원하는 경우 KeyChain API를 사용합니다. 앱에서 KeyChain API를 통한 사용자 인증 정보 사용을 요청하는 경우, 사용자는 설치된 사용자 인증 정보 중에서 앱이 액세스 할 수 있는 사용자 인증 정보를 시스템 제공 UI를 통해 선택합니다. 이렇게 하면 사용자 동의를 받아 여러 앱이 동일한 사용자 인증 정보 집합을 사용할 수 있습니다.

개별 앱이 전용으로 액세스 할 수 있는 고유한 사용자 인증 정보를 저장할 수 있게 하려면 Android Keystore 제공자를 사용합니다. Android Keystore 제공자를 통해 앱에서 전용으로 사용할 수 있는 사용자 인증 정보를 관리할 수 있을 뿐만 아니라 KeyChain API가 시스템 수준의 사용자 인증 정보에 제공하는 것과 동일한 보안 이점을 얻을 수 있습니다. 이 방법은 사용자 인증 정보를 선택하기 위한 사용자 상호작용이 필요 없습니다.

 

#. Android Keystore Provider 사용

이 기능을 사용하려면 표준 KeyStore 및 KeyPairGenerator 또는 KeyGenerator 클래스와 Android 4.3(API 수준 18)에서 도입된 AndroidKeyStore 제공자를 함께 사용합니다.

AndroidKeyStore는 KeyStore.getInstance(type) 메서드에서 사용하기 위해 KeyStore 유형으로 등록되고, KeyPairGenerator.getInstance(algorithm, provider) 및 KeyGenerator.getInstance(algorithm, provider) 메서드에서 사용하기 위해 제공자로 등록됩니다.

 

#. 새 비공개 키 생성

새 PrivateKey를 생성하려면 자체 서명된 인증서에 사용할 초기 X.509 속성도 지정해야 합니다. 나중에 KeyStore.setKeyEntry를 사용하여 이 인증서를 CA(인증 기관)에서 서명한 인증서로 바꿀 수 있습니다.

키를 생성하려면 KeyPairGenerator와 KeyPairGeneratorSpec을 함께 사용합니다.

    /*
     * Generate a new EC key pair entry in the Android Keystore by
     * using the KeyPairGenerator API. The private key can only be
     * used for signing or verification and only with SHA-256 or
     * SHA-512 as the message digest.
     */
    val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(
            KeyProperties.KEY_ALGORITHM_EC,
            "AndroidKeyStore"
    )
    val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder(
            alias,
            KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
    ).run {
        setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
        build()
    }

    kpg.initialize(parameterSpec)

    val kp = kpg.generateKeyPair()    

 

#. 새 보안 비밀키 생성

키를 생성하려면 KeyGenerator와 KeyGenParameterSpec을 함께 사용합니다.

 

#. 보다 안전하게  암호화된 키 가져오

Android 9(API 수준 28) 이상에서는 ASN.1로 인코딩된 키 형식을 사용하여 암호화된 키를 Keystore로 안전하게 가져올 수 있습니다. 그런 다음, Keymaster가 Keystore의 키를 복호화하므로 키의 내용이 기기의 호스트 메모리에 일반 텍스트로 표시되지 않습니다. 이 프로세스는 추가적인 키 복호화 보안을 제공합니다.

★ 참고: 이 기능은 Keymaster 4 이상과 함께 제공되는 기기에서만 지원됩니다.

암호화된 키를 Keystore로 안전하게 가져오도록 지원하려면 다음 단계를 완료하세요.

▶ PURPOSE_WRAP_KEY 용도로 사용되는 키 쌍을 생성합니다. 이 키 쌍에 증명도 추가하는 것이 좋습니다.

▶ 신뢰할 수 있는 서버나 컴퓨터에서 SecureKeyWrapper에 포함되어야 하는 ASN.1 메시지를 생성합니다.

wrapper에는 다음 스키마가 있습니다.

KeyDescription ::= SEQUENCE {
        keyFormat INTEGER,
        authorizationList AuthorizationList
    }

    SecureKeyWrapper ::= SEQUENCE {
        wrapperFormatVersion INTEGER,
        encryptedTransportKey OCTET_STRING,
        initializationVector OCTET_STRING,
        keyDescription KeyDescription,
        secureKey OCTET_STRING,
        tag OCTET_STRING
    }    

▶ WrappedKeyEntry 객체를 생성하고 ASN.1 메시지를 바이트 배열로 전달합니다.

▶ 이 WrappedKeyEntry 객체를 setEntry() 오버로드에 전달합니다. 이 메서드는 Keystore.Entry 객체를 허용합니다.

 

#. 키 저장소 항목 작업

AndroidKeyStore 제공자 사용은 모든 표준 KeyStore API에서 발생합니다.

 

#. 항목 나열

aliases() 메서드를 호출하여 키 저장소의 항목을 나열합니다.

    /*
     * Load the Android KeyStore instance using the
     * "AndroidKeyStore" provider to list out what entries are
     * currently stored.
     */
    val ks: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply {
        load(null)
    }
    val aliases: Enumeration<String> = ks.aliases()    

 

#. 데이터 서명 및 확인

키 저장소에서 KeyStore.Entry를 가져오고 sign() 등의 Signature API를 사용하여 데이터에 서명합니다.

    /*
     * Use a PrivateKey in the KeyStore to create a signature over
     * some data.
     */
    val ks: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply {
        load(null)
    }
    val entry: KeyStore.Entry = ks.getEntry(alias, null)
    if (entry !is KeyStore.PrivateKeyEntry) {
        Log.w(TAG, "Not an instance of a PrivateKeyEntry")
        return null
    }
    val signature: ByteArray = Signature.getInstance("SHA256withECDSA").run {
        initSign(entry.privateKey)
        update(data)
        sign()
    }    

마찬가지로, verify(byte[]) 메서드를 사용하여 데이터를 확인합니다.

   /*
     * Verify a signature previously made by a PrivateKey in our
     * KeyStore. This uses the X.509 certificate attached to our
     * private key in the KeyStore to validate a previously
     * generated signature.
     */
    val ks = KeyStore.getInstance("AndroidKeyStore").apply {
        load(null)
    }
    val entry = ks.getEntry(alias, null) as? KeyStore.PrivateKeyEntry
    if (entry == null) {
        Log.w(TAG, "Not an instance of a PrivateKeyEntry")
        return false
    }
    val valid: Boolean = Signature.getInstance("SHA256withECDSA").run {
        initVerify(entry.certificate)
        update(data)
        verify(signature)
    }    

 

#. 키 사용을 위한 사용자 인증 도구

키를 생성하거나 AndroidKeyStore로 가져올 때, 사용자가 인증된 경우에만 키 사용을 승인하도록 지정할 수 있습니다. 사용자는 보안 잠금 화면의 사용자 인증 정보(패턴/PIN/비밀번호, 지문)의 일부를 사용하여 인증됩니다.

이 기능은 고급 보안 기능이며, 일반적으로 키 생성/가져오기 이후(이전이나 도중은 아님) 애플리케이션 프로세스가 손상되더라도 키 사용을 위한 사용자 인증 요구사항이 무시되어서는 안 된다는 요구사항이 있는 경우에만 유용합니다.

사용자가 인증된 경우에만 키 사용을 승인하는 경우, 다음 두 가지 모드 중 하나로 작동하도록 구성됩니다.

▶ 사용자 인증을 통해 일정 기간 동안 키 사용을 승인합니다. 이 모드의 모든 키는 사용자가 보안 잠금 화면의 잠금을 해제하거나 KeyguardManager.createConfirmDeviceCredentialIntent 흐름을 사용하여 보안 잠금 화면 사용자 인증 정보를 확인하는 즉시 사용이 승인됩니다. 승인이 유효한 기간은 키마다 다르며, 키를 생성하거나 가져올 때 setUserAuthenticationValidityDurationSeconds를 사용하여 지정됩니다. 이러한 키는 보안 잠금 화면이 사용 설정된 경우에만 생성하거나 가져올 수 있습니다(KeyguardManager.isDeviceSecure() 참조). 보안 잠금 화면을 사용할 수 없거나(None, Swipe 또는 사용자를 인증하지 않는 기타 모드로 재구성됨) 기기 관리자 등이 강제로 재설정하면 키가 영구적으로 무효화됩니다.

▶ 사용자 인증을 통해 하나의 키와 관련된 특정 암호화 작업을 승인합니다. 이 모드에서는 이러한 키와 관련된 각 작업을 사용자가 개별적으로 승인해야 합니다. 현재, 이러한 승인의 유일한 수단은 지문 인증(FingerprintManager.authenticate)뿐입니다. 최소 하나 이상의 지문이 등록된 경우에만 키를 생성하거나 가져올 수 있습니다(FingerprintManager.hasEnrolledFingerprints 참조). 지문을 새로 등록하거나 모든 지문을 등록 해제하면 키가 영구적으로 무효화됩니다.

 

#. 지원되는 알고리즘

▶ Cipher

▶ KeyGenerator

▶ KeyFactory

▶ KeyPairGenerator

▶ Mac

▶ Signature

▶ SecretKeyFactory

 

#. Cipher

알고리즘 지원 버전(API 수준) 메모
AES/CBC/NoPadding 23 이상  
AES/CBC/PKCS7Padding 23 이상  
AES/CTR/NoPadding 23 이상  
AES/ECB/NoPadding 23 이상  
AES/ECB/PKCS7Padding 23 이상  
AES/GCM/NoPadding 23 이상 12바이트 길이 IV만 지원됩니다.
RSA/ECB/NoPadding 18 이상  
RSA/ECB/PKCS1Padding 18 이상  
RSA/ECB/OAEPWithSHA-1AndMGF1Padding 23 이상  
RSA/ECB/OAEPWithSHA-224AndMGF1Padding 23 이상  
RSA/ECB/OAEPWithSHA-256AndMGF1Padding 23 이상  
RSA/ECB/OAEPWithSHA-384AndMGF1Padding 23 이상  
RSA/ECB/OAEPWithSHA-512AndMGF1Padding 23 이상  
RSA/ECB/OAEPPadding 23 이상  

 

#. KeyGenerator

알고리즘 지원 버전(API 수준) 메모
AES 23 이상 지원되는 크기: 128, 192, 256
HmacSHA1 23 이상
  • 지원되는 크기: 8--1024(포함), 8의 배수여야 함
  • 기본 크기: 160
HmacSHA224 23 이상
  • 지원되는 크기: 8--1024(포함), 8의 배수여야 함
  • 기본 크기: 224
HmacSHA256 23 이상
  • 지원되는 크기: 8--1024(포함), 8의 배수여야 함
  • 기본 크기: 256
HmacSHA384 23 이상
  • 지원되는 크기: 8--1024(포함), 8의 배수여야 함
  • 기본 크기: 384
HmacSHA512 23 이상
  • 지원되는 크기: 8--1024(포함), 8의 배수여야 함
  • 기본 크기: 512

 

#. KeyFactory

알고리즘 지원 버전(API 수준) 메모
EC 23 이상 지원되는 키 사양: KeyInfo(비공개 키 전용), ECPublicKeySpec(공개 키 전용), X509EncodedKeySpec(공개 키 전용)
RSA 23 이상 지원되는 키 사양: KeyInfo(비공개 키 전용), RSAPublicKeySpec(공개 키 전용), X509EncodedKeySpec(공개 키 전용)
 
 
#. Keystore
KeyStore는 KeyPairGenerator  KeyGenerator와 동일한 키 유형을 지원합니다.
 
#. KeyPairGenerator
알고리즘 지원 버전(API 수준) 메모
DSA 19–22  
EC 23 이상
  • 지원되는 크기: 224, 256, 384, 521
  • 지원되는 명명된 곡선: P-224(secp224r1), P-256(secp256r1 및 prime256v1), P-384(secp384r1), P-521(secp521r1)
API 수준 23 이전에는 'RSA' 알고리즘으로 초기화된 KeyPairGeneratorSpec의 KeyPairGenerator를 사용하여 EC 키를 생성할 수 있으며, 키 유형은 setKeyType(String)을 사용하여 'EC'로 설정됩니다. EC 곡선 이름은 이 방법으로 지정할 수 없으며, 요청한 키 크기에 따라 NIST P 곡선이 자동으로 선택됩니다.
RSA 18 이상
  • 지원되는 크기: 512, 768, 1024, 2048, 3072, 4096
  • 지원되는 공개 지수: 3, 65537
  • 기본 공개 지수: 65537
 
#. Mac
알고리즘 지원 버전(API 수준) 메모
HmacSHA1 23 이상  
HmacSHA224 23 이상  
HmacSHA256 23 이상  
HmacSHA384 23 이상  
HmacSHA512 23 이상

 

#. Signature

알고리즘 지원 버전(API 수준) 메모
MD5withRSA 18 이상  
NONEwithECDSA 23 이상  
NONEwithRSA 18 이상  
SHA1withDSA 19–22  
SHA1withECDSA 19 이상  
SHA1withRSA 18 이상  
SHA1withRSA/PSS 23 이상  
SHA224withDSA 20–22  
SHA224withECDSA 20 이상  
SHA224withRSA 20 이상  
SHA224withRSA/PSS 23 이상  
SHA256withDSA 19–22  
SHA256withECDSA 19 이상  
SHA256withRSA 18 이상  
SHA256withRSA/PSS 23 이상  
SHA384withDSA 19–22  
SHA384withECDSA 19 이상  
SHA384withRSA 18 이상  
SHA384withRSA/PSS 23 이상  
SHA512withDSA 19–22  
SHA512withECDSA 19 이상  
SHA512withRSA 18 이상  
SHA512withRSA/PSS 23 이상

 

#. SecretKeyFactory

알고리즘 지원 버전(API 수준) 메모
AES 23 이상 지원되는 키 사양: KeyInfo
HmacSHA1 23 이상 지원되는 키 사양: KeyInfo
HmacSHA224 23 이상 지원되는 키 사양: KeyInfo
HmacSHA256 23 이상 지원되는 키 사양: KeyInfo
HmacSHA384 23 이상 지원되는 키 사양: KeyInfo
HmacSHA512 23 이상 지원되는 키 사양: KeyInfo

 

#. Sample

Android AsymmetricFingerprintDialogAndroid BasicAndroidKeyStore 및 Android ConfirmCredential샘플은 이 페이지에서 설명한 API의 사용 사례를 자세히 보여줍니다.

 

Reference

1. https://developer.android.com/training/articles/keystore?hl=ko

반응형
반응형

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와 함께 IntentBinder 또는 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

반응형

+ Recent posts