רכיבי WebView – הכללה של קובץ לא בטוח

קטגוריה ב-OWASP: MASVS-STORAGE: אחסון

סקירה כללית

במסמך הזה מפורטות כמה בעיות שקשורות להכללת קבצים, שיש להן פתרונות דומים. הבעיות האלה מתמקדות בנקודות חולשה שנובעות מגישה לקבצים ב-WebViews, ומשתנות מ-WebSettings מסוכן שמאפשר גישה לקבצים או מפעיל JavaScript, ועד לשיטה של WebKit שיוצרת בקשה לבחירת קובץ. המסמך הזה יעזור לכם לפתור בעיות ב-WebView שנובעות משימוש בסכימה file://, מגישה בלתי מוגבלת לקבצים מקומיים ומ-XSS (Cross-Site Scripting).

באופן ספציפי יותר, במסמך הזה מפורטים הנושאים הבאים:

  • WebSettings היא כיתה שמכילה שיטות שמנהלות את מצבי ההגדרה של WebViews. השיטות האלה עלולות לחשוף את WebView למתקפות שונות, שתוארו בהמשך. במסמך הזה נבחן את השיטות שקשורות לאופן הגישה לקובצים, ואת ההגדרה שמאפשרת להריץ JavaScript:
  • אפשר להשתמש בשיטות setAllowFileAccess, setAllowFileAccessFromFileURLs ו-setAllowUniversalAccessFromFileURLs כדי להעניק גישה לקבצים מקומיים באמצעות כתובת URL של סכימה של קובץ (file://). עם זאת, סקריפטים זדוניים עלולים לנצל אותן כדי לגשת לקבצים מקומיים שרירותיים שיש לאפליקציה גישה אליהם, כמו התיקייה /data/ שלהם. לכן, השיטות האלה סומנו כלא מאובטחות והוצאו משימוש ב-API 30 לטובת חלופות בטוחות יותר, כמו WebViewAssetLoader.
  • אפשר להשתמש בשיטה setJavascriptEnabled כדי להפעיל את ההרצה של JavaScript בתוך רכיבי WebView. כתוצאה מכך, האפליקציות חשופות לפרצת אבטחה מסוג XSS שמבוססת על קבצים. במיוחד כשהגדרתם שהם יאפשרו טעינה של קבצים מקומיים או תוכן אינטרנט לא מהימן שעשוי להכיל קוד שניתן להפעלה, או כשהגדרתם שהם יאפשרו גישה לקבצים שאפשר ליצור או לשנות על ידי מקורות חיצוניים, או כשהגדרתם שרכיבי WebView יוכלו להריץ JavaScript, המשתמשים והנתונים שלהם נמצאים בסיכון.
  • WebChromeClient.onShowFileChooser היא שיטה ששייכת לחבילה android.webkit, שמספקת כלים לגלישה באינטרנט. אפשר להשתמש בשיטה הזו כדי לאפשר למשתמשים לבחור קבצים ב-WebView. עם זאת, אפשר לנצל לרעה את התכונה הזו כי תצוגות ה-WebView לא אוכפות הגבלות על הקובץ שנבחר.

השפעה

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

סיכון: גישה מסוכנת לקבצים דרך file://

הפעלת ההרשאות setAllowFileAccess,‏ setAllowFileAccessFromFileURLs ו-setAllowUniversalAccessFromFileURLs עלולה לאפשר לכוונות זדוניות ולבקשות WebView עם הקשר file:// לגשת לקבצים מקומיים שרירותיים, כולל קובצי cookie של WebView ונתונים פרטיים של האפליקציה. בנוסף, השימוש בשיטה onShowFileChooser יכול לאפשר למשתמשים לבחור קבצים ולהוריד אותם ממקורות לא מהימנים.

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

פעולות מיטיגציה

אימות כתובות URL של קבצים

אם באפליקציה שלכם נדרשת גישה לקבצים דרך כתובות URL מסוג file://, חשוב להוסיף לרשימת ההיתרים רק כתובות URL ספציפיות שידועות כחוקיות, ולהימנע מטעויות נפוצות.

שימוש ב-WebViewAssetLoader

במקום השיטות שצוינו, צריך להשתמש ב-WebViewAssetLoader. השיטה הזו משתמשת בסכימה http(s)//: במקום בסכימה file:// כדי לגשת לנכסים של מערכת הקבצים המקומית, והיא לא חשופה להתקפה המתוארת.

Kotlin

val assetLoader: WebViewAssetLoader = Builder()
  .addPathHandler("/assets/", AssetsPathHandler(this))
  .build()

webView.setWebViewClient(object : WebViewClientCompat() {
  @RequiresApi(21)
  override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest): WebResourceResponse {
    return assetLoader.shouldInterceptRequest(request.url)
  }

  @Suppress("deprecation") // for API < 21
  override fun shouldInterceptRequest(view: WebView?, url: String?): WebResourceResponse {
    return assetLoader.shouldInterceptRequest(Uri.parse(url))
  }
})

val webViewSettings: WebSettings = webView.getSettings()
// Setting this off for security. Off by default for SDK versions >= 16.
webViewSettings.allowFileAccessFromFileURLs = false
// Off by default, deprecated for SDK versions >= 30.
webViewSettings.allowUniversalAccessFromFileURLs = false
// Keeping these off is less critical but still a good idea, especially if your app is not
// using file:// or content:// URLs.
webViewSettings.allowFileAccess = false
webViewSettings.allowContentAccess = false

// Assets are hosted under http(s)://appassets.androidplatform.net/assets/... .
// If the application's assets are in the "main/assets" folder this will read the file
// from "main/assets/www/index.html" and load it as if it were hosted on:
// https://appassets.androidplatform.net/assets/www/index.html
webView.loadUrl("https://appassets.androidplatform.net/assets/www/index.html")

Java

final WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
         .addPathHandler("/assets/", new AssetsPathHandler(this))
         .build();

webView.setWebViewClient(new WebViewClientCompat() {
    @Override
    @RequiresApi(21)
    public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
        return assetLoader.shouldInterceptRequest(request.getUrl());
    }

    @Override
    @SuppressWarnings("deprecation") // for API < 21
    public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
        return assetLoader.shouldInterceptRequest(Uri.parse(url));
    }
});

WebSettings webViewSettings = webView.getSettings();
// Setting this off for security. Off by default for SDK versions >= 16.
webViewSettings.setAllowFileAccessFromFileURLs(false);
// Off by default, deprecated for SDK versions >= 30.
webViewSettings.setAllowUniversalAccessFromFileURLs(false);
// Keeping these off is less critical but still a good idea, especially if your app is not
// using file:// or content:// URLs.
webViewSettings.setAllowFileAccess(false);
webViewSettings.setAllowContentAccess(false);

// Assets are hosted under http(s)://appassets.androidplatform.net/assets/... .
// If the application's assets are in the "main/assets" folder this will read the file
// from "main/assets/www/index.html" and load it as if it were hosted on:
// https://appassets.androidplatform.net/assets/www/index.html
webview.loadUrl("https://appassets.androidplatform.net/assets/www/index.html");

השבתת שיטות מסוכנות של WebSettings

כברירת מחדל, הערכים של השיטות setAllowFileAccess(), ‏ setAllowFileAccessFromFileURLs() ו-setAllowUniversalAccessFromFileURLs() מוגדרים ל-TRUE ברמת API 29 ומטה, ול-FALSE ברמת API 30 ואילך.

אם יש צורך להגדיר WebSettings אחרים, מומלץ להשבית את השיטות האלה באופן מפורש, במיוחד באפליקציות שמטרגטות רמות API שקטנות מ-29 או שוות לה.


סיכון: XSS מבוסס-קובץ

הגדרת השיטה setJavacriptEnabled ל-TRUE מאפשרת להריץ JavaScript ב-WebView. בשילוב עם הפעלת הגישה לקובץ כפי שמתואר למעלה, אפשר לבצע XSS מבוסס-קובץ באמצעות הפעלת קוד בקבצים שרירותיים או באתרים זדוניים שנפתחים ב-WebView.

פעולות מיטיגציה

מניעת טעינת קבצים מקומיים ב-WebViews

בדומה לסיכונים הקודמים, אפשר למנוע XSS שמבוסס על קבצים אם ההגדרות של setAllowFileAccess(),‏ setAllowFileAccessFromFileURLs() ו-setAllowUniversalAccessFromFileURLs() מוגדרות כ-FALSE.

מניעת הפעלת JavaScript ברכיבי WebView

מגדירים את השיטה setJavascriptEnabled לערך FALSE כדי שלא ניתן יהיה להריץ JavaScript בתוך רכיבי WebView.

מוודאים שרכיבי WebView לא טוענים תוכן לא מהימן

לפעמים צריך להפעיל את ההגדרות האלה ב-WebViews. במקרה כזה, חשוב לוודא שרק תוכן מהימן נטען. אחת הדרכים הטובות להבטיח שהתוכן מהימן היא להגביל את ההפעלה של JavaScript רק לקוד שאתם שולטים בו ולא לאפשר הפעלה של JavaScript שרירותי. לחלופין, אפשר למנוע את טעינת התנועה בטקסט ללא הצפנה כדי לוודא שרכיבי WebView עם הגדרות מסוכנות לא יוכלו לטעון לפחות כתובות URL מסוג HTTP. אפשר לעשות זאת דרך המניפסט, על ידי הגדרת android:usesCleartextTraffic לערך False, או על ידי הגדרת Network Security Config שאוסרת על תנועת HTTP.


משאבים