[android 보안] 사용자 관리 #2
4.4. 사용자 메타데이터
-
안드로이드는 사용자 데이터를 /data/system/users/ 디렉터리에 저장한다.
이 디렉터리에는 사용자 디렉터리뿐만 아니라 사용자에 대한 메타데이터를 XML 형식으로 보관한다.
* 4.4.1. 사용자 리스트 파일
-
사용자는 할당된 사용자 ID 와 동일한 이름의 전용 “사용자 시스템 디렉터리” 와 사용자 ID 에 기반을 둔 파일명의 사용자 메타데이터를 저장한 XML 파일을 갖고 있다.
userlist.xml 파일은 시스템에 있는 모든 사용자에 대한 데이터를 갖고 있다.
-
보조 사용자와 제한된 프로필에는 10번부터 시작하는 ID 가 할당된다.
* 4.4.2. 사용자 메타데이터 파일
-
<user> 요소 속성
id
serialNumber
flags
사용자 종류를 나타내는 플래그
created
lastLoggedIn
icon
partial
사용자 초기화가 완료되지 않았음을 나타낸다.
pinHash
PIN 으로 보호하는 제한에 대한 솔트(salt)값을 포함한 SHA1+MD5 PIN 해시값
salt
PIN 으로 보호하는 제한에 대한 PIN 솔트값
failedAttempts
PIN 으로 보호하는 제한에 실패한 PIN 입력 횟수
lastAttemptMS
PIN 으로 보호하는 제한에 대한 최종 PIN 입력 시각
-
flags 속성이 사용자의 종류를 결정하는 가장 중요한 요소이다.
FLAG_PRIMARY ( 주사용자 )
FLAG_ADMIN ( 관리자 권한을 가진 사용자로 사용자 생성 삭제가 가능하다. )
FLAG_GUEST
FLAG_RESTRICTED ( 제한된 사용자 )
FLAG_INITIALIZED
-
주 사용자의 flag 속성은 19 ( FLAG_INITIALIZED | FLAG_ADMIN | FLAG_PRIMARY ).
보조 사용자의 flag 속성은 16 ( FLAG_INITIALIZED )
제한된 프로필의 flag 속성은 24 ( FLAG_INITIALIZED | FLAG_RESTRICTED ) 이다.
* 4.4.3. 사용자 시스템 디렉터리
-
사용자 ID 가 12인 보조 사용자의 경우 사용자 시스템 디렉터리는 /data/system/users/12/ 이다.
-
사용자 시스템 디렉터리 안의 accounts.db 는 온라인 계정 정보를 담고 있는 SQLite DB 이다.
appwidgets.xml 은 사용자가 홈 화면에 추가한 위젯에 대한 정보를 담고 있다.
device_policies.xml 파일은 현재 디바이스 정책을 설명한다.
gesture.key 와 password.key 파일은 각기 현재 선택된 잠금 화면의 패턴의 해시값과 PIN/패스워드의 해시값을 담고 있다.
inputmethod/ 디렉터리는 입력 방법에 대한 정보
photo.png 파일은 사용자의 프로필 사진을 저장
settings.db 파일은 사용자 고유의 시스템 설정값
wallpaper 파일은 현재 선택한 바탕화면 그림.
pacakge-restrictions.xml 파일은 사용자가 설치한 앱을 정의하고 그 앱의 상태 정보를 저장한다.
4.5. 사용자별 앱 관리
-
전용 계정 및 설정 외에도 각 사용자는 다른 사용자가 접근할 수 없는 자신만의 앱 데이터를 갖고 있다.
안드로이드에서는 각 앱에 사용자별 유효 UID 를 할당하고, 그 UID 가 소유한 전용 앱 데이터 디렉터리를 만들어 사용자 데이터를 격리시킨다.
* 4.5.1. 앱 데이터 디렉터리
-
안드로이드 APK 패키지를 /data/app/ 디렉터리에 복사해 설치하고, /data/data/ 디렉터리 밑에 각 앱의 전용 데이터 디렉터리를 생성한다.
다중 사용자 지원이 활성화될 때, 이 구조를 그대로 유지하면서 추가된 사용자를 지원하기 위해 약간 확장되었다.
하위 호환성을 위해 주 사용자의 앱 데이터는 여전히 /data/data/ 디렉터리에 저장된다.
-
만약 새로운 앱을 설치할 때 시스템에 다른 사용자가 있으면 패키지 매니저 서비스는 각 사용자별로 앱 데이터 디렉터리를 생성한다.
installd 데몬의 도움(mkuserdata 명령을 이용)을 받아 생성한다.
-
사용자 데이터 디렉터리는 사용자 ID 와 같은 이름으로 /data/user/ 밑에 들어간다.
디바이스 소유자 디렉터리 (0/) 는 /data/data/ 디렉터리에 심볼링 링크로 연결된다.
-
동일한 브라우저 앱에 대해 각각의 데이터 디렉터리를 갖고 있고, 각 디렉터리는 서로 다른 리눅스 사용자가 소유하고 있다.
-
디바이스 소유자와 사용자 ID 가 다른 보조 사용자가 브라우저 앱을 동시에 실행하면, u0_a16 과 u13_a16 리눅스 사용자로 실행되는 두 개의 프로세스가 실행된다.
* 4.5.2. 앱 공유
-
설치된 앱이 각 사용자마다 전용 데이터 디렉터리를 갖는 반면 APK 파일은 모든 사용자가 공유한다.
APK 파일은 /data/app/ 디렉터리로 복사되어 모든 사용자가 읽을 수 있다.
앱이 사용하는 공유 라이브러리는 /data/app-lib/<패키지명>/ 디렉터리로 복사되고 /data/users/<사용자 ID>/<패키지명>/lib/ 디렉터리에 심볼릭 링크된다.
각 앱의 최적화된 DEX 파일은 /data/dalvik-cache/ 디렉터리에 저장되어 모든 사용자가 공유한다.
따라서 일단 앱이 설치되면 디바이스의 모든 사용자가 접근할 수 있으며, 각 사용자의 앱 디렉터리가 자동으로 생성된다.
-
안드로이드는 각 사용자의 시스템 디렉터리에 package-restrictions.xml 파일을 만들어 사용자마다 서로 다른 앱이 설치된 것처럼 보이게 한다.
package-restrictions.xml 파일은 어떤 앱이 해당 사용자에게 활성화되어 있는지 여부를 표시한다.
-
<pkg> 태그의 inst 속성이 ‘false’로 설정되어 있으면 이 보조 사용자에게는 마치 이 앱이 설치되어 있지 않은 것처럼 보인다.
이 정보에 기반을 두고 패키지 매니저 서비스는 해당 패키지가 이 사용자에게는 설치되어 있지 않다고 표시하고, 이 패키지는 런처나 설정 화면의 앱 목록에서 보이지 않는다.
-
앱이 설치되어 있지만 정지된 상태로 표시될 수도 있다.
stopped 속성이 ‘true’ 로 설정되어 있으면 된다.
안드로이드에는 한 번도 실행되지 않은 앱에 대한 특별한 상태 (<pkg> 태그의 nl 속성)이 있다.
디바이스 소유자는 특정 사용자가 어떤 패키지를 사용하지 못하게 막을 수 있는데, 이 경우 blocked 속성이 ‘true’ 로 설정한다.
-
디바이스 소유자가 앱을 설치할 때 모든 사용자의 package-restrictions.xml 파일에는 inst=“false” 속성을 가진 <pkg> 태그가 추가된다.
다른 사용자가 동일한 앱을 설치하면 inst 속성이 제거되고 그 사용자에게 앱이 설치된 것으로 간주한다. ( 보조 사용자가 설치 과정을 시작하는 방법에 따라 /data/app/ 디렉터리에 있는 APK 파일이 변경될 수도 있다. 특히 앱 업데이트의 경우 APK 가 변경된다. )
-
제한된 프로필은 앱을 설치할 수 없지만, 만약 디바이스 소유자가 제한된 프로필에게 어떤 앱을 허용할 때에는 동일한 과정이 적용된다.
PackageManagerService.installExistingPackageAsUser() 메서드를 호출해 앱을 설치하면 패키지에 대한 설치 플래그를 설정하고, 이에 따라 pacakge-restrictions.xml 파일을 갱신한다.
-
사용자가 패키지를 제거하면 앱 데이터가 삭제되고 내부의 사용자별 패키지 설치 플래그는 ‘false’ 로 설정된다.
앱을 설치한 모든 사용자가 앱을 제거해야 APK 파일과 네이티브 라이브러리 디렉터리가 삭제된다.
디바이스 소유자는 앱 설정 화면의 “All” 탭을 통해 시스템에 설치된 모든 앱을 볼 수 있으며, 자신이 직접 설치하지 않은 앱도 전체 사용자로부터 제거할 수 있다.
[Uninstall for all users] 액션은 오버플로우(Overflow Menu)에 숨어 있으므로 실수로 이 액션을 선택하는 일은 없다.
-
이 방식의 유저 앱 관리는 큰 단점이 하나 있는데,
다른 사용자가 설치한 앱이라도 누구나 업데이트 할 수 있다는 점이다.
모든 사용자의 앱은 각자 고유한 데이터 디렉터리를 갖고 있으므로 업데이트에 새로운 권한이 추가되지 않는 한 일반적으로 이 체계는 문제가 되지 않는다.
안드로이드에서는 설치할 때 권한을 부여하므로, 어떤 사용자가 앱을 업데이트해서 사용자의 프라이버시에 영향을 주는 권한(가령 READ_CONTACTS와 같은 권한)을 허용하면 이 권한은 앱을 사용하는 모든 사용자에게 영향을 미친다.
다른 사용자에게는 앱에 새로운 권한이 부여된 사실이 통지되지 않으므로 시스템 설정에서 앱에 대한 상세 정보를 직접 확인하지 않는 한 다른 사용자들은 변경된 내용을 알아채지 못할 수도 있다.
-
안드로이드에서는 다중 사용자 지원을 처음으로 활성화하는 경우에만 이런 문제점에 대한 경고 메시지를 주고, 그 후로는 어떤 앱의 권한이 변경되어도 사용자에게 알려주지 않는다.
4.6. 외부 저장소
-
안드로이드 초기 몇 세대 디바이스들은 FAT 파일시스템으로 포맷된 SD카드를 마운트하는 방식으로 외부 저장소를 구현했기 때문에 외부 저장소를 종종 SD 카드라고 부르기도 했다.
그러나 외부 저장소의 정의는 더 광범위하게 단순히 불변의 포직스(POSIX) 권한 클래스(owner, group, others, all)와 모드(read, write,execute) 등을 지원하며 대소문자를 구분하지 않는 파일시스템이면 된다.
이 정의를 만족하면 어떻게 구현하든 상관없다.
* 4.6.1. 외부 저장소 구현
-
신형 디바이스는 에뮬레이션을 통해 외부 저장소를 구현하는데, SD 카드 슬롯이 아예 없는 경우도 있다.
넥서스 S (이 디바이스는 외부 저장소를 위한 전용 파티션을 사용한다.) 이후에 출시된 넥서스 디바이스는 모두 에뮬레이션을 통해 외부 저장소를 구현한다.
SD 카드가 없는 디바이스에서 외부 저장소는 기본 저장소에 있는 FAT 포맷된 파티션을 직접 마운트하거나 외부 저장소를 에뮬레이트하는 헬퍼 데몬을 이용해 구현한다.
-
안드로이드 버전 4.4(Kitkat)부터 WRITE_EXTERNAL_STORAGE 권한이 없는 앱도 외부 저장소에 있는 패키지 고유의 디렉터리(com.example.app 패키지의 경우 Android/data/com.example.app/)를 관리할 수 있게 되었다.(WRITE_EXTERNAL_STORAGE 권한은 카메라 사진, 비디오 등 외부 저장소에 있는 모든 데이터에 접근할 수 있게 허용한다.) 이 기능을 “합성된 권한(Synchronized Permission)” 이라고 하며, AOSP 에서는 FUSE 데몬에 기반을 두고 구현한다.
-
FUSE 데몬은 원시 디바이스 저장소를 에워싸고 지정된 권한 에뮬레이션 모드에 기반해 파일접근과 권한을 관리한다.
-
FUSE(Filesystem in Userspace)는 완전히 기능을 갖춘 파일시스템을 사용자 공간 프로그램에서 구현할 수 있게 해주는 리눅스의 기능이다.
FUSE 는 대상 파일시스템에 대한 모든 VFS(Virtual FileSystem)시스템 호출을 자신의 사용자 공간 구현으로 전달하는 범용 FUSE 커널 모듈을 이용해 구현한다.
커널 모듈과 사용자 공간의 구현은 /dev/fuse 장치를 열어 획득한 특수한 파일 디스크립터(File Descriptor)를 통해 통신한다.
* 4.6.2. 다중 사용자 외부 저장소
-
다중 사용자 환경에서 안드로이드 보안 모델을 지키기 위해 안드로이드 호환성 정의 문서(CDD)는 외부 저장소에 많은 요구사항을 두고 있다.
이 중 가장 중요한 것은 “안드로이드 디바이스에 있는 각 사용자는 반드시 별도의 격리된 외부 저장소 디렉터리를 가져야 한다” 는 것이다.
불행히도 외부 저장소는 전통적으로 누구나 읽을 수 있으며, 권한을 지원하지 않는 FAT 파일 시스템을 이용해 구현되었기 때문에 이 요구사항이 문제가 된다.
-
구글의 다중 사용자 외부 저장소는 하위 호환성을 제공하기 위해 리눅스 커널이 제공하는 다음의 세 기능을 활용해 사용자마다 고유한 외부 저장소를 구현한다.
마운트 네임스페이스
바운드 마운트
공유 서브트리
** 리눅스 마운트 고급 기능
-
각 파일시스템은 “마운트 지점(Mount Point)” 이라는 특정 디렉터리에 마운트되어 어떤 서브트리에 연결된다.
전통적으로 디렉터리 트리는 모든 프로세스가 공유하며, 각 프로세스는 동일한 디렉터리 구조를 보게 된다.
-
리눅스 버전 2.4.19부터 프로세스별 마운트 네임스페이스를 추가로 지원하게 됨에 따라,
각 프로세스는 고유한 마운트 지점을 가지고 다른 프로세스와는 다른 디렉터리 구조를 사용할 수 있게 되었다.
-
바인드 마운트(Bind Mount) 를 이용하면 어떤 디렉터리나 파일을 디렉터리 트리 안에 있는 다른 경로에 마운트해 동일한 디렉터리나 파일을 여러 곳에 보이게 할 수 있다.
바인드 마운트는 mount() 시스템 API 를 호출할 때 MS_BIND 플래그를 지정하거나 mount 명령에 —bind 파라미터를 전달해 생성한다.
-
공유 서브트리를 이용하면 프로세스가 자신의 고유한 네임스페이스를 갖게 되지만,
자신이 실행된 후에 마운트된 파일시스템에도 여전히 접근이 가능하다.
공유 서브트리는 4가지 마운트 방식을 제공하며, 안드로이드는 그중 공유 마운트와 슬레이브 마운트를 지원한다.
부모 네임스페이스에서 생성된 공유 마운트(Shared Mount)는 모든 자식 네임스페이스에 전달되어 이 네임스페이스에서 복제된 모든 프로세스를 볼 수 있다.
슬레이브 마운트(Slave Mount)는 공유 마운트인 마스터 마운트(Master Mount)를 갖고 있으며, 마스터 마운트는 새로 생성된 마운트를 한쪽 방향으로만 전달한다.
즉, 마스터는 슬레이브에게 전달되지만 슬레이브에서 마운트된 것은 마스터에게 전달되지 않는다.
이 체계를 통해 프로세스는 자신의 마운트를 다른 프로세스에는 보여주지 않으면서 공유된 시스템 마운트는 볼 수 있다.
** 안드로이드 구현
-
안드로이드 4.4(Kitkat)부터는 외부 저장소에 직접 마운트하는 것은 지원하지 않지만, 실제로 SD 카드를 사용하는 경우에는 sdcard FUSE 데몬을 통해 에뮬레이트한다.
-
내부 저장소를 통해 1차 외부 저장소(Primary External Storage)를 지원하는 디바이스에서는 sdcard FUSE 데몬이 /data/media/ 디렉터리를 소스로 사용해 /mnt/shell/emulated 에 에뮬레이트된 파일시스템을 생성한다.
-
앱 데이터 디렉터리와 마찬가지로, 각 사용자는 자신의 사용자 ID 와 같은 이름의 전용 외부 저장소 데이터 디렉터리를 갖는다.
안드로이드는 각 사용자가 실행한 앱만 사용자의 외부 저장소 데이터 디렉터리에 접근하고, 다른 사용자의 데이터 디렉터리는 보여주지 않기 위해 마운트 네임스페이스와 바운드 마운트를 함께 사용한다.
-
모든 앱은 zygote 프로세스에서 포크되므로 외부 저장소는 두 단계로 준비된다.
먼저 모든 포크된 앱 프로세스가 공유하는 마운트 지점은 zygote 프로세스에서 설정하고,
프로세스 자신만 볼 수 있는 전용 마운트 지점은 앱 프로세스를 분화하는 부분에서 설정한다.
-
mountEmulatedStorage() 함수는 프로세스 UID 에서 현재 사용자 ID 를 가져오고, 프로세스를 위한 새로운 마운트 네임스페이스를 생성하기 위해 CLONE_NEWNS 플래그를 이용해 unshare() 시스템 API 를 호출한다.
그런 다음 디바이스의 고유 init.rc 파일에서 초기화하는 EMULATED_STORAGE_SOURCE, EMULATED_STORAGE_TARGET, EXTERNAL_STORAGE 환경 변수의 값을 가져온 뒤, EMULATED_STORAGE_SOURCE, EMULATED_STORAGET_TARGET 과 현재 사용자 ID 의 값에 기반해 마운트 소스와 타깃 디렉터리 경로를 결정한다.
* 4.6.3. 외부 저장소 권한
-
원래 외부 저장소로 사용되었던 FAT 파일시스템을 에뮬레이트하기 위해 sdcard FUSE 데몬은 외부 저장소에 있는 각 파일이나 디렉터리에 고정된 사용자, 그룹, 접근 권한을 할당한다.
게다가 접근 권한은 변경할 수 없고 심볼릭 링크나 하드 링크는 지원하지 않는다.
할당된 사용자와 접근 권한은 sdcard 데몬이 사용하는 권한 유도 모드(Permission Derivation Mode)에 의해 결정된다.
-
이전 버전의 안드로이드와 하위 호환되며, 안드로이드 4.4(Kitkat)의 기본 모드인 레거시 모드( -l 옵션을 지정 ) 에서는 대부분의 파일과 디렉터리를 루트 사용자와 sdcard_r 그룹이 소유한다.
READ_EXTERNAL_STORAGE 권한을 가진 앱은 보조 그룹이 sdcard_r 에 포함되어 있으므로 다른 앱에서 생성한 파일도 대부분 읽을 수 있다.
-
이전 버전의 안드로이드에서는 외부 저장소에 있는 모든 파일과 디렉터리에 동일 소유자와 접근 권한이 할당되었지만,
안드로이드 4.4는 디렉터리를 생성한 앱이 디렉터리 소유자가 되기 때문에 앱 고유의 외부 파일 디렉터리(Context.getExternalFilesDir() 메서드가 반환하는 Android/data/<패키지명>/디렉터리)를 다르게 처리한다.
이 디렉터리에 파일을 읽거나 쓰기 위해 앱이 WRITE_EXTERNAL_STORAGE 권한을 가져야 하는 것은 아니다.
그렇지만 안드로이드 4.4에서도 앱의 외부 파일 디렉터리는 디렉터리의 소유자 그룹이 sdcard_r 로 설정되어 있기 때문에
READ_EXTERNAL_STORAGE 나 WRITE_EXTERNAL_STORAGE 권한을 가진 어떤 앱에서도 접근할 수 있다.
-
안드로이드 4.4에서는 sdcard 데몬에 -d 옵션을 지정해 디렉터리 구조에 기반을 둔 더욱 융통성 있는 권한 유도 모드를 지원한다.
이 권한 유도 모드는 Pictures/ 와 Music/ 디렉터리에 전용 그룹(sdcard_pics 와 sdcard_av)을 설정해 앱이 접근할 수 있는 파일을 더욱 섬세하게 제어할 수 있다.
-
디렉터리 기반 권한 모드에서는 사용자 디렉터리가 Android/user/ 밑에 들어간다.
4.7. 그 외 다중 사용자 기능
-
안드로이드 버전 4.4(Kitkat)부터 안드로이드의 자격증명 저장소(암호 키를 안전하게 관리할 수 있는 저장소)는 각 사용자가 자기만의 키 저장소를 가질 수 있게 한다.
-
AccountManager API 를 통해 접근할 수 있는 안드로이드 온라인 계정 데이터베이스는 보조 사용자가 고유한 계정을 가질 수 있을 뿐만 아니라, 제한된 프로필이 주 사용자의 계정에 대한 정보 일부를 공유할 수 있도록 확장되었다.
-
안드로이드는 사용자마다 서로 다른 디바이스 관리 정책을 설정할 수 있게 한다.
안드로이드 버전 4.4부터 사용자마다 고유한 VPN(사용자의 트래픽을 VPN 으로 전송하여 다른 사용자는 이 트래픽에 접근할 수 없다)을 설정할 수 있도록 지원한다.
4.8. 요약
-
안드로이드는 각 사용자마다 전용 내부 및 외부 저장소를 제공함으로써 여러 사용자가 디바이스를 공유할 수 있게 한다.
다중 사용자 지원은 확고한 보안 모델로 구현되었으며, 각 사용자의 앱은 고유한 UID 가 할당되어 전용 프로세스에서 실행되므로 다른 사용자의 데이터에 접근할 수 없다.
사용자 ID 를 고려한 UID 할당 체계와 각 사용자가 자신의 저장소만 접근할 수 있는 저장소 마운트 규칙을 결합해 사용자를 격리한다.
-
현재 다중 사용자 환경에서의 전화 작동 방식이 아직 정의되어 있지 않기 때문에,
사용자는 전화 기능이 없는 디바이스(태블릿)에서만 지원되고 있다.
계정 데이터베이스 관리, 자격증명 저장소, 디바이스 정책, VPN 지원 등 대부분의 안드로이드 기능은 다중 사용자를 염두에 두고 구현되었으며, 각 사용자마다 고유하게 설정할 수 있다.
정리 목차
4. 사용자 관리
4.1. 다중 사용자 지원 개요
4.2. 사용자 종류
4.2.1. 주 사용자(소유자)
4.2.2. 보조 사용자
4.2.3. 제한된 프로필
4.2.4. 게스트 사용자
4.3. 사용자 관리
4.3.1. 명령행 도구
4.3.2. 사용자 상태 및 관련된 브로드캐스트
4.4. 사용자 메타 데이터
4.4.1. 사용자 리스트 파일
4.4.2. 사용자 메타데이터 파일
4.4.3. 사용자 시스템 디렉터리
4.5. 사용자별 앱 관리
4.5.1. 앱 데이터 디렉터리
4.5.2. 앱 공유
4.6. 외부 저장소
4.6.1. 외부 저장소 구현
4.6.2. 다중 사용자 외부 저장소
4.6.3. 외부 저장소 권한
4.7. 그 외 다중 사용자 기능
4.8. 요약
'프로그래밍 놀이터 > 안드로이드, Java' 카테고리의 다른 글
[android] JobStatus.getUID NullPointerException (0) | 2018.04.28 |
---|---|
[android] Framework 혹은 3rd-party lib 이 뿜는 Exception 을 피해보자 ( ex) NullPointerException at acm.a ) (0) | 2018.04.22 |
[android 보안] 사용자 관리 #1 (0) | 2018.04.20 |
[android 보안] 패키지 관리 #2 (0) | 2018.04.19 |
[android 보안] 패키지 관리 #1 (0) | 2018.04.18 |
댓글