תצוגות אינטרנט – טעינת URI לא בטוחה

קטגוריית OWASP: MASVS-CODE: איכות הקוד

סקירה כללית

טעינה של URI לא בטוח מתרחשת כשאפליקציית Android לא מצליחה להעריך בצורה נכונה את התוקף של URI לפני שהיא טוענת אותו ל-WebView.

הסיבה הבסיסית לפגיעות מסוג זה היא שURI מורכב מכמה חלקים, שלפחות הסכימה והמארח (של חלק הרשות) צריכים לעבור אימות (למשל, להיכלל ברשימת ההיתרים) לפני ש-URI נטען ל-WebView או לפני שהאפליקציה משתמשת בו באופן פנימי.

הטעויות הנפוצות ביותר הן:

  • הבדיקה מתבצעת רק לגבי המארח ולא לגבי הסכימה, ולכן תוקף יכול להשתמש בסכימות כמו http://, content:// או javascript:// עם מארח מאומת.
  • הניתוח של ה-URI נכשל, במיוחד במקרים שבהם ה-URI מתקבל כמחרוזת.
  • אימות הסכימה אבל לא המארח (אימות מארח לא מספיק).

במקרה האחרון, זה קורה בדרך כלל כשהאפליקציה צריכה לאפשר תתי-דומיינים שרירותיים של דומיין ראשי. לכן, גם אם שם המארח חולץ בצורה נכונה, האפליקציה משתמשת בשיטות כמו startsWith, endsWith, או contains של המחלקה java.lang.String כדי לאמת את הנוכחות של דומיין ראשי בקטע המחרוזת שחולץ. שימוש לא נכון בשיטות האלה עלול להוביל לתוצאות שגויות ולגרום לאפליקציה לבטוח באופן לא תקין במארח שעלול להיות זדוני.

השפעה

ההשפעה יכולה להיות שונה בהתאם להקשר שבו נעשה שימוש במארח. במקרים שבהם טעינה של מזהה 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

משאבים