การดีซีเรียลไลซ์ที่ไม่ปลอดภัย

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

ภาพรวม

เมื่อจัดเก็บหรือโอนข้อมูลออบเจ็กต์ Java จำนวนมาก การซีเรียลไลซ์ข้อมูลก่อนมักจะมีประสิทธิภาพมากกว่า จากนั้นแอปพลิเคชัน กิจกรรม หรือผู้ให้บริการที่รับข้อมูลจะดำเนินการดีซีเรียลไลซ์ข้อมูล ในสถานการณ์ปกติ ระบบจะซีเรียลไลซ์และดีซีเรียลไลซ์ข้อมูลโดยไม่ต้องให้ผู้ใช้ดำเนินการใดๆ อย่างไรก็ตาม ผู้ไม่ประสงค์ดีอาจใช้ความสัมพันธ์ที่เชื่อถือได้ระหว่างกระบวนการดีซีเรียลไลซ์กับออบเจ็กต์ที่ต้องการในทางที่ผิด เช่น ดักจับและแก้ไขออบเจ็กต์ที่ซีเรียลไลซ์แล้ว ซึ่งจะทำให้ผู้ไม่ประสงค์ดีสามารถโจมตีได้ เช่น การปฏิเสธการให้บริการ (DoS), การโจมตีเพื่อยกระดับสิทธิ์ และการดำเนินการโค้ดจากระยะไกล (RCE)

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

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

การโจมตีเพื่อใช้ประโยชน์จากการดีซีเรียลไลซ์ใน Android มี 3 ช่องทางหลักๆ ดังนี้

  • ใช้ประโยชน์จากสมมติฐานที่ไม่ถูกต้องของนักพัฒนาแอปที่ว่าการดีซีเรียลไลซ์ออบเจ็กต์ที่มาจากประเภทคลาสที่กำหนดเองนั้นปลอดภัย ในความเป็นจริงแล้ว ออบเจ็กต์ที่มาจากคลาสใดก็ได้อาจถูกแทนที่ด้วยเนื้อหาที่เป็นอันตราย ซึ่งในกรณีที่เลวร้ายที่สุดอาจรบกวนตัวโหลดคลาสของแอปพลิเคชันเดียวกันหรือแอปพลิเคชันอื่นๆ การรบกวนนี้อยู่ในรูปแบบของการแทรกค่าที่เป็นอันตราย ซึ่งอาจนำไปสู่การขโมยข้อมูลหรือการยึดบัญชีตามวัตถุประสงค์ของคลาส
  • ใช้ประโยชน์จากวิธีการดีซีเรียลไลซ์ที่ถือว่าไม่ปลอดภัยโดยการออกแบบ (เช่น CVE-2023-35669 ซึ่งเป็นข้อบกพร่องในการโจมตีเพื่อยกระดับสิทธิ์ในเครื่องที่อนุญาตให้แทรกโค้ด JavaScript ที่กำหนดเองผ่านช่องทางการดีซีเรียลไลซ์ Deep Link)
  • ใช้ประโยชน์จากข้อบกพร่องในตรรกะของแอปพลิเคชัน (เช่น CVE-2023-20963 ซึ่งเป็นข้อบกพร่องในการโจมตีเพื่อยกระดับสิทธิ์ในเครื่องที่อนุญาตให้แอปดาวน์โหลด และเรียกใช้โค้ดภายในสภาพแวดล้อมที่มีสิทธิ์ผ่านข้อบกพร่องภายในตรรกะพัสดุ WorkSource ของ Android)

ผลกระทบ

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

ความเสี่ยง: การดีซีเรียลไลซ์อินพุตที่ไม่น่าเชื่อถือ

ผู้โจมตีสามารถใช้ประโยชน์จากการขาดการยืนยันพัสดุภายในตรรกะของแอปพลิเคชันเพื่อแทรกออบเจ็กต์ที่กำหนดเอง ซึ่งเมื่อดีซีเรียลไลซ์แล้ว อาจบังคับให้แอปพลิเคชันเรียกใช้โค้ดที่เป็นอันตรายที่อาจส่งผลให้เกิดการปฏิเสธการให้บริการ (DoS), การโจมตีเพื่อยกระดับสิทธิ์ และการดำเนินการโค้ดจากระยะไกล (RCE)

การโจมตีประเภทนี้อาจเกิดขึ้นอย่างแนบเนียน เช่น แอปพลิเคชันอาจมี Intent ที่คาดหวังพารามิเตอร์เพียงรายการเดียว ซึ่งจะได้รับการดีซีเรียลไลซ์หลังจากผ่านการตรวจสอบแล้ว หากผู้โจมตีส่งพารามิเตอร์เพิ่มเติมที่เป็นอันตรายรายการที่ 2 ที่ไม่คาดคิดมาพร้อมกับพารามิเตอร์ที่คาดหวัง การดำเนินการนี้จะทำให้มีการดีซีเรียลไลซ์ออบเจ็กต์ข้อมูลทั้งหมดที่แทรก เนื่องจาก Intent จะถือว่าพารามิเตอร์เพิ่มเติมเป็น Bundle ผู้ใช้ที่เป็นอันตรายอาจใช้ลักษณะการทำงานนี้เพื่อแทรกข้อมูลออบเจ็กต์ ซึ่งเมื่อดีซีเรียลไลซ์แล้ว อาจนำไปสู่ RCE, การบุกรุกข้อมูล หรือข้อมูลสูญหาย

การลดความเสี่ยง

แนวทางปฏิบัติแนะนำคือให้ถือว่าข้อมูลที่ซีเรียลไลซ์แล้วทั้งหมดไม่น่าเชื่อถือและอาจเป็นอันตราย ตรวจสอบข้อมูลเพื่อยืนยันว่าข้อมูลเป็นคลาสและรูปแบบที่แอปพลิเคชันคาดหวัง เพื่อให้มั่นใจในความสมบูรณ์ของข้อมูลที่ซีเรียลไลซ์แล้ว

โซลูชันที่เป็นไปได้คือการใช้รูปแบบการมองไปข้างหน้าสำหรับ java.io.ObjectInputStream ไลบรารี การแก้ไขโค้ดที่รับผิดชอบการ ดีซีเรียลไลซ์จะช่วยให้มั่นใจได้ว่าระบบจะดีซีเรียลไลซ์เฉพาะชุดคลาสที่ระบุไว้อย่างชัดเจนภายใน Intent

ตั้งแต่ Android 13 (ระดับ API 33) เป็นต้นมา มีการอัปเดตเมธอดหลายรายการภายในคลาส Intent ซึ่งถือเป็นทางเลือกที่ปลอดภัยกว่าเมธอดเก่าที่เลิกใช้งานแล้วสำหรับการจัดการพัสดุ เมธอดใหม่ที่ปลอดภัยกว่าประเภทข้อมูล เช่น getParcelableExtra(java.lang.String, java.lang.Class) และ getParcelableArrayListExtra(java.lang.String, java.lang.Class) จะตรวจสอบประเภทข้อมูลเพื่อตรวจหาจุดอ่อนที่เกิดจากความไม่ตรงกันซึ่งอาจทำให้แอปพลิเคชัน ขัดข้องและอาจถูกใช้ประโยชน์เพื่อโจมตีเพื่อยกระดับสิทธิ์ เช่น CVE-2021-0928

ตัวอย่างต่อไปนี้แสดงวิธีใช้คลาส Parcel เวอร์ชันที่ปลอดภัย

สมมติว่าคลาส UserParcelable ใช้ Parcelable และสร้างอินสแตนซ์ของข้อมูลผู้ใช้ที่เขียนลงใน Parcel จากนั้นคุณสามารถใช้เมธอดที่ปลอดภัยกว่าประเภทข้อมูลต่อไปนี้ของ readParcelable เพื่ออ่านพัสดุที่ซีเรียลไลซ์แล้ว:

Kotlin

val parcel = Parcel.obtain()
val userParcelable = parcel.readParcelable(UserParcelable::class.java.classLoader)

Java

Parcel parcel = Parcel.obtain();
UserParcelable userParcelable = parcel.readParcelable(UserParcelable.class, UserParcelable.CREATOR);

โปรดสังเกตการใช้ UserParcelable.CREATOR ภายในเมธอดในตัวอย่าง Java ด้านบน พารามิเตอร์ที่จำเป็นนี้จะบอกเมธอด readParcelable ว่าควรคาดหวังประเภทใด และมีประสิทธิภาพมากกว่าเมธอด readParcelable เวอร์ชันที่เลิกใช้งานแล้ว

ความเสี่ยงเฉพาะ

ส่วนนี้รวบรวมความเสี่ยงที่ต้องใช้กลยุทธ์การลดความเสี่ยงที่ไม่เป็นมาตรฐานหรือได้รับการลดความเสี่ยงใน SDK ระดับหนึ่งและแสดงไว้ที่นี่เพื่อความสมบูรณ์

ความเสี่ยง: การดีซีเรียลไลซ์ออบเจ็กต์ที่ไม่ต้องการ

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

การลดความเสี่ยง

หากคลาสรับช่วง Serializable อินเทอร์เฟซ ตาม คำแนะนำของ OWASP คุณควรใช้เมธอด readObject ดังนี้เพื่อหลีกเลี่ยงไม่ให้มีการดีซีเรียลไลซ์ออบเจ็กต์ชุดหนึ่งในคลาส

Kotlin

@Throws(IOException::class)
private final fun readObject(in: ObjectInputStream) {
    throw IOException("Cannot be deserialized")
}

Java

private final void readObject(ObjectInputStream in) throws java.io.IOException {
    throw new java.io.IOException("Cannot be deserialized");
}

แหล่งข้อมูล