PHẢI BẠN KHÔNG? CÙNG SỬA NHÉ: những lỗi khi sử dụng quyền truy cập của Android
!!! Đây là một bài dịch Việt hoá, có thêm thắt một chút để nội dung dễ hiểu hơn. Bài viết gốc: https://blog.oversecured.com/Common-mistakes-when-using-permissions-in-Android/
Trong quá trình phát triển ứng dụng và khi thực hiện kiểm thử xâm nhập theo check list MSTG của OWASP chúng ta cần chú ý tới việc xin cấp quyền cho ứng dụng Android. Nội dung chính mà chúng ta sẽ thảo luận trong bài viết này là: sử dụng tính năng quyền truy cập như thế nào để tránh xảy ra các vấn đề bảo mật?
1. Khai báo và phân loại quyền truy cập
1.1. Quyền truy cập (Permission)
Quyền truy cập là cơ chế của hệ điều hành Android nhằm hạn chế khả năng truy cập của các thành phần trong ứng dụng Android (activities, broadcast receivers, services, and content providers). Để mở khoá những hạn chế này, lập trình viên cần khai báo các quyền muốn được sử dụng trong tệp tin AndroidManifest.xml
của ứng dụng. Khi ứng dụng hoạt động, hệ điều hành sẽ kiểm tra danh sách các quyền được khai báo trước để xác định xem ứng dụng được phép truy cập những dữ liệu gì, được phép thực hiện những hành động gì.
Ví dụ như: một ứng dụng muốn truy cập danh bạ điện thoại, thì lập trình viên cần khai báo trong tệp tin AndroidManifest.xml
thẻ uses-permission
<uses-permission android:name="android.permission.READ_CONTACTS" />
Đây là một quyền truy cập thuộc nhóm quyền (protection level) nguy hiểm. Những quyền thuộc nhóm này sẽ được xin cấp khi người dùng thực hiện việc cài đặt ứng dụng. Nếu người dùng từ chối thì ứng dụng sẽ không được cài đặt nữa.
1.2. Quyền truy cập tự định nghĩa (Custom Permission)
Bên cạnh các quyền do hệ thống định nghĩa, lập trình viên cũng có thể tự định nghĩa quyền truy cập. Việc này có tác dụng hạn chế các ứng dụng được truy cập vào tài nguyên trong ứng dụng của họ. Ví dụ như mình có một ứng dụng, với 1 activity được khai báo exported (đồng nghĩa với việc các bên khác, bao gồm bản thân hệ điều hành Android, có thể khởi động activity này). Nhưng mình không muốn các ứng dụng không liên quan được tự do khởi động activity này lên. Do đó mình sẽ khai báo activity như sau:
<!-- App A - Khai báo quyền truy cập tự định nghĩa -->
<permission android:name="com.mycoolcam.USE_COOL_CAMERA" android:protectionLevel="dangerous" />
...
<activity android:name=".CoolCamActivity" android:exported="true" android:permission="com.mycoolcam.USE_COOL_CAMERA">
<intent-filter>
<action android:name="com.mycoolcam.LAUNCH_COOL_CAM" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Như vậy, nếu một ứng dụng khác muốn khởi động activity CoolCamActivity thì ứng dụng đó sẽ phải khai báo như sau:
<!-- App B - Khai báo xin cấp quyền truy cập -->
<uses-permission android:name="com.mycoolcam.USE_COOL_CAMERA" />
Quyền truy cập trên sẽ được xin khi thực hiện cài đặt ứng dụng B, do ứng dụng A đã khai báo com.mycoolcam.USE_COOL_CAMERA thuộc nhóm quyền nguy hiểm.
1.3. Các nhóm quyền truy cập (Permission Group)
Các quyền truy cập của ứng dụng Android được chia thành 4 nhóm sau:
- Nhóm quyền phổ thông (normal): các quyền thuộc nhóm này sẽ được chấp thuận trong quá trình cài đặt ứng dụng, người dùng sẽ không thấy thông báo ứng dụng yêu cầu được xin cấp phép sử dụng.
- Nhóm quyền nguy hiểm (dangerous): các quyền thuộc nhóm này sẽ cần người dùng chấp thuận trong quá trình cài đặt. Đa số các quyền thuộc nhóm này sẽ cho phép ứng dụng truy cập vào những dữ liệu nhạy cảm và những chức năng quan trọng.
- Nhóm quyền đặc trưng (signature): ứng dụng xin cấp quyền thuộc nhóm này sẽ cần được ký bởi cùng chữ ký với ứng dụng đã định nghĩa quyền. Nếu như ứng dụng A trong ví dụ ở phần 1.2 định nghĩa quyền truy cập của mình thuộc nhóm quyền đặc trưng, thì ứng dụng B phải được ký cùng chữ ký với ứng dụng A. Nếu không thì ứng dụng B sẽ không được cấp quyền, tương đương với việc không thể cài đặt được vào thiết bị.
- Nhóm quyền hệ thống (signatureOrSystem): nhóm quyền này thường được sử dụng bởi các ứng dụng cài đặt sẵn cùng với hệ thống.
Bốn nhóm quyền trên giúp hệ thống quản lý tài nguyên một cách tốt hơn. Việc xin cấp quyền một cách hợp lý cũng giúp cho ứng dụng được bảo vệ tốt hơn.
2. Những lỗi khi sử dụng quyền truy cập
2.1. Quên định nghĩa permissionLevel
Điều gì sẽ xảy ra nếu chúng ta quên mất việc định nghĩa permissionLevel?
<permission android:name="com.mycoolcam.USE_COOL_CAMERA" />
Cũng không khó đoán lắm đúng không. Lúc này hệ thống sẽ sử dụng giá trị mặc định cho quyền truy cập của chúng ta, giá trị mặc định này là normal
. Điều này khiến cho mọi ứng dụng đều có thể xin cấp quyền này, và quyền này được chấp thuận ngay trong quá trình cài đặt.
Vậy nên bài học đầu tiên là đừng quên.
2.2. Hệ sinh thái không đầy đủ
Chúng ta xét trường hợp như sau:
- Một công ty phát triển 2 ứng dụng, gọi là ứng dụng
My Cool Cam
và ứng dụngMy Cool Reader
. - Hai ứng dụng này kết hợp lại tạo thành một hệ sinh thái, nơi mà ứng dụng
Reader
sử dụng chức năng của ứng dụngCam
. - Hai ứng dụng này được ký với cùng chữ ký.
Tệp tin AndroidManifest.xml
của ứng dụng My Cool Cam
:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.mycoolcam">
<permission android:name="com.mycoolcam.USE_COOL_CAMERA" android:protectionLevel="signature" />
<uses-permission android:name="com.mycoolcam.USE_COOL_CAMERA" />
<application android:label="My Cool Cam">
<activity android:name=".CoolCamActivity" android:exported="true" android:permission="com.mycoolcam.USE_COOL_CAMERA">
<intent-filter>
<action android:name="com.mycoolcam.LAUNCH_COOL_CAM" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<!-- ... -->
</application>
</manifest>
Tệp tin AndroidManifest.xml
của ứng dụng My Cool Reader
:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.mycoolreader">
<uses-permission android:name="com.mycoolcam.USE_COOL_CAMERA" />
<application android:label="My Cool Reader">
<provider android:name=".AllUserNotesContentProvider" android:authorities="com.mycoolreader.notes_provider" android:exported="true" android:permission="com.mycoolcam.USE_COOL_CAMERA" />
<!-- ... -->
</application>
</manifest>
Kịch bản đẹp thì mọi thứ sẽ hoạt động tốt và an toàn, khi hai ứng dụng này tạo thành một hệ sinh thái kín thì chỉ có hai ứng dụng này mới truy cập được dữ liệu từ AllUserNotesContentProvider
. Nếu một ứng dụng khác nữa muốn truy cập dữ liệu từ AllUserNotesContentProvider
thì nó cũng phải nằm trong hệ sinh thái trên.
Tiếc rằng chưa chắc kịch bản đẹp ấy sẽ xảy ra. Nếu trên thiết bị chỉ cài đặt ứng dụng My Cool Reader
thì sao? Lúc này do đối tượng định nghĩa quyền truy cập là My Cool Cam
không tồn tại, nên hệ thống sẽ coi như com.mycoolcam.USE_COOL_CAMERA
thuộc nhóm quyền phổ thông (normal).
Nếu có ứng dụng X khác khai báo trong AndroidManifest.xml
<uses-permission android:name="com.mycoolcam.USE_COOL_CAMERA" />
Thì ứng dụng X sẽ truy cập được dữ liệu từ AllUserNotesContentProvider
kể cả khi nó không được ký cùng chữ ký với ứng dụng My Cool Reader
.
Để tránh kịch bản xấu trên thì chỉ cần định nghĩa permissionLevel
của quyền chung ở mọi ứng dụng nằm trong cùng hệ sinh thái.
2.3. Lỗi gõ sai
Đến trứng vịt còn lộn được nữa là người. Ai cũng có lúc sai thôi, quan trọng là biết ngã ở đâu thì gấp đôi ở đấy.
2.3.1. Gõ sai tên quyền
Ứng dụng A:
<permission android:name="com.mycoolcam.USE_COOL_CAMERA" android:protectionLevel="signature" />
Ứng dụng B:
<activity android:name=".CoolCamActivity" android:exported="true" android:permission="com.mycoolcam.USE_COOL_CAM">
<intent-filter>
<action android:name="com.mycoolcam.LAUNCH_COOL_CAM" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Lỗi sai: ứng dụng A khai báo USE_COOL_CAMERA
thuộc nhóm quyền đặc trưng, điểm này không sai. Nhưng ứng dụng B lại khai báo USE_COOL_CAM
, nên hệ thống sẽ coi quyền này thuộc nhóm quyền phổ thông.
2.3.2. Gõ sai khi khai báo thành phần
Ứng dụng A:
<permission android:name="com.mycoolcam.USE_COOL_CAMERA" android:protectionLevel="signature" />
Ứng dụng B:
<activity android:name=".CoolCamActivity" android:exported="true" android:uses-permission="com.mycoolcam.USE_COOL_CAMERA">
<intent-filter>
<action android:name="com.mycoolcam.LAUNCH_COOL_CAM" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Lỗi sai: đáng ra ứng dụng B phải khai báo với thuộc tính android:permission
(cho biết cần phải có quyền gì để được sử dụng chức năng), nhưng ở đây lại sử dụng android:uses-permission
. Thành ra CoolCamActivity
không yêu cầu bất cứ quyền gì cả, khiến mọi ứng dụng có thể sử dụng CoolCamActivity
.
2.4. Bảo vệ chưa đầy đủ
Một số chức năng nhạy cảm đôi khi không được bảo vệ một cách đầy đủ, khiến cho dữ liệu có thể bị truy cập bởi những đối tượng ngoài ý muốn. Chúng ta xét ví dụ sau :
- Tệp tin AndroidManifest.xml
<uses-permission android:name="android.permission.READ_CONTACTS" /> ... <provider android:name=".ContactsProvider" android:authorities="com.exampleapp.contacts" android:exported="true" />
- Tệp tin ContactsProvider.java
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return getContext().getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projection, selection, selectionArgs, sortOrder); }
Ở trường hợp trên, để truy cập URI ContactsContract.CommonDataKinds.Phone
(content://com.android.contacts/data/phones
) cần có quyền android.permission.READ_CONTACTS
. Nhưng do khai báo chưa đầy đủ, khiến cho ContactsProvider
không được bảo vệ. Vì thế có thể truy cập content://com.exampleapp.contacts
một cách dễ dàng, mà vẫn đạt được hiệu quả như khi truy cập URI ContactsContract.CommonDataKinds.Phone
.
All Rights Reserved