WebView – การรวมไฟล์ที่ไม่ปลอดภัย

หมวดหมู่ OWASP: MASVS-STORAGE: Storage

ภาพรวม

เอกสารนี้ครอบคลุมปัญหาหลายอย่างที่เกี่ยวข้องกับการรวมไฟล์ซึ่งมีการบรรเทาปัญหาที่คล้ายกัน ปัญหาเหล่านี้มุ่งเน้นไปที่ช่องโหว่ที่เกิดจากการเข้าถึงไฟล์ภายใน WebView และมีตั้งแต่WebSettingsที่เป็นอันตรายซึ่งอนุญาตให้เข้าถึงไฟล์ หรือเปิดใช้ JavaScript ไปจนถึงเมธอด WebKit ที่สร้างคำขอการเลือกไฟล์ เอกสารนี้จะเป็นประโยชน์หากคุณกำลังมองหาคำแนะนำเกี่ยวกับ การแก้ไขปัญหาภายใน WebView ที่เกิดจากการใช้สคีม file:// การเข้าถึงไฟล์ในเครื่องแบบไม่จำกัด และ Cross-Site Scripting

กล่าวอย่างเจาะจงคือเอกสารนี้ครอบคลุมหัวข้อต่อไปนี้

  • WebSettings เป็นคลาสที่มีเมธอดที่จัดการสถานะการตั้งค่าสำหรับ WebView วิธีการเหล่านี้สามารถเปิด 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:// เข้าถึงไฟล์ในเครื่องโดยพลการได้ ซึ่งรวมถึง คุกกี้ WebView และข้อมูลส่วนตัวของแอป นอกจากนี้ การใช้onShowFileChooser ยังช่วยให้ผู้ใช้เลือกและดาวน์โหลดไฟล์จากแหล่งที่มาที่ไม่น่าเชื่อถือได้ด้วย

วิธีการเหล่านี้อาจนำไปสู่การกรองข้อมูล PII, ข้อมูลเข้าสู่ระบบ หรือข้อมูลที่ละเอียดอ่อนอื่นๆ ออกได้ ทั้งนี้ขึ้นอยู่กับการกำหนดค่าแอปพลิเคชัน

การลดปัญหา

ตรวจสอบ URL ของไฟล์

หากแอปของคุณต้องเข้าถึงไฟล์ผ่าน file://URL คุณควรอนุญาตเฉพาะ 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

การลดปัญหา

ป้องกันไม่ให้ WebView โหลดไฟล์ในเครื่อง

เช่นเดียวกับความเสี่ยงก่อนหน้า คุณสามารถหลีกเลี่ยง XSS ที่อิงตามไฟล์ได้หากตั้งค่า setAllowFileAccess(), setAllowFileAccessFromFileURLs() และ setAllowUniversalAccessFromFileURLs() เป็น FALSE

ป้องกันไม่ให้ WebView เรียกใช้ JavaScript

ตั้งค่าเมธอด setJavascriptEnabled เป็น FALSE เพื่อไม่ให้ JavaScript เรียกใช้ภายใน WebView ได้

ตรวจสอบว่า WebView ไม่ได้โหลดเนื้อหาที่ไม่น่าเชื่อถือ

บางครั้งการเปิดใช้การตั้งค่าเหล่านี้ใน WebView ก็จำเป็น ในกรณีนี้ คุณต้องตรวจสอบว่ามีการโหลดเฉพาะเนื้อหาที่เชื่อถือได้ การจำกัด การเรียกใช้ JavaScript เฉพาะที่คุณควบคุมและไม่อนุญาต JavaScript ที่กำหนดเองเป็นวิธีที่ดีในการตรวจสอบว่าเนื้อหาน่าเชื่อถือ ไม่เช่นนั้น การป้องกันไม่ให้โหลดการรับส่งข้อความที่ไม่มีการเข้ารหัสจะช่วยให้มั่นใจได้ว่า WebView ที่มีการตั้งค่าที่เป็นอันตรายจะไม่สามารถโหลด HTTP URL ได้อย่างน้อยที่สุด ซึ่งทำได้ผ่านไฟล์ Manifest โดยตั้งค่า android:usesCleartextTraffic เป็น False หรือโดยการตั้งค่า Network Security Config ที่ไม่อนุญาตให้มีการเข้าชมแบบ HTTP


แหล่งข้อมูล