การจัดการคลิปบอร์ดที่ปลอดภัย

หมวดหมู่ OWASP: MASVS-CODE: คุณภาพของโค้ด

ภาพรวม

Android มีเฟรมเวิร์กที่มีประสิทธิภาพซึ่งเรียกว่าคลิปบอร์ดสำหรับ การคัดลอกและวางข้อมูลระหว่างแอปพลิเคชัน การติดตั้งฟีเจอร์นี้อย่างไม่เหมาะสมอาจทำให้ข้อมูลที่เกี่ยวข้องกับผู้ใช้ถูกเปิดเผยต่อผู้ไม่ประสงค์ดีหรือแอปพลิเคชันที่ไม่ได้รับอนุญาต

ความเสี่ยงที่เฉพาะเจาะจงซึ่งเกี่ยวข้องกับการเปิดเผยข้อมูลในคลิปบอร์ดจะขึ้นอยู่กับ ลักษณะของแอปพลิเคชันและข้อมูลส่วนบุคคลที่ระบุตัวบุคคลนั้นได้ (PII) ที่แอปพลิเคชัน จัดการ ผลกระทบจะสูงเป็นพิเศษสำหรับแอปพลิเคชันทางการเงิน เนื่องจากอาจ เปิดเผยข้อมูลการชำระเงิน หรือแอปที่จัดการรหัสการยืนยันแบบ 2 ขั้นตอน (2FA)

เวกเตอร์การโจมตีที่อาจใช้เพื่อกรองข้อมูลในคลิปบอร์ดออกจะแตกต่างกันไปตามเวอร์ชัน Android ดังนี้

  • Android เวอร์ชันเก่ากว่า Android 10 (ระดับ API 29) อนุญาตให้แอปพลิเคชันที่ทำงานเบื้องหลังเข้าถึงข้อมูลคลิปบอร์ดของแอปที่ทำงานอยู่เบื้องหน้า ซึ่งอาจอนุญาตให้ผู้ไม่ประสงค์ดีเข้าถึงข้อมูลที่คัดลอกโดยตรง
  • ตั้งแต่ Android 12 (ระดับ API 31) เป็นต้นไป ทุกครั้งที่แอปพลิเคชันเข้าถึงข้อมูลภายในคลิปบอร์ดและวางข้อมูลนั้น ระบบจะแสดงข้อความโทสต์แก่ผู้ใช้ ซึ่งจะทำให้การโจมตีสังเกตได้ยากขึ้น นอกจากนี้ Android ยังรองรับClipDescription.EXTRA_IS_SENSITIVEหรือandroid.content.extra.IS_SENSITIVEแฟล็กพิเศษเพื่อ ปกป้อง PII ซึ่งจะช่วยให้นักพัฒนาซอฟต์แวร์ ซ่อนตัวอย่างเนื้อหาในคลิปบอร์ดภายใน GUI ของคีย์บอร์ด เพื่อป้องกันไม่ให้ระบบแสดงข้อมูลที่คัดลอกเป็นข้อความธรรมดาและอาจ ถูกแอปพลิเคชันที่เป็นอันตรายขโมยไป การไม่ใช้ Flag อย่างใดอย่างหนึ่งที่กล่าวถึงข้างต้นอาจทำให้ผู้โจมตีสามารถกรองข้อมูลที่ละเอียดอ่อนซึ่งคัดลอกไปยังคลิปบอร์ดได้โดยการแอบมองหรือผ่านแอปพลิเคชันที่เป็นอันตราย ซึ่งขณะทำงานในเบื้องหลังจะถ่ายภาพหน้าจอหรือบันทึกวิดีโอของกิจกรรมของผู้ใช้ที่ถูกต้อง

ผลกระทบ

การใช้ประโยชน์จากการจัดการคลิปบอร์ดที่ไม่เหมาะสมอาจส่งผลให้ผู้ไม่ประสงค์ดีกรองข้อมูลที่มีความละเอียดอ่อนหรือข้อมูลทางการเงินที่เกี่ยวข้องกับผู้ใช้ออกไป ซึ่งอาจช่วยให้ผู้โจมตีดำเนินการต่อไปได้ เช่น แคมเปญฟิชชิงหรือการโจรกรรมข้อมูลส่วนบุคคล

การลดปัญหา

ติดค่าสถานะข้อมูลที่ละเอียดอ่อน

โซลูชันนี้ใช้เพื่อปกปิดตัวอย่างเนื้อหาในคลิปบอร์ด ภายใน GUI ของแป้นพิมพ์ ข้อมูลที่ละเอียดอ่อนซึ่งคัดลอกได้ เช่น รหัสผ่านหรือข้อมูลบัตรเครดิต ควรมีการติดแฟล็กด้วย ClipDescription.EXTRA_IS_SENSITIVE หรือ android.content.extra.IS_SENSITIVE ก่อนเรียกใช้ 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);

บังคับใช้ Android เวอร์ชันล่าสุด

การบังคับให้แอปทำงานบน Android เวอร์ชันที่ใหม่กว่าหรือเท่ากับ Android 10 (API 29) จะป้องกันไม่ให้กระบวนการทำงานเบื้องหลังเข้าถึงข้อมูลในคลิปบอร์ดในแอปพลิเคชันที่ทำงานอยู่เบื้องหน้า

หากต้องการบังคับให้แอปทํางานใน Android 10 (API 29) ขึ้นไปเท่านั้น ให้ตั้งค่าต่อไปนี้สําหรับการตั้งค่าเวอร์ชันในไฟล์ Gradle Build ภายในโปรเจ็กต์ใน Android Studio

ดึงดูด

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"
          ...
      }
      ...
    }
    ...

ลบเนื้อหาในคลิปบอร์ดหลังจากระยะเวลาที่กำหนด

หากแอปพลิเคชันมีไว้เพื่อเรียกใช้ใน Android เวอร์ชันต่ำกว่า Android 10 (ระดับ API 29) แอปพลิเคชันใดก็ตามที่ทำงานในเบื้องหลังจะเข้าถึงข้อมูลในคลิปบอร์ดได้ เพื่อลดความเสี่ยงนี้ คุณควรใช้ฟังก์ชันที่ล้างข้อมูลที่คัดลอกไปยังคลิปบอร์ดหลังจากระยะเวลาหนึ่ง ฟังก์ชันนี้จะดำเนินการโดยอัตโนมัติตั้งแต่ Android 13 (ระดับ API 33) เป็นต้นไป สำหรับ Android เวอร์ชันเก่ากว่า คุณสามารถลบได้โดยใส่ข้อมูลโค้ดต่อไปนี้ ไว้ในโค้ดของแอปพลิเคชัน

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);

แหล่งข้อมูล