본문 바로가기
프로그래밍 놀이터/안드로이드, Java

[android 보안] 권한 #1

by 돼지왕 왕돼지 2018. 4. 16.
반응형

[android 보안] 권한



출처 : Android Security Internals 2장

/data/system/packages.xml, /etc/group, /etc/permission/platform.xml, /system/priv-app/, ACCESS_ALL_EXTERNAL_STORAGE, ACCESS_NETWORK_STATE, ahead of time, AID_INET, AID_NET_RAW, android runtime, android_filesystem.config.h, AOT, Art, assign-permission, camera, Capability, CAP_NET_ADMIN, CAP_NET_RAW, cert, Compile, CONFIG_ANDROID_PARANOID_NETWORK, Copy-On-Write, dalvik, Dangerous, effective uid, exec, fork, getPackageInfo, GET_ACCOUNTS, gid, gid 기능부여, inet, inet 그룹, init.rc, internet 권한, Main, NET_ADMIN, Normal, package, package name, packageinfo, PackageManager, packages.xml, Paranoid Network Security, perms, platform.xml, pm list permissions, PPID, priv-app, process name, protection level, PS, READ_SMS, real uid, seinfo, selinux, setCapabilities, setgroupsIntArray, setrlimitsFromArray, Signature, signatureOrSystem, specialization, supplementary gid, system partition, targetsdkversion, UID, user id, vm 프로세스, zygote, [android 보안] 권한, 권한, 권한 보호 수준, 권한 할당, 권한의 본질, 동일 개발자, 디바이스 노드, 디바이스 제어, 롤리팝, 루트 실행, 명시적 권한 요청, 보조 gid, 분화, 사용자 데이터, 새로 추가된 권한, 소켓 접근, 시스템 앱, 시스템 이미지, 안드로이드 런타임, 암묵적 승인, 앱 설치, 앱 설치 갱신 제거, 접근 모드, 지역 소켓, 커널 수준 적용, 키 서명, 파라노이드 네트워크 보안, 파일, 파일 소유자, 프로세스 속성 할당, 할당 권한

개요 목차


2. 권한

     2.1. 권한의 본질


     2.2. 권한 요청


     2.3. 권한 관리


     2.4. 권한 보호 수준

          2.4.1. normal

          2.4.2. dangerous

          2.4.3. signature

          2.4.4. signatureOrSystem


     2.5.권한 할당

          2.5.1. 권한과 프로세스 속성

          2.5.2. 프로세스 속성 할당 


     2.6. 권한 적용

          2.6.1. 커널 수준 적용

          2.6.2. 네이티브 데몬 수준 적용

          2.6.3. 프레임워크 수준 적용


     2.7. 시스템 권한

          2.7.1. signature 권한

          2.7.2. development 권한


     2.8. 공유 사용자 ID


     2.9. 커스텀 권한


     2.10. 공개 컴포넌트와 비공개 컴포넌트


     2.11. 액티비티 권한과 서비스 권한


     2.12. 브로드캐스트 권한


     2.13. 콘텐트 제공자 권한

          2.13.1. 정적 제공자 권한

          2.13.2. 동적 제공자 권한


     2.14. 팬딩 인텐트


     2.15. 요약





2.1. 권한의 본질


-

이전 버전에서는 권한이 필요하지 않았던 기능에 새로 적용되는 내장된( built-in )권한은 앱 매니페스트에 지정된 targetSdkVersion 값에 따라 적용되었다.

새로운 권한이 소개되기 전의 안드로이드 버전을 타깃팅하는 앱은 새로운 권한에 대해서는 알 수 없으므로 권한을 별도로 요청하지 않아도 암묵적으로 승인받았다.

그러나 암묵적으로 승인받은 권한도 앱 설치 화면의 권한 목록에 나타나므로 사용자는 이 앱이 사용하는 권한을 알 수 있다.

권한이 적용되는 버전을 앱 타깃팅할 경우에는 명시적으로 권한을 요청해야 한다.



-

pm list permissions 명령은 현재 시스템이 알고 있는 권한을 나열한다.

이 명령에 -f 파라미터를 추가하면 정의한 패키지, 레이블, 설명, 보호 수준 등 추가적인 정보를 볼 수 있다.



-

내장된 권한은 android 패키지에서 정의되므로 권한명이 android.permission 으로 시작한다.





2.2. 권한 요청





2.3. 권한 관리


-

권한은 앱을 설치할 때 시스템 “패키지 매니저” 서비스에 의해 각 앱에 부여된다.

패키지 매니저는 미리 설치된 패키지와 사용자가 설치한 패키지들의 설치 경로, 버전, 서명 인증서, 할당된 권한 등에 대한 데이터베이스와 함께 각 디바이스에서 정의된 권한 목록을 모두 관리한다.



-

패키지 데이터베이스는 /data/system/packages.xml 이라는 XML 파일에 저장되어 있으며 앱을 설치, 갱신, 제거할 때마다 이 파일도 함께 갱신된다.



-

packages.xml 에서...

각 패키지 항목은 <package> 요소로 나타나며, 여기에는 할당된 UID(user ID), 서명 인증서(<cert> 태그), 할당된 권한(<perms> 태그의 자식 항목)이 들어 있다.



-

프로그램 코드에서 설치된 패키지에 대한 정보를 가져오려면 PackageManager 클래스의 getPackageInfo() 메서드를 사용하는데,

이 메서드를 호출하면 packages.xml 의 <package> 태그 안에 있는 정보를 담은 PackageInfo 객체가 반환된다.





2.4. 권한 보호 수준


-

권한의 보호 수준(protection level)은 “권한에 따른 잠재적인 위험을 명백히 나타내고 시스템이 권한 허용 여부를 판단할 때 따라야 할 절차를 나타낸다”.

즉 보호 수준에 따라 권한을 부여할지가 결정된다.



* 2.4.1. normal


-

기본값으로서 시스템이나 다른 앱에 위험이 적은 권한을 정의한다.

보호 수준이 normal 인 경우 사용자의 동의 없이 자동으로 권한이 부여되며, ACCESS_NETWORK_STATE(앱이 네트워크 정보에 접근할 수 있게 한다)와 GET_ACCOUNTS(앱이 계정 서비스에 등록된 계정 목록에 접근할 수 있게 한다) 권한 등이 여기에 해당한다.




* 2.4.2. dangerous


-

보호 수준이 dangerous 인 권한은 사용자 데이터에 접근하거나 어느 정도 디바이스를 제어할 수 있으며, READ_SMS(앱이 SMS 메시지를 읽을 수 있게 한다.)와 CAMERA(앱이 카메라 장치에 접근할 수 있게 한다) 권한이 여기에 속한다.

안드로이드는 dangerous 권한을 부여하기 전에 요청한 권한에 대한 정보를 보여주는 확인 다이얼로그를 띄운다.

안드로이드에서는 요청한 모든 권한을 설치할 때 부여해야 하므로, 사용자는 dangerous 권한을 요청하는 앱을 설치하도록 승인하거나 설치를 취소할 수 있다.

( MOS (Marshmellow) 부터는 Runtime permission 이 등장해서 이야기가 조금 달라졌다. )




* 2.4.3. signature


-

signature 권한은 권한을 선언한 앱과 동일한 키로 서명된 앱에만 부여된다.

앱이나 플랫폼 개발자만 갖고 있는 암호 키를 소유하고 있어야 하므로 이 권한 수준이 “가장 강력”하다.

따라서 signature 권한을 사용하는 앱들은 일반적으로 동일한 개발자에 의해 관리된다.

내장된 signature 권한은 일반적으로 디바이스를 관리하는 시스템 앱이 사용하며, NET_ADMIN(네트워크 인터페이스, IPSec 등을 설정한다)과 ACCESS_ALL_EXTERNAL_STORAGE(모든 다중 사용자 외부 저장소에 접근한다.) 권한이 여기에 속한다.




* 2.4.4. signatureOrSystem


-

이 보호 수준의 권한은 너무 강한 signature 권한 수준에 대한 약간의 절충안이다.

이 권한은 시스템 이미지에 포함되어 있거나 권한을 선언한 앱과 동일한 키로 서명한 앱에 부여된다.

이 권한 수준을 이용하면 서명 키를 공유하지 않고도 안드로이드 디바이스에 미리 설치되는 앱에 권한을 부여할 수 있다.

안드로이드 버전 4.3(JellyBean MR2)까지는 system 파티션에 설치되는 모든 앱에 자동으로 signatureOrSystem 권한이 부여되었지만, 안드로이드 버전 4.4(Kitkat)부터는 signatureOrSystem 보호 수준의 권한을 가지려면 앱이 /system/priv-app/ 디렉터리에 설치되어 있어야 한다.






2.5. 권한 할당


-

앱이나 시스템 서비스와 같은 상위 컴포넌트는 패키지 매니저에 문의해 앱에 어떤 권한이 할당되어 있는지 확인해서 접근을 허용할지를 판단한다.

네이티브 데몬과 같은 하위 컴포넌트는 일반적으로 패키지 매니저에 접근하지 않으므로 프로세스에 할당된 UID/GID 및 보조 GID(Supplementary GID)를 확인해 접근할 권한이 있는지를 판단한다.

디바이스 파일, 유닉스 도메인 소켓(지역 소켓), 네트워크 소켓 등의 시스템 자원에 대한 접근은 자원의 소유자와 접근 모드 및 접근 프로세스의 UID/GID 에 기반해 커널에서 적용한다.



* 2.5.1. 권한과 프로세스 속성


-

실제(real) 및 유효(effective) UID/GID 와 보조 GID 가 가장 중요하다.



-

안드로이드 앱에서 설치 시에 고유한 UID 가 할당되어 앱이 전용 프로세스에서 실행된다.

앱이 실행될 때 프로세스의 UID/GID 는 설치자(패키지 매니저 서비스)가 할당한 앱 UID 로 설정된다.

앱에 추가적인 권한이 할당되어 있으면 이 권한들은 GID 에 대응되어 보조 GID 가 프로세스에 할당된다.

내장된 권한에 대한 GID 대응은 /etc/permission/platform.xml 파일에 정의되어 있다.



-

platform.xml 에서...

<permission> 과 반대로, <assign-permission> 태그는 UID 에 권한을 할당한다.

시스템 프로세스는 특정 패키지에 속하지 않으므로, 특정 UID 로 실행되는 시스템 프로세스에 상위 수준의 권한을 할당하기 위해 사용된다.



-

안드로이드에는 /etc/group 파일이 없고, 대신 그룹명과 GID 의 대응은 android_filesystem.config.h 헤더 파일에 정적으로 정의되어 있다.


android_filesystem_config.h 파일에서는 안드로이드 시스템 핵심 디렉터리와 파일의 소유자, 접근 모드, (실행 파일의 경우) 연결된 기능(Capability)도 정의하고 있다.



-

패키지 매니저는 시스템 부팅 시 platform.xml 파일을 읽고 권한 및 권한에 연결된 GID 목록을 관리하며, 앱 설치 시 패키지에 권한을 부여할 때 각 권한에 연결된 GID 가 있는지를 검사한다.

연결된 GID 가 있으면 앱에 연결된 보조 GID 목록에 해당 GID 를 추가한다.

보조 GID 목록은 Packages.list 파일의 마지막 필드에 추가된다.




* 2.6.2. 프로세스 속성 할당


-

각 앱 프로세스는 사실 앱의 바이트코드를 실행하는 달빅 VM 프로세스일 뿐이다.

그러나 앱 메모리 사용량과 시작 시간을 줄이기 위해 안드로이드는 앱을 실행할 때마다 달빅 VM 프로세스를 새로 시작하는 것이 아니라, zygote 라는 부분 초기화된 프로세스를 포크(fork)해 실행한다.

그러나 네이티브 프로세스를 시작할 때와는 달리 exec() 함수를 호출하지 않고 자바 클래스에 명시된 main() 함수를 바로 실행한다.


범용 zygote 프로세스가 구체적인 앱 프로세스로 변화하므로, 이 과정을 분화(specialization)이라고 부른다.

포크된 프로세스는 zygote 프로세스 이미지를 상속받으므로 대부분의 핵심 프레임워크 자바 클래스가 미리 로딩되어 있다.

이 클래스들은 결코 변하지 않고 리눅스가 프로세스를 포크할 때에는 쓰기 연산 시 복사(copy-on-write) 메커니즘을 사용하므로, 모든 zygote 의 자식 프로세스(즉, 앱 프로세스)는 동일한 프레임워크 자바 클래스를 공유한다.



-

zygote 프로세스는 init.rc 초기화 스크립트에 의해 이미 시작되어 있으며, 같은 이름의 zygote 유닉스 도메인 소켓(unix domain socket, uds)로 명령을 받는다.

새로운 앱 프로세스를 시작하라고 요청받으면, zygote 는 자신을 포크하고 자식 프로세스는 자신을 분화하기 위해 여러 가지 작업을 한다.

( 한국어 번역본 책의 57p 에는 dalvik_system_Zygote.cpp 의 forkAndSpecializeCommon() 메서드의 발췌코드가 있다. )



-

자식 프로세스는 먼저 setgroupsIntArray() 를 통해 권한에 맞는 보조 GID 들을 설정한다.

그 다음 setrlimitsFromArray() 를 통해 리소스 한계를 설정한다.

그 다음 실제, 유효, 저장된 사용자 및 그룹 ID 를 설정한다.



-

부모 프로세스인 zygote 와 마찬가지로 자식 프로세스는 처음에 루트로 실행되므로 자신의 리소스 한계 및 모든 프로세스 속성을 바꿀 수 있다.

프로세스 속성을 새로 설정한 후 자식 프로세스는 할당된 UID/GID 로 실행되며, 저장된 사용자 ID 가 0이 아니므로 루트로 돌아갈 수는 없다.



-

이후 setCapabilities() 를 통해 자신의 기능을 설정한다.

그 다음 미리 정의된 제어 그룹 중 하나에 자신을 추가해 스케줄링 정책을 설정한다.

프로세스명(프로세스 목록에 나타나는 이름으로, 일반적으로 패키지 명을 사용한다. )과 seinfo태그(SELinux 에서 사용)를 설정한다.

마지막으로 요청에 따라 디버깅 기능을 활성화한다.



-

안드로이드 4.4(Kitkat) 에서는 안드로이드 런타임(Android Runtime, ART)이라는 실험적인 런타임을 새로 소개했으며, ART 는 안드로이드 롤리팝(5.0)에서 달빅을 대체했다.

ART 는 앱 설치 시 Ahead-Of-Time (AOT) 컴파일하는 기능 등 현재의 실행 환경에 많은 변화를 가져왔지만, zygote 기반 앱 프로세스를 실행하는 달빅의 모델을 그대로 사용한다.



-

zygote 와 앱 프로세스의 관계는 ps 명령으로 가져온 프로세스 목록을 보면 확실히 알 수 있다.


PID 는 프로세스의 ID, PPID 는 부모 프로세스의 ID, NAME 은 프로세스명을 나타낸다.


모든 앱 프로세스의 부모 프로세스는 zygote 프로세스이다.

모든 프로세스는 내장된 사용자( radio, nfc 등 )나 설치 시에 자동으로 할당된 사용자 등 전용 사용자 계정으로 실행된다.

프로세스명은 각 앱의 패키지명으로 설정된다.






2.6. 권한 적용


-

앱 프로세스는 zygote 프로세스가 포크되면서 UID, GID, 보조 GID 를 할당받는다.

커널과 시스템 데몬은 이 프로세스 식별자를 이용해 어떤 시스템 자원이나 기능에 접근을 허용할지 판단한다.



* 2.6.1. 커널 수준 적용


-

일반 파일, 디바이스 노드, 지역 소켓에 대한 접근은 다른 리눅스 시스템에서와 동일하게 제어된다.

다만 안드로이드에서는 네트워크 소켓을 생성할 프로세스는 inet 그룹에 속해 있어야 한다는 점이 추가되었다.

안드로이드 커널에 추가된 특징을 “파라노이드 네트워크 보안(Paranoid Network Security)” 라고 하며, 안드로이드 커널에서 추가적인 검사를 통해 구현한다.



-

AID_INET (GID 3003, 그룹명 inet)에 속하지 않고 CAP_NET_RAW 기능(이 기능이 있어야 RAW와 PACKET 소켓을 사용할 수 있다.)이 없는 호출자 프로세스에는 접근 거부 오류가 발생한다.

비-안드로이드 커널에서는 CONFIG_ANDROID_PARANOID_NETWORK 를 정의하지 않으므로 inet 그룹에 속하지 않더라도 소켓을 생성할 수 있다.


앱 프로세스가 inet 그룹에 속하려면 INTERNET 권한을 가져야 하고, 결국 INTERNET 권한이 있는 프로세스만 네트워크 소켓을 생성할 수 있게 된다.



-

안드로이드 커널은 특정 GID 로 수행되는 프로세스에 어떤 기능을 부여한다.

예를 들어 AID_NET_RAW(GID 3004) 그룹으로 실행되는 프로세스는 CAP_NET_RAW 기능을

AID_NET_ADMIN(GID 3005) 그룹으로 실행되는 프로세스는 CAP_NET_ADMIN 기능을 부여받는다.



-

파라노이드 네트워크 보안은 블루투스 소켓 및 커널 터널링 드라이버(VPN에서 사용)에 대한 접근을 제어하기 위해서도 사용된다.

커널이 특별하게 다루는 안드로이드 GID 들의 전체목록은 커널 소스 트리의 include/linux/android_aid.h 에서 확인할 수 있다.




* 2.6.2. 네이티브 데몬 수준 적용


-

프로세스 간 통신을 위해 안드로이드에서는 바인더를 선호하지만, 하위 수준의 네이티브 데몬들은 유닉스 도메인 소켓(지역 소켓)을 종종 사용한다.

유닉스 도메인 소켓은 파일시스템에 노드로 표현되므로 표준 파일시스템 권한을 사용해 접근을 제어할 수 있다.



-

대부분의 소켓은 소유자와 그룹만 접근할 수 있는 모드로 생성되므로 다른 UID/GID 로 실행되는 클라이언트는 소켓에 연결할 수 없다.

시스템 데몬이 사용하는 지역 소켓은 init.rc 에 정의되어 있으며, 부팅 시 init 에 의해 지정된 접근 모드로 생성된다.



-

(한국어 번역판 60p에 init.rc 에 정의된 vold 데몬 항목이 첨부되어 있다.)

볼륨 관리 데몬은 0600 접근 모드로 vold 라는 소켓을 선언하며, 이파일의 소유자는 root, 그룹은 mount 로 설정되어 있다.

mount 그룹(AID_MOUNT, GID 1009) 에 속한 프로세스들은 슈퍼유저로 실행되지 않아도 지역 소켓을 통해 명령을 보낼 수 있다.

안드로이드 데몬을 위한 지역 소켓은 /dev/socket/ 디렉터리에 만들어진다.



-

지역 소켓에 연결할 클라이언트의 자격증명은 커널이 검사하므로 사용자수준 프로세스가 위조할 수 없다.

이렇게 함으로써 네이티브 데몬은 특정 클라이언트만 연결할 수 있도록 정밀하게 제어할 수 있다.



-

지역 소켓 연결 기능은 android.net.LocalSocket 클래스로 구현되어 있어서 자바 앱에서도 사용할 수 있으며,

상위 수준 시스템 서비스가 JNI 코드를 사용하지 않고도 네이티브 데몬과 통신할 수 있게 해준다.




* 2.6.3. 프레임워크 수준 적용


-

정적 권한은 선언적 보안(Declarative Security)의 한 사례다.

선언적 보안을 이용할 때에는 역할(Role)및 권한 등 보안 속성이 컴포넌트 자체가 아닌 컴포넌트의 메타 파일(안드로이드는 AndroidManifest.xml)에 들어가며, 컨테이너나 런타임 환경에 의해 적용된다.

선언적 보안은 보안 결정사항과 비지니스 로직을 분리시키는 장점은 있지만, 컴포넌트 안에서 보안 검사를 수행하는 것보다 융통성이 떨어진다.



-

권한을 동적으로 적용하려면 구현할 작업량은 더 많아지지만 접근은 더욱 정밀하게 제어할 수 있다.

동적 권한 적용은 런타임 환경이 아닌 각 컴포넌트가 보안 결정을 내리므로, 명령적(Imperative) 보안의 사례라고 할 수 있다.



** 동적 권한 적용


-

권한 확인 연산은 상당히 자주 호출되므로, 안드로이드는 android.content.Context 클래스를 통해 권한을 확인하는 다양한 헬퍼 메서드를 제공한다.



-

Context.checkPermission(String permission, int pid, int uid) 메서드는 전달한 UID 가 해당 권한이 있으면 PERMISSION_GRANTED  를 반환하고, 해당 권한이 없으면 PERMISSION_DENIED 를 반환한다.

호출자가 루트나 시스템이면 자동으로 허용된다.

요청한 권한을 호출자 앱이 선언한 경우에는 실제 권한을 조사하지 않고 접근을 허용한다.

그렇지 않은 경우에는 타깃 컴포넌트가 공개인지(exported 되었는지) 비공개인지를 메서드가 검사해, 만약 비공개 컴포넌트인 경우에는 접근을 거부한다.

마지막으로 호출자 프로세스가 해당 권한을 갖고 있는지 패키지 매니저 서비스에 문의한다.



-

동일한 앱을 여러 사용자가 설치한 경우 동일 앱에 여러 UID 가 부여될 수 있다.



-

checkCallingOrSelfPermission(String permission) 메서드는 Binder.getCallingUid() 와 Binder.getCallingPid() 메서드를 호출해 가져온 값을 이용해서 checkPermission(String permission, int pid, int uid) 메서드를 호출한다.



-

encorcePermission(String permission, int pid, int uid, String message) 메서드는 결과를 반환하지 않지만, 권한이 없는 경우 메시지를 담은 SecurityException 을 발생시킨다.



** 정적 권한 적용


-

권한을 검사하는 ActivityManagerService 는 지정된 인텐트를 찾아내어 타깃 컴포넌트가 연결된 권한 속성을 갖고 있는지를 확인한다.

그리고 타깃 컴포넌트가 어떤 권한을 요구하는 경우 패키지 매니저에 권한 검사를 의뢰한다.



** 액티비티와 서비스 권한 적용



** 콘텐트 제공자 권한 적용


-

콘텐트 제공자 권한은 컴포넌트 전체, 혹은 익스포트된 특정 URI 를 보호할 수 있으며, 읽기와 쓰기에 서로 다른 권한을 지정할 수 있다.



** 브로드캐스트 권한 적용


-

타깃 수신자가 필요한 권한을 갖고 있지 않으면 그 수신자에게 브로드케스트 메시지가 전달되지 않으며, 그렇다 하더라도 예외는 발생되지 않는다.

이와 반대로 브로드케스트 수신자도 자신에게 메시지를 보내려는 발신자 또한 어떤 권한을 갖고 있어야 한다고 요구할 수 있다.



-

필수 권한은 매니페스트 파일에 지정되거나 브로드캐스트를 등록할 때 동적으로 설정할 수 있다.

이 권한들도 브로드캐스트를 전달할 때 검사하며, 권한이 없다고 해서 SecurityException 이 발생되지는 않는다.

즉, 브로드캐스트를 전달하는 과정에서는 수신자가 권한을 지정하는 경우 발신자가 가져야 할 권한과 발신자가 권한을 지정하는 경우 수신자가 가져야 할 권한, 이렇게 두 가지 방식으로 검사한다.



** 보호된 브로드캐스트와 스티키 브로드캐스트


-

BOOT_COMPLETED 및 PACKAGE_INSTALLED 같은 시스템 브로드캐스트는 보호된(protected) 브로드캐스트로 선언되어 있으며 SYSTEM_UID, PHONE_UID, SHELL_UID, BLUETOOTH_UID 또는 루트 UID 로 실행되는 시스템 프로세스만 전송할 수 있다.

다른 UID 로 실행되는 프로세스가 보호된 브로드캐스트를 전송하기 위해 sendBroadcast 메서드를 호출하면 SecurityException 을 발생시킨다.



-

스티키(Sticky) 브로드캐스트는 브로드캐스트가 완료된 후에도 전송한 인텐트를 시스템이 유지하는데, 스티키 브로드케스트를 전송하는 프로세스는 BROADCAST_STICKY 권한을 갖고 있어야 한다.

만약 전송자가 이 권한을 갖고 있지 않으면 브로드캐스트는 전송되지 않고 SecurityException 이 발생한다.





반응형

댓글