Xử lý bảng nhớ tạm một cách an toàn

Danh mục OWASP: MASVS-CODE: Chất lượng mã

Tổng quan

Android cung cấp một khung mạnh mẽ được gọi là bảng nhớ tạm để sao chép và dán dữ liệu giữa các ứng dụng. Việc triển khai không đúng cách tính năng này có thể làm lộ dữ liệu liên quan đến người dùng cho các ứng dụng hoặc đối tượng xấu trái phép.

Rủi ro cụ thể liên quan đến việc lộ dữ liệu trên bảng nhớ tạm phụ thuộc vào bản chất của ứng dụng và Thông tin nhận dạng cá nhân (PII) mà ứng dụng đang xử lý. Tác động này đặc biệt lớn đối với các ứng dụng tài chính, vì chúng có thể tiết lộ dữ liệu thanh toán hoặc các ứng dụng xử lý mã xác thực hai yếu tố (2FA).

Các vectơ tấn công có thể được tận dụng để trích xuất dữ liệu trên bảng nhớ tạm sẽ khác nhau tuỳ thuộc vào phiên bản Android:

  • Các phiên bản Android cũ hơn Android 10 (cấp độ API 29) cho phép các ứng dụng chạy ở chế độ nền truy cập vào thông tin trên bảng nhớ tạm của ứng dụng trên nền trước, có khả năng cho phép các đối tượng xấu truy cập trực tiếp vào mọi dữ liệu đã sao chép.
  • Từ Android 12 (cấp độ API 31) trở đi, mỗi khi một ứng dụng truy cập vào dữ liệu trong bảng nhớ tạm và dán dữ liệu đó, một thông báo ngắn sẽ xuất hiện cho người dùng, khiến các cuộc tấn công khó bị phát hiện hơn. Ngoài ra, để bảo vệ thông tin nhận dạng cá nhân, Android hỗ trợ cờ đặc biệt ClipDescription.EXTRA_IS_SENSITIVE hoặc android.content.extra.IS_SENSITIVE. Điều này cho phép nhà phát triển che giấu trực quan bản xem trước nội dung trong bảng nhớ tạm trong giao diện người dùng đồ hoạ của bàn phím, ngăn dữ liệu đã sao chép xuất hiện dưới dạng văn bản thuần tuý và có khả năng bị các ứng dụng độc hại đánh cắp. Việc không triển khai một trong các cờ nêu trên có thể cho phép kẻ tấn công trích xuất dữ liệu nhạy cảm đã sao chép vào bảng nhớ tạm bằng cách nhìn trộm hoặc thông qua các ứng dụng độc hại. Trong khi chạy ở chế độ nền, các ứng dụng này sẽ chụp ảnh màn hình hoặc quay video về các hoạt động của người dùng hợp pháp.

Tác động

Việc khai thác quy trình xử lý bảng nhớ tạm không đúng cách có thể khiến các đối tượng xấu trích xuất dữ liệu tài chính hoặc dữ liệu nhạy cảm liên quan đến người dùng. Điều này có thể giúp kẻ tấn công thực hiện các hành động khác như chiến dịch lừa đảo hoặc đánh cắp danh tính.

Giải pháp giảm thiểu

Gắn cờ dữ liệu nhạy cảm

Giải pháp này được dùng để che giấu nội dung xem trước của nội dung trên bảng nhớ tạm trong GUI bàn phím. Mọi dữ liệu nhạy cảm có thể sao chép, chẳng hạn như mật khẩu hoặc dữ liệu thẻ tín dụng, đều phải được gắn cờ bằng ClipDescription.EXTRA_IS_SENSITIVE hoặc android.content.extra.IS_SENSITIVE trước khi gọi ClipboardManager.setPrimaryClip().

Kotlin

// If your app is compiled with the API level 33 SDK or higher.
clipData.apply {
    description.extras = PersistableBundle().apply {
        putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true)
    }
}

// If your app is compiled with API level 32 SDK or lower.
clipData.apply {
    description.extras = PersistableBundle().apply {
        putBoolean("android.content.extra.IS_SENSITIVE", true)
    }
}

Java

// If your app is compiled with the API level 33 SDK or higher.
PersistableBundle extras = new PersistableBundle();
extras.putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true);
clipData.getDescription().setExtras(extras);

// If your app is compiled with API level 32 SDK or lower.
PersistableBundle extras = new PersistableBundle();
extras.putBoolean("android.content.extra.IS_SENSITIVE", true);
clipData.getDescription().setExtras(extras);

Thực thi các phiên bản Android mới nhất

Việc thực thi ứng dụng chạy trên các phiên bản Android từ Android 10 (API 29) trở lên sẽ ngăn các quy trình ở chế độ nền truy cập vào dữ liệu trên bảng nhớ tạm trong ứng dụng ở nền trước.

Để buộc ứng dụng chỉ chạy trên Android 10 (API 29) trở lên, hãy đặt các giá trị sau cho chế độ cài đặt phiên bản trong tệp bản dựng Gradle trong dự án của bạn trong Android Studio.

Groovy

android {
      namespace 'com.example.testapp'
      compileSdk [SDK_LATEST_VERSION]

      defaultConfig {
          applicationId "com.example.testapp"
          minSdk 29
          targetSdk [SDK_LATEST_VERSION]
          versionCode 1
          versionName "1.0"
          ...
      }
      ...
    }
    ...

Kotlin

android {
      namespace = "com.example.testapp"
      compileSdk = [SDK_LATEST_VERSION]

      defaultConfig {
          applicationId = "com.example.testapp"
          minSdk = 29
          targetSdk = [SDK_LATEST_VERSION]
          versionCode = 1
          versionName = "1.0"
          ...
      }
      ...
    }
    ...

Xoá nội dung trong Bảng nhớ tạm sau một khoảng thời gian xác định

Nếu ứng dụng được thiết kế để chạy trên các phiên bản Android thấp hơn Android 10 (cấp độ API 29), thì mọi ứng dụng chạy ở chế độ nền đều có thể truy cập vào dữ liệu trên bảng nhớ tạm. Để giảm thiểu rủi ro này, bạn nên triển khai một hàm xoá mọi dữ liệu đã sao chép vào bảng tạm sau một khoảng thời gian cụ thể. Chức năng này tự động thực hiện kể từ Android 13 (cấp độ API 33). Đối với các phiên bản Android cũ, bạn có thể thực hiện việc xoá này bằng cách thêm đoạn mã sau vào mã của ứng dụng.

Kotlin

//The Executor makes this task Asynchronous so that the UI continues being responsive
backgroundExecutor.schedule({
    //Creates a clip object with the content of the Clipboard
    val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
    val clip = clipboard.primaryClip
    //If SDK version is higher or equal to 28, it deletes Clipboard data with clearPrimaryClip()
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        clipboard.clearPrimaryClip()
    } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
    //If SDK version is lower than 28, it will replace Clipboard content with an empty value
        val newEmptyClip = ClipData.newPlainText("EmptyClipContent", "")
        clipboard.setPrimaryClip(newEmptyClip)
     }
//The delay after which the Clipboard is cleared, measured in seconds
}, 5, TimeUnit.SECONDS)

Java

//The Executor makes this task Asynchronous so that the UI continues being responsive

ScheduledExecutorService backgroundExecutor = Executors.newSingleThreadScheduledExecutor();

backgroundExecutor.schedule(new Runnable() {
    @Override
    public void run() {
        //Creates a clip object with the content of the Clipboard
        ClipboardManager clipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
        ClipData clip = clipboard.getPrimaryClip();
        //If SDK version is higher or equal to 28, it deletes Clipboard data with clearPrimaryClip()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            clipboard.clearPrimaryClip();
            //If SDK version is lower than 28, it will replace Clipboard content with an empty value
        } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
            ClipData newEmptyClip = ClipData.newPlainText("EmptyClipContent", "");
            clipboard.setPrimaryClip(newEmptyClip);
        }
    //The delay after which the Clipboard is cleared, measured in seconds
    }, 5, TimeUnit.SECONDS);

Tài nguyên