HOME > android > basic

안드로이드 - UID(User Id), GID(Group Id)와 Security

JSFollow07 Aug 2019

안드로이드는 리눅스의 UID(User Id)와 GID(Group Id)를 사용하여 앱마다 다르게 권한을 설정할 수 있습니다.

예를 들어, 앱에 할당되는 UID는 Root 또는 System이 아니기 때문에 /system 폴더에 접근할 수 없습니다. 반대로, 앱이 GID media_rw를 갖고 있으면 sdcard에 접근할 수 있습니다. 이런 방식으로 시스템은 앱마다 다른 권한을 부여할 수 있고, 보안을 유지할 수 있습니다.

UID(User Id)

UID는 사용자의 ID입니다. 안드로이드는 앱마다 다른 UID를 할당합니다. 이렇게 하는 이유는 서로 다른 앱의 데이터에 접근하지 못하도록 만들기 위해서 입니다. 앱은 자신의 데이터 폴더만 접근가능하고, 시스템이나 다른 앱의 폴더에 접근할 수 없습니다.

예를들어, 루팅된 디바이스의 shell에서 아래 처럼 앱의 데이터 폴더의 권한 및 UID를 볼 수 있습니다.

generic_x86:/data/data # ls -aln
# Permission   [UID] [GID]                        [File name]
drwx------  10 10126 10126 4096 2019-08-08 20:17 com.google.android.apps.maps
drwx------   8 10108 10108 4096 2019-08-08 20:17 com.google.android.apps.messaging
drwx------   8 10129 10129 4096 2019-08-08 20:17 com.google.android.youtube

왼쪽에 있는 퍼미션을 보면 User만 rwx 권한이 있고 Group과 Others는 어떤 권한도 없습니다. 그리고 UID를 보면 앱마다 다른 UID가 설정되어 있습니다. 따라서, Root를 제외한 모든 앱은 서로의 파일에 접근할 수 없습니다.

앱 UID 확인

adb shell에서 ps -ef 명령어를 입력하면 프로세스의 UID를 확인할 수 있습니다.

127|generic_x86:/ $ ps -ef
UID            PID  PPID C STIME TTY          TIME CMD
root             1     0 0 09:02 ?        00:00:01 init second_stage
audioserver   1706     1 0 09:06 ?        00:00:00 android.hardware.audio@2.0-service
cameraserver  1708     1 0 09:06 ?        00:00:00 android.hardware.camera.provider@2.4-service
media         1709     1 0 09:06 ?        00:00:00 android.hardware.cas@1.1-service
system        1710     1 0 09:06 ?        00:00:00 android.hardware.configstore@1.1-service
media         1711     1 0 09:06 ?        00:00:00 android.hardware.drm@1.0-service
tombstoned    1774     1 0 09:07 ?        00:00:00 tombstoned
radio         2110  1701 0 09:12 ?        00:00:07 com.android.phone
u0_a114       2113  1701 0 09:12 ?        00:00:02 com.google.android.inputmethod.latin
u0_a93        2114  1701 0 09:12 ?        00:00:01 com.google.android.apps.nexuslauncher

위에 보이는 UID의 이름들은 각각 다른 숫자로 할당되어 있습니다. UID 이름이 아닌 숫자로 보고 싶으면 ps -efn 명령어를 사용하면 됩니다.

GID(Group Id)

GID는 Group Id입니다. User는 1개의 Primary group과 1개 이상의 Supplementary group을 갖을 수 있습니다. 결론적으로, User는 여러개의 GID를 갖을 수 있습니다.

앱이 특정 파일에 대한 접근 권한이 필요하다면 그 권한과 관련된 GID를 받아야 합니다. 예를 들어, 만약 앱이 android.permission.INTERNET 퍼미션을 얻는다면 시스템은 앱에 GID inet을 추가해 줍니다. 앱은 inet으로 접근 가능한 영역에 접근할 수 있게 됩니다.

GID도 UID처럼 ls -al명령어로 파일들의 권한을 확인할 수 있습니다. 다음은 /sdcard 파일들의 GID 정보입니다.(GID를 숫자로 보려면 ls -aln을 사용하세요)

generic_x86:/sdcard # ls -al
# Permission  [UID] [GID]                          [File name]
drwxrwx--x  2 root sdcard_rw 4096 2019-07-19 22:04 Alarms
drwxrwx--x  3 root sdcard_rw 4096 2019-07-19 22:04 Android
drwxrwx--x  2 root sdcard_rw 4096 2019-07-19 22:04 DCIM
drwxrwx--x  2 root sdcard_rw 4096 2019-08-01 23:36 Download
drwxrwx--x  2 root sdcard_rw 4096 2019-07-19 22:04 Movies
drwxrwx--x  2 root sdcard_rw 4096 2019-07-19 22:04 Music
....

위의 결과에서 왼쪽 퍼미션을 보면 User와 Group은 rwx를 권한이 있습니다. 따라서 앱이 /sdcard 영역에 접근하려면 root가 되거나 sdcard_rw Group에 속해야 합니다.

사실 앱이 WRITE_EXTERNAL_STORAGE 권한을 받으면 sdcard_rw GID을 받지 않습니다. 실제로는 앱의 mountinfo 정보가 변경되어 /sdcard에 접근할 수 있게 됩니다. 그리고 참고로, 안드로이드 Q에서는 Scoped Storage로 인해 외부 저장소에 접근하는 권한 및 방식들이 이전과 조금 달라졌습니다.

앱 GID 확인

앱의 GID를 확인하는 것은 UID보다 조금 더 번거롭습니다. 먼저 확인하려는 UID를 알아야 합니다.

예를 들어, Launcher 앱의 GID를 확인하려면 아래와 같은 명령어로 UID를 찾습니다.

generic_x86:/ $ ps -ef | grep launcher
u0_a93        2114  1701 0 22:09:12 ?     00:00:01 com.google.android.apps.nexuslauncher

그리고 /proc/[pid]/status파일을 확인하면 GID를 알 수 있습니다. 저의 경우 PID는 2114이고 확인한 결과는 아래와 같습니다.

generic_x86:/ $ cat /proc/2114/status                                                          
Name:	s.nexuslauncher
Umask:	0077
State:	S (sleeping)
Tgid:	2114
Ngid:	0
Pid:	2114
PPid:	1701
TracerPid:	0
Uid:	10093	10093	10093	10093
Gid:	10093	10093	10093	10093
FDSize:	128
Groups:	9997 20093 50093

위의 결과에서 Gid는 Primary group으로 1개만 설정되어있고, Groups는 Supplementary group으로 여러개의 GID가 설정되어 있습니다.

앱의 UID는 누가 언제 할당하는가?

앱의 UID는 앱이 설치될 때 결정됩니다. PackageManager는 시스템에 앱을 설치할 때 앱마다 다른 ID를 부여해 줍니다.

시스템 앱이 아닌, 일반적인 3rd party 앱은 10000~20000 사이의 숫자로 UID가 할당됩니다. 하지만 shell, root, installd 등은 고정된 시스템 UID이며, 이런 예약된 UID들은 10000 미만의 값으로 설정됩니다.

고정된(예약된) UID는 안드로이드 코드 android_filesystem_config.h에 정의되어 있습니다.

#define AID_RADIO 1001           /* telephony subsystem, RIL */
#define AID_BLUETOOTH 1002       /* bluetooth subsystem */
#define AID_GRAPHICS 1003        /* graphics devices */
#define AID_INPUT 1004           /* input devices */
#define AID_AUDIO 1005           /* audio devices */
#define AID_CAMERA 1006          /* camera devices */
#define AID_LOG 1007             /* log devices */
#define AID_COMPASS 1008         /* compass device */
....

하지만 앱마다 UID가 같은 경우도 있습니다. 안드로이드에 SharedUserId라는 개념이 있으며 여러 앱이 동일한 UID를 사용할 수 있게 합니다.

아래와 같이 앱의 AndroidManifest.xml에 sharedUserId 속성으로 ID를 정할 수 있습니다.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.codechacha.sample"
    android:sharedUserId="android.uid.codechacha">

여러 앱을 위와 동일하게 sharedUserId를 설정하면, 앱이 단말에 설치될 때 동일한 UID를 할당 받습니다. 중요한 것은, SharedUserId를 설정할 때 앱들끼리 동일한 Signature로 서명이 되어야 합니다. 그렇지 않으면 앱이 설치되지 않습니다.

앱의 GID는 누가 언제 할당하는가?

고정적인 GID는 앱의 UID와 동일하게 설정되며, 앱마다 GID가 더 추가될 수 있습니다. 위에서 소개한 것처럼, 시스템은 앱이 어떤 퍼미션을 갖고 있을 때 어떤 GID를 할당할 지 참고하는 테이블이 있습니다.

안드로이드 코드 frameworks/base/data/etc/platform.xml가 그 테이블 입니다. 아래와 같이 퍼미션 당 어떤 GID가 할당될 지 적혀있습니다.

<permission name="android.permission.INTERNET" >
    <group gid="inet" />
</permission>
<permission name="android.permission.WRITE_MEDIA_STORAGE" >
    <group gid="media_rw" />
</permission>
<permission name="android.permission.LOOP_RADIO" >
    <group gid="loop_radio" />
</permission>

위의 퍼미션이 앱에 할당되면 dumpsys 로그에서 GID가 할당되었는지 확인할 수 있습니다.

adb shell dumpsys package [package name]을 입력하면 아래와 같은 로그가 출력되고 여기에 gids=[3003]처럼 할당된 GID를 보여줍니다. GID 3003은 inet입니다.

Packages:
  Package [com.codechacha.sample] (e5cd86f):
    userId=10085
    pkg=Package{9a1a3a5 com.codechacha.sample}
    codePath=/data/app/com.codechacha.sample-UC4OqmKDH97R9vhuk7qtDw==
    ......
    install permissions:
      android.permission.INTERNET: granted=true
    User 0: ceDataInode=-4294952062 installed=true hidden=false suspended=false stopped=false notLaunched=false enabled=0 instant=false virtual=false
      gids=[3003]

보안 측면에서 UID와 GID

안드로이드에는 퍼미션 시스템이 있습니다. 앱은 AndroidManifest.xml에 퍼미션을 선언할 수 있고, 사용자가 허락하면 시스템 내에서 그 권한과 관련된 API를 사용할 수 있습니다.

퍼미션 시스템과 별개로, 안드로이드는 앱마다 다른 권한을 부여하는 UID와 GID를 사용합니다. 예를 들어, 어떤 시스템 API를 호출할 때, API는 함수를 호출하는 UID를 체크하여 몇몇 UID만 그 API를 사용할 수 있게 합니다. 그래서 어떤 API는 UID가 system인 프로세스만 사용할 수 있는 것들도 있습니다.

그 외에 다른 권한 및 보안(Security)

그 외에 안드로이드는 selinux를 사용합니다. 앱이 파일에 접근할 수 있는 UID, GID 권한을 모두 갖고 있어도 selinux 권한이 없어 접근이 안될 수도 있습니다. selinux는 파일마다 label을 붙이며, 앱의 프로세스가 해당 label의 접근 권한이 없다면 읽거나 쓸 수 없습니다.

정리

리눅스의 UID와 GID가 안드로이드에서 어떻게 사용되는지 알아보았습니다. 또한 앱이 어떻게 UID와 GID를 얻게 되는지, 어떤 방식으로 권한을 확인할 수 있는지 알아보았습니다. 그리고 보안 측면에서 어떻게 사용되는지 알아보았습니다.

참고