WebView – גשרים מקומיים

קטגוריה ב-OWASP: MASVS-PLATFORM: Platform Interaction

סקירה כללית

גשר מקורי, שנקרא לפעמים גשר JavaScript, הוא מנגנון שמקל על התקשורת בין WebView לבין קוד מקורי של Android. כדי להשתמש בו, צריך להשתמש בשיטה addJavascriptInterface. היא מאפשרת תקשורת דו-כיוונית בין קוד JavaScript שפועל ב-WebView לבין קוד Java של אפליקציית Android. השיטה addJavascriptInterface חושפת אובייקט Java לכל המסגרות של WebView, וכל מסגרת יכולה לגשת לשם האובייקט ולקרוא לשיטות שלו. עם זאת, אין מנגנון באפליקציה שמאפשר לאמת את המקור של המסגרת שקוראת בתוך WebView, ולכן יש חששות לגבי אבטחה כי מהימנות התוכן לא ברורה.

אפשר גם להטמיע גשר מקורי עם ערוצי הודעות HTML באמצעות WebViewCompat.postWebMessage או WebMessagePort.postMessage של Android כדי לתקשר עם Window.postMessage של JavaScript. ‫WebViewCompat.postWebMessage ו-WebMessagePort.postMessage יכולים לקבל הודעות JavaScript שנשלחות דרך Window.postMessage, שיופעלו ב-WebView.

יש כמה סיכונים שקשורים לגשרים מקוריים:

  • גשרים מבוססי JavascriptInterface:
    • השיטה addJavascriptInterface מחדירה אובייקט Java שסופק לכל פריים ב-WebView, כולל iframe, מה שאומר שהיא חשופה להתקפה של צדדים שלישיים זדוניים שמחדירים פריים לאתר לגיטימי. אפליקציות שמיועדות לרמת API 16 או לגרסאות קודמות נמצאות בסיכון גבוה במיוחד להתקפה, כי אפשר להשתמש בשיטה הזו כדי לאפשר ל-JavaScript לשלוט באפליקציית המארח.
    • שיקוף של תוכן שסופק על ידי משתמשים לא מהימנים ב-WebView עם הפעלת גשר מקורי מאפשר פרצת אבטחה XSS‏ (cross-site scripting).
  • גשרים שמבוססים על MessageChannel:
    • היעדר בדיקות מקור בנקודות הקצה של ערוץ ההודעות אומר שהודעות יתקבלו מכל שולח, כולל הודעות שמכילות קוד זדוני.
    • יכול להיות ש-Java ייחשף בטעות ל-JavaScript אקראי.

השפעה

גורמים זדוניים יכולים לנצל את השיטות addJavascriptInterface, postWebMessage ו-postMessage כדי לגשת ל-WebView, לתפעל אותו או להחדיר אליו קוד שהם שולטים בו. הדבר עלול להוביל להפניה של משתמשים לאתרים זדוניים, לטעינה של תוכן זדוני או להרצה של קוד זדוני במכשירים שלהם, שיכול לחלץ מידע אישי רגיש או להשיג הסלמת הרשאות (privilege escalation).

סיכון: סיכונים של addJavascriptInterface

רכיב WebView מטמיע פונקציות בסיסיות של דפדפן, כמו עיבוד דפים, ניווט וביצוע JavaScript. אפשר להשתמש ב-WebView בתוך אפליקציה כדי להציג תוכן מהאינטרנט כחלק מפריסת הפעילות. הטמעה של גשר מקורי ב-WebView באמצעות השיטה addJavascriptInterface עלולה ליצור בעיות אבטחה כמו פרצת אבטחה XSS‏ (cross-site scripting), או לאפשר לתוקפים לטעון תוכן לא מהימן באמצעות הזרקת ממשק ולשנות את אפליקציית המארח בדרכים לא רצויות, ולהריץ קוד Java עם ההרשאות של אפליקציית המארח.

אמצעי צמצום סיכונים

השבתה של JavaScript

בתרחישים שבהם WebView לא דורש JavaScript, אל תקראו ל-setJavaScriptEnabled בתוך WebSettings (לדוגמה, בזמן הצגת תוכן HTML סטטי). כברירת מחדל, ההרצה של JavaScript מושבתת ב-WebView.

הסרת ממשק JavaScript כשנטען תוכן לא מהימן

מוודאים שאובייקטים מממשק ה-JavaScript מוסרים על ידי קריאה ל-removeJavascriptInterface לפני שרכיב ה-WebView טוען תוכן לא מהימן. לדוגמה, אפשר לעשות זאת בקריאה ל-shouldInterceptRequest.

Kotlin

webView.removeJavascriptInterface("myObject")

Java

webView.removeJavascriptInterface("myObject");

טעינה של תוכן מהאינטרנט רק דרך HTTPS

אם אתם צריכים לטעון תוכן לא מהימן, צריך לוודא ש-WebView טוען תוכן מהאינטרנט באמצעות חיבור מוצפן (אפשר לעיין גם בהנחיות שלנו בנושא תקשורת בטקסט לא מוצפן). כדי למנוע את טעינת הדף הראשונית בחיבורים לא מוצפנים, צריך להגדיר את android:usesCleartextTraffic לערך false בקובץ AndroidManifest או לאסור תעבורת נתונים של HTTP בתצורה של אבטחת רשת. מידע נוסף זמין בusesCleartextTraffic.

Xml

<application
    android:usesCleartextTraffic="false">
    <!-- Other application elements -->
</application>

כדי לוודא שלא מתבצעות הפניות אוטומטיות וגלישה נוספת באפליקציה בתעבורה לא מוצפנת, צריך לבדוק את סכימת ה-HTTP ב-loadUrl או ב-shouldInterceptRequest:

Kotlin

fun loadSecureUrl(webView: WebView?, url: String?) {
    webView?.let { wv ->  // Ensure valid WebView and URL
        url?.let {
            try {
                val uri = URI(url)
                if (uri.scheme.equals("https", ignoreCase = true)) { // Enforce HTTPS scheme for security
                    wv.loadUrl(url)
                } else {
                    // Log an error or handle the case where the URL is not secure
                    System.err.println("Attempted to load a non-HTTPS URL: $url")
                }
            } catch (e: Exception) {
                // Handle exception for improper URL format
                System.err.println("Invalid URL syntax: $url")
            }
        }
    }
}

Java

public void loadSecureUrl(WebView webView, String url) {
    if (webView != null && url != null) { // Ensure valid WebView and URL
        try {
            URI uri = new URI(url);
            String scheme = uri.getScheme();
            if ("https".equalsIgnoreCase(scheme)) { // Enforce HTTPS scheme for security
                webView.loadUrl(url);
            } else {
                // Log an error or handle the case where the URL is not secure
                System.err.println("Attempted to load a non-HTTPS URL: " + url);
            }
        } catch (URISyntaxException e) {
            // Handle exception for improper URL format
            System.err.println("Invalid URL syntax: " + url);
        }
    }
}

אימות תוכן לא מהימן

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

לא לטעון תוכן לא מהימן

אם אפשר, טוענים ל-WebView רק כתובות URL ותוכן שנמצאים בבעלות מפתח האפליקציה.

לא לחשוף מידע אישי רגיש

אם האפליקציה שלכם ניגשת למידע אישי רגיש באמצעות WebView, כדאי להשתמש בשיטה clearCache כדי למחוק קבצים שנשמרו באופן מקומי, לפני שמשתמשים בממשק JavaScript. אפשר גם להשתמש בכותרות בצד השרת, כמו no-store, כדי לציין שאפליקציה לא צריכה לשמור במטמון תוכן מסוים.

לא לחשוף פונקציות רגישות

אם האפליקציה שלכם דורשת הרשאת גישה למידע רגיש או אוספת מידע אישי רגיש, עליכם לוודא שהיא נקראת מתוך קוד באפליקציה ושהמשתמשים מקבלים גילוי נאות במקום בולט. מומלץ להימנע משימוש בממשקי JavaScript לביצוע פעולות רגישות או לגישה לנתוני משתמשים.

רמת ה-API לטירגוט היא 21 ומעלה

דרך מאובטחת אחת להשתמש בשיטה addJavascriptInterface היא לטרגט רמת API‏ 21 ומעלה, על ידי הקפדה על כך שהשיטה תופעל רק כשמריצים אותה ברמת API‏ 21 ומעלה. לפני API 21, ‏ JavaScript יכול היה להשתמש בהשתקפות כדי לגשת לשדות הציבוריים של אובייקט מוזרק.


סיכון: סיכונים ב-MessageChannel

היעדר בקרת מקור ב-postWebMessage() וב-postMessage() עלול לאפשר לתוקפים ליירט הודעות או לשלוח הודעות לטיפול מקומי.

אמצעי צמצום סיכונים

כשמגדירים את postWebMessage() או postMessage(), צריך לאשר רק הודעות מדומיינים מהימנים. כדי לעשות זאת, לא משתמשים ב- * כמקור היעד, אלא מציינים במפורש את הדומיין שממנו צפויים לשלוח את ההודעות.


משאבים