หมวดหมู่ OWASP: MASVS-PLATFORM: การโต้ตอบกับแพลตฟอร์ม
ภาพรวม
การเปลี่ยนเส้นทาง Intent เกิดขึ้นเมื่อผู้โจมตีสามารถควบคุมเนื้อหาของ Intent ที่ใช้เปิดคอมโพเนนต์ใหม่ในบริบทของแอปที่มีช่องโหว่ได้บางส่วนหรือทั้งหมด
คุณระบุ Intent ที่ใช้เปิดคอมโพเนนต์ใหม่ได้หลายวิธี
โดยส่วนใหญ่จะระบุเป็น Intent ที่ซีเรียลไลซ์ในฟิลด์ extras หรือมาร์ชัล
เป็นสตริงและแยกวิเคราะห์ การควบคุมพารามิเตอร์บางส่วนอาจทำให้เกิดผลลัพธ์เดียวกันได้เช่นกัน
ผลกระทบ
ผลกระทบอาจแตกต่างกันไป ผู้โจมตีอาจเรียกใช้ฟีเจอร์ภายในในแอปที่มีช่องโหว่ หรืออาจเข้าถึงคอมโพเนนต์ส่วนตัว เช่น ออบเจ็กต์ ContentProvider ที่ไม่ได้ส่งออก
การลดปัญหา
โดยทั่วไปแล้ว อย่าแสดงฟีเจอร์ที่เกี่ยวข้องกับการเปลี่ยนเส้นทางเจตนาที่ซ้อนกัน ในกรณีที่หลีกเลี่ยงไม่ได้ ให้ใช้วิธีการลดความเสี่ยงต่อไปนี้
- ล้างข้อมูลที่รวมไว้อย่างเหมาะสม โปรดอย่าลืมตรวจสอบ
หรือล้างการแจ้งว่าไม่เหมาะสม (
FLAG_GRANT_READ_URI_PERMISSION, FLAG_GRANT_WRITE_URI_PERMISSION, FLAG_GRANT_PERSISTABLE_URI_PERMISSION, and FLAG_GRANT_PREFIX_URI_PERMISSION) และตรวจสอบว่าระบบ เปลี่ยนเส้นทางเจตนาไปที่ใดIntentSanitizerช่วยคุณในกระบวนการนี้ได้ - ใช้ออบเจ็กต์
PendingIntentซึ่งจะป้องกันไม่ให้มีการส่งออกคอมโพเนนต์และทําให้ Intent การดําเนินการเป้าหมายเปลี่ยนแปลงไม่ได้
แอปจะตรวจสอบได้ว่ามีการเปลี่ยนเส้นทาง Intent ไปที่ใดโดยใช้เมธอดต่างๆ เช่น
ResolveActivity
Kotlin
val intent = getIntent()
// Get the component name of the nested intent.
val forward = intent.getParcelableExtra<Parcelable>("key") as Intent
val name: ComponentName = forward.resolveActivity(packageManager)
// Check that the package name and class name contain the expected values.
if (name.packagename == "safe_package" && name.className == "safe_class") {
// Redirect the nested intent.
startActivity(forward)
}
Java
Intent intent = getIntent()
// Get the component name of the nested intent.
Intent forward = (Intent) intent.getParcelableExtra("key");
ComponentName name = forward.resolveActivity(getPackageManager());
// Check that the package name and class name contain the expected values.
if (name.getPackageName().equals("safe_package") &&
name.getClassName().equals("safe_class")) {
// Redirect the nested intent.
startActivity(forward);
}
แอปสามารถใช้ IntentSanitizer โดยใช้ตรรกะที่คล้ายกับ
ต่อไปนี้
Kotlin
val intent = IntentSanitizer.Builder()
.allowComponent("com.example.ActivityA")
.allowData("com.example")
.allowType("text/plain")
.build()
.sanitizeByThrowing(intent)
Java
Intent intent = new IntentSanitizer.Builder()
.allowComponent("com.example.ActivityA")
.allowData("com.example")
.allowType("text/plain")
.build()
.sanitizeByThrowing(intent);
การปกป้องเริ่มต้น
Android 16 เปิดตัวโซลูชันการปิดช่องโหว่ด้านความปลอดภัยโดยค่าเริ่มต้นเพื่อIntent การเจาะช่องโหว่จากการเปลี่ยนเส้นทาง ในกรณีส่วนใหญ่ แอปที่ใช้ Intent จะไม่พบปัญหาความเข้ากันได้
เลือกไม่ใช้การจัดการการเปลี่ยนเส้นทางตามความตั้งใจ
Android 16 เปิดตัว API ใหม่ที่อนุญาตให้แอปเลือกไม่ใช้การปกป้องความปลอดภัยในการเปิดตัว ซึ่งอาจจำเป็นในบางกรณีที่ลักษณะการทำงานด้านความปลอดภัยเริ่มต้น รบกวน Use Case ที่ถูกต้องของแอป
ใน Android 16 คุณสามารถเลือกไม่รับการป้องกันความปลอดภัยได้โดยใช้เมธอด
removeLaunchSecurityProtection() ในออบเจ็กต์ Intent เช่น
val i = intent
val iSublevel: Intent? = i.getParcelableExtra("sub_intent")
iSublevel?.removeLaunchSecurityProtection() // Opt out from hardening
iSublevel?.let { startActivity(it) }
ความผิดพลาดที่พบบ่อย
- ตรวจสอบว่า
getCallingActivity()แสดงผลค่าที่ไม่ใช่ค่า Null หรือไม่ แอปที่เป็นอันตราย อาจให้ค่า Null สำหรับฟังก์ชันนี้ - สมมติว่า
checkCallingPermission()ทำงานในทุกบริบท หรือเมธอด จะโยนข้อยกเว้นเมื่อเมธอดนั้นส่งคืนจำนวนเต็ม
ฟีเจอร์การแก้ไขข้อบกพร่อง
สำหรับแอปที่กำหนดเป้าหมายเป็น Android 12 (ระดับ API 31) ขึ้นไป คุณสามารถเปิดใช้ฟีเจอร์การแก้ไขข้อบกพร่องซึ่งในบางกรณีจะช่วยตรวจหาว่าแอปเปิด Intent อย่างไม่ปลอดภัยหรือไม่
หากแอปของคุณดำเนินการทั้ง 2 อย่างต่อไปนี้ ระบบจะตรวจพบการเปิดใช้ Intent ที่ไม่ปลอดภัย และจะเกิดการละเมิดStrictMode
- แอปของคุณยกเลิกการส่ง Intent ที่ซ้อนกันจากส่วนเสริมของ Intent ที่ส่งแล้ว
- แอปของคุณจะเริ่มคอมโพเนนต์ของแอปทันทีโดยใช้ Intent ที่ซ้อนกันนั้น เช่น
ส่ง Intent ไปยัง
startActivity(),startService()หรือbindService()