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 bên độc hại trái phép.

Rủi ro cụ thể liên quan đến việc tiết lộ dữ liệu 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ý. Mức độ tác động đặc biệt cao đối với các ứng dụng tài chính, vì các ứng dụng này có thể hiển thị 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 để đánh cắp dữ liệu trên bảng nhớ tạm sẽ khác nhau tuỳ theo phiên bản Android:

  • Các phiên bản Android cũ hơn Android 10 (API cấp 29) cho phép các ứng dụng chạy ở chế độ nền truy cập vào thông tin bảng nhớ tạm của ứng dụng chạy ở chế độ nền trước, có thể cho phép các tác nhân độc hại truy cập trực tiếp vào mọi dữ liệu đã sao chép.
  • Kể từ Android 12 trở lên (API cấp 31), 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ẽ hiển thị cho người dùng, khiến các cuộc tấn công khó bị bỏ qua hơn. Ngoài ra, để bảo vệ PII, 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 làm rối mã nguồn của bản xem trước nội dung 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 hiển thị dưới dạng văn bản thô và có thể 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 thực tế có thể cho phép kẻ tấn công đánh cắp dữ liệu nhạy cảm được sao chép vào bảng nhớ tạm bằng cách xem 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ề hoạt động của người dùng hợp pháp.

Tác động

Việc khai thác hoạt động xử lý bảng nhớ tạm không đúng cách có thể dẫn đến việc dữ liệu tài chính hoặc dữ liệu nhạy cảm liên quan đến người dùng bị các tác nhân độc hại đánh cắp. Đ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 giả mạ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 để làm rối mã nguồn của bản xem trước nội dung trên bảng nhớ tạm trong giao diện người dùng đồ hoạ của 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, 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 mới hơn hoặc bằng Android 10 (API 29) sẽ ngăn các quy trình ở chế độ nền truy cập vào dữ liệu bảng nhớ tạm trong ứng dụng trên 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 trên 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 (API cấp 29), thì mọi ứng dụng chạy nền đều có thể truy cập vào dữ liệu trên bảng nhớ tạm. Để giảm rủi ro này, bạn nên triển khai một hàm xoá mọi dữ liệu được sao chép vào bảng nhớ tạm sau một khoảng thời gian cụ thể. Hàm này được tự động thực hiện kể từ Android 13 (API cấp 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 đưa đ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