WebView - การโหลด URI ที่ไม่ปลอดภัย

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

ภาพรวม

การโหลด URI ที่ไม่ปลอดภัยเกิดขึ้นเมื่อแอปพลิเคชัน Android ประเมินความถูกต้องของ URI ไม่ถูกต้องก่อนที่จะโหลดลงใน WebView

สาเหตุพื้นฐานที่อยู่เบื้องหลังช่องโหว่ประเภทนี้คือ URI ประกอบด้วยหลายส่วน ซึ่งอย่างน้อยที่สุดจะต้องมีการยืนยัน (เช่น การเพิ่มในรายการที่อนุญาต) รูปแบบและโฮสต์ (ของส่วนการให้สิทธิ์) ก่อนที่จะโหลด URI ไปยัง WebView หรือใช้ภายในแอปพลิเคชัน

ข้อผิดพลาดที่พบบ่อยที่สุดมีดังนี้

  • ตรวจสอบโฮสต์แต่ไม่ตรวจสอบรูปแบบ ซึ่งจะทำให้ผู้โจมตีใช้รูปแบบต่างๆ เช่น http://, content:// หรือ javascript:// กับโฮสต์ที่ได้รับการตรวจสอบสิทธิ์ได้
  • การแยกวิเคราะห์ URI ไม่ถูกต้อง โดยเฉพาะในกรณีที่ได้รับ URI เป็นสตริง
  • ตรวจสอบรูปแบบ แต่ไม่ใช่โฮสต์ (การตรวจสอบโฮสต์ไม่เพียงพอ)

ในกรณีสุดท้ายนี้ มักเกิดขึ้นเมื่อแอปพลิเคชันต้องอนุญาตโดเมนย่อยที่กำหนดเองของโดเมนหลัก ดังนั้น แม้ว่าจะมีการดึงชื่อโฮสต์อย่างถูกต้อง แต่แอปจะใช้วิธีการต่างๆ เช่น startsWith, endsWith, หรือ contains ของคลาส java.lang.String เพื่อตรวจสอบว่ามีโดเมนหลักอยู่ในส่วนสตริงที่ดึงออกมาหรือไม่ หากใช้วิธีการเหล่านี้อย่างไม่ถูกต้อง อาจทำให้ได้ผลลัพธ์ที่ไม่ถูกต้องและบังคับให้แอปพลิเคชันเชื่อถือโฮสต์ที่อาจเป็นอันตรายอย่างไม่เหมาะสม

ผลกระทบ

ผลกระทบอาจแตกต่างกันไปตามบริบทที่ใช้โฮสต์ ในกรณีที่การโหลด URI ที่เป็นอันตราย (เช่น URI ที่หลีกเลี่ยงการกรอง/รายการที่อนุญาต) ใน WebView อาจทำให้เกิดการลักลอบใช้บัญชี (เช่น การใช้ฟิชชิง) การเรียกใช้โค้ด (เช่น การโหลด JavaScript ที่เป็นอันตราย) หรือการบุกรุกอุปกรณ์ (โค้ดที่ใช้ช่องโหว่ซึ่งส่งผ่านไฮเปอร์ลิงก์)

การลดปัญหา

เมื่อจัดการ URI สตริง สิ่งสำคัญคือต้องแยกวิเคราะห์สตริงเป็น URI และ ตรวจสอบทั้งรูปแบบและโฮสต์

Kotlin

fun isUriTrusted(incomingUri: String, trustedHostName: String): Boolean {
    try {
        val uri = Uri.parse(incomingUri)
        return uri.scheme == "https" && uri.host == trustedHostName
    } catch (e: NullPointerException) {
        throw NullPointerException("incomingUri is null or not well-formed")
    }
}

Java

public static boolean isUriTrusted(String incomingUri, String trustedHostName)
    throws NullPointerException {
        try {
            Uri uri = Uri.parse(incomingUri);
            return uri.getScheme().equals("https") &&
            uri.getHost().equals(trustedHostName);
        } catch (NullPointerException e) {
            throw new NullPointerException(
                "incomingUri is null or not well-formed");
        }
    }

สำหรับการตรวจสอบโฮสต์ หลังจากแยกส่วน URI ที่เกี่ยวข้องแล้ว คุณควร ตรวจสอบทั้งส่วน (ไม่ใช่บางส่วน) เพื่อระบุได้อย่างถูกต้องว่า โฮสต์เชื่อถือได้หรือไม่ เมื่อหลีกเลี่ยงการใช้วิธีการต่างๆ เช่น startsWith หรือ endsWith ไม่ได้ คุณควรใช้ไวยากรณ์ที่ถูกต้องและไม่มองข้ามอักขระหรือสัญลักษณ์ที่จำเป็น (เช่น endsWith ต้องมีอักขระจุด "." ก่อนชื่อโดเมนเพื่อให้ตรงกันอย่างถูกต้อง) การละเลยอักขระเหล่านี้อาจทำให้การจับคู่ไม่ถูกต้องและกระทบต่อความปลอดภัย เนื่องจากโดเมนย่อยซ้อนกันได้ไม่จำกัด การจับคู่นิพจน์ทั่วไปจึงไม่ใช่กลยุทธ์ที่แนะนำสำหรับการตรวจสอบชื่อโฮสต์

ผู้ร่วมให้ข้อมูล: Dimitrios Valsamaras และ Michael Peck จาก Microsoft Threat Intelligence

แหล่งข้อมูล