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

ผลกระทบ

ผลกระทบของการรวมไฟล์อาจขึ้นอยู่กับ `WebSettings` ที่กำหนดค่าไว้ใน WebView สิทธิ์ในไฟล์ที่กว้างเกินไปอาจทำให้ผู้โจมตีเข้าถึงไฟล์ในเครื่องและขโมยข้อมูลที่ละเอียดอ่อน, PII (ข้อมูลส่วนบุคคลที่ระบุตัวบุคคลนั้นได้) หรือข้อมูลแอปส่วนตัว การเปิดใช้การเรียกใช้ JavaScript อาจทำให้ผู้โจมตีเรียกใช้ JavaScript ภายใน WebView หรือในอุปกรณ์ของผู้ใช้ได้ ไฟล์ที่เลือกโดยใช้เมธอด onShowFileChooser อาจทำให้ความปลอดภัยของผู้ใช้ตกอยู่ในความเสี่ยงเนื่องจากเมธอดหรือ WebView ไม่สามารถตรวจสอบได้ว่าแหล่งที่มาของไฟล์น่าเชื่อถือ

ความเสี่ยง: การเข้าถึงไฟล์อย่างมีความเสี่ยงผ่าน file://

การเปิดใช้ setAllowFileAccess, setAllowFileAccessFromFileURLs และ setAllowUniversalAccessFromFileURLs อาจอนุญาตให้ Intent ที่เป็นอันตรายและคำขอ WebView ที่มีบริบท file:// เข้าถึงไฟล์ในเครื่องที่กำหนดเอง รวมถึง คุกกี้ WebView และข้อมูลส่วนตัวของแอป นอกจากนี้ การใช้เมธอด onShowFileChooser อาจอนุญาตให้ผู้ใช้เลือกและดาวน์โหลดไฟล์จากแหล่งที่มาที่ไม่น่าเชื่อถือ

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

วิธีบรรเทาปัญหา

ตรวจสอบ 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() จะตั้งค่าเป็น TRUE โดยค่าเริ่มต้นในระดับ API 29 ลงไป และ FALSE ในระดับ API 30 ขึ้นไป เมธอด setAllowFileAccessFromFileURLs() และ setAllowUniversalAccessFromFileURLs() จะตั้งค่าเป็น FALSE โดยค่าเริ่มต้นใน API ระดับ 16 ขึ้นไป (ค่าเริ่มต้นเหล่านี้มีการเปลี่ยนแปลงในระดับ API 16 เพื่อบังคับใช้การรักษาความปลอดภัยที่เข้มงวดขึ้นโดยจำกัดการเข้าถึงไฟล์ในเครื่องแบบ Same-Origin ซึ่งจะช่วยปกป้องแอปจาก Cross-site Scripting (XSS) ที่อาจเกิดขึ้นและการเข้าถึงไฟล์ที่ไม่ได้รับอนุญาต)

หากจำเป็นต้องกำหนดค่า WebSettings อื่นๆ คุณควรปิดใช้ เมธอดเหล่านี้อย่างชัดแจ้ง โดยเฉพาะอย่างยิ่งสำหรับแอปที่กำหนดเป้าหมาย API ระดับ 29 ลงไป


ความเสี่ยง: File-based XSS

การตั้งค่าเมธอด setJavacriptEnabled เป็น TRUE จะอนุญาตให้เรียกใช้ JavaScript ภายใน WebView และเมื่อรวมกับการเข้าถึงไฟล์ที่เปิดใช้ตามที่อธิบายไว้ก่อนหน้านี้ จะทำให้เกิด File-based XSS ได้ผ่านการเรียกใช้โค้ดภายในไฟล์ที่กำหนดเองหรือเว็บไซต์ที่เป็นอันตรายที่เปิดภายใน WebView

วิธีบรรเทาปัญหา

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

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

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

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

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

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


แหล่งข้อมูล