WebView – natywne mosty

Kategoria OWASP: MASVS-PLATFORM: Platform Interaction

Przegląd

Most natywny, czasami nazywany mostem JavaScript, to mechanizm, który ułatwia komunikację między komponentem WebView a natywnym kodem Androida. Jest to możliwe dzięki użyciu metody addJavascriptInterface. Umożliwia to dwukierunkową komunikację między kodem JavaScript działającym w komponencie WebView a kodem Java aplikacji na Androida. Metoda addJavascriptInterface udostępnia obiekt Java wszystkim ramkom komponentu WebView, a każda ramka może uzyskać dostęp do nazwy obiektu i wywoływać jego metody. Aplikacja nie ma jednak możliwości sprawdzenia pochodzenia ramki wywołującej w komponencie WebView, co budzi obawy dotyczące bezpieczeństwa, ponieważ wiarygodność treści pozostaje nieokreślona.

Most natywny można też zaimplementować za pomocą kanałów wiadomości HTML, używając metod WebViewCompat.postWebMessage lub WebMessagePort.postMessage do komunikacji z metodą JavaScript Window.postMessage. Metody WebViewCompat.postWebMessage i WebMessagePort.postMessage mogą akceptować wiadomości JavaScript wysyłane za pomocą metody Window.postMessage, które będą wykonywane w komponencie WebView.

Z mostami natywnymi wiąże się kilka zagrożeń:

  • Mosty oparte na JavascriptInterface:
    • Metoda addJavascriptInterface wstrzykuje dostarczony obiekt Java do każdej ramki komponentu WebView, w tym do ramek iframe, co oznacza, że jest podatna na ataki złośliwych osób trzecich, które wstrzykują ramki do legalnej witryny. Aplikacje kierowane na poziom API 16 lub starszy są szczególnie narażone na ataki, ponieważ ta metoda może być używana do umożliwienia JavaScriptowi kontrolowania aplikacji hosta.
    • Odzwierciedlanie w komponentach WebView z włączonym mostem natywnym treści dostarczonych przez niezaufanych użytkowników umożliwia ataki typu cross-site scripting (XSS).
  • Mosty oparte na MessageChannel:
    • Brak sprawdzania pochodzenia w punktach końcowych kanału wiadomości oznacza, że wiadomości będą akceptowane od każdego nadawcy, w tym od tych, które zawierają złośliwy kod.
    • Możliwe jest przypadkowe udostępnienie Javy dowolnemu JavaScriptowi.

Wpływ

Złośliwi użytkownicy mogą wykorzystać metody addJavascriptInterface, postWebMessage i postMessage, aby uzyskać dostęp do kodu, który kontrolują, manipulować nim lub wstrzykiwać go do komponentu WebView. Może to spowodować przekierowanie użytkowników do złośliwych witryn, wczytanie złośliwych treści lub uruchomienie na ich urządzeniach złośliwego kodu, który może wyodrębnić dane wrażliwe lub uzyskać eskalację uprawnień.

Ryzyko: zagrożenia związane z addJavascriptInterface

WebView implementuje podstawowe funkcje przeglądarki, takie jak renderowanie stron, nawigacja i wykonywanie JavaScriptu. WebView można używać w aplikacji do wyświetlania treści internetowych jako części układu aktywności. Implementowanie mostu natywnego w komponencie WebView za pomocą metody addJavascriptInterface może powodować problemy z bezpieczeństwem, takie jak ataki typu cross-site scripting (XSS), lub umożliwiać atakującym wczytywanie niezaufanych treści przez wstrzykiwanie interfejsu i manipulowanie aplikacją hosta w niepożądany sposób, wykonując kod Java z uprawnieniami aplikacji hosta.

Środki zaradcze

Wyłącz JavaScript

W sytuacjach, w których komponent WebView nie wymaga JavaScriptu, nie wywołuj setJavaScriptEnabled w WebSettings (np. podczas wyświetlania statycznej treści HTML). Domyślnie wykonywanie JavaScriptu jest w komponencie WebView wyłączone.

Usuń interfejs JavaScript podczas wczytywania niezaufanych treści

Zanim komponent WebView wczyta niezaufane treści, usuń obiekty z interfejsu JavaScript, wywołując metodę removeJavascriptInterface. Możesz to zrobić np. w wywołaniu metody shouldInterceptRequest.

Kotlin

webView.removeJavascriptInterface("myObject")

Java

webView.removeJavascriptInterface("myObject");

Wczytuj treści internetowe tylko przez HTTPS

Jeśli musisz wczytać niezaufane treści, upewnij się, że komponent WebView wczytuje treści internetowe przez zaszyfrowane połączenie (zapoznaj się też z naszymi wytycznymi dotyczącymi komunikacji w postaci zwykłego tekstu). Aby zapobiec wczytywaniu początkowej strony przez niezaszyfrowane połączenia, ustaw android:usesCleartextTraffic na false w pliku AndroidManifest lub zablokuj ruch HTTP w konfiguracji zabezpieczeń sieciowych. Więcej informacji znajdziesz w dokumentacji usesCleartextTraffic.

XML

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

Aby mieć pewność, że przekierowania i dalsze przeglądanie aplikacji nie będą odbywać się przez niezaszyfrowany ruch, sprawdź schemat HTTP w loadUrl lub 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);
        }
    }
}

Sprawdź niezaufane treści

Jeśli w komponencie WebView wczytywane są linki zewnętrzne, sprawdź zarówno schemat, jak i hosta (domeny na liście dozwolonych). Wszystkie domeny, których nie ma na liście dozwolonych, powinny być otwierane w domyślnej przeglądarce.

Nie wczytuj niezaufanych treści

Jeśli to możliwe, wczytuj w komponencie WebView tylko adresy URL o ściśle określonym zakresie oraz treści należące do dewelopera aplikacji.

Nie udostępniaj informacji poufnych

Jeśli Twoja aplikacja uzyskuje dostęp do danych wrażliwych za pomocą komponentu WebView, przed użyciem interfejsu JavaScript rozważ użycie metody clearCache, aby usunąć wszystkie pliki przechowywane lokalnie. Możesz też użyć nagłówków po stronie serwera, np. no-store, aby wskazać, że aplikacja nie powinna zapisywać w pamięci podręcznej określonych treści.

Nie udostępniaj funkcji poufnych

Jeśli Twoja aplikacja wymaga uprawnień newralgicznych lub zbiera dane wrażliwe, upewnij się, że jest wywoływana z kodu w aplikacji i że użytkownicy są o tym wyraźnie informowani. Unikaj używania interfejsów JavaScript do operacji poufnych lub danych użytkownika.

Kieruj aplikację co najmniej na poziom API 21

Bezpiecznym sposobem używania metody addJavascriptInterface jest kierowanie aplikacji co najmniej na poziom API 21 przez zapewnienie, że metoda jest wywoływana tylko wtedy, gdy aplikacja działa na poziomie API 21 lub wyższym. Przed poziomem API 21 JavaScript mógł używać odbicia do uzyskiwania dostępu do pól publicznych wstrzykniętego obiektu.


Ryzyko: zagrożenia związane z MessageChannel

Brak kontroli pochodzenia w metodach postWebMessage() i postMessage() może umożliwić atakującym przechwytywanie wiadomości lub wysyłanie wiadomości do natywnych obsługi.

Środki zaradcze

Podczas konfigurowania metod postWebMessage() lub postMessage() zezwalaj tylko na wiadomości z zaufanych domen, unikając używania symbolu * jako docelowego pochodzenia, i zamiast tego wyraźnie określ oczekiwaną domenę wysyłającą.


Zasoby