WebView – Liaisons natives

Catégorie OWASP : MASVS-PLATFORM : interaction avec la plate-forme

Présentation

Un pont natif, parfois appelé pont JavaScript, est un mécanisme qui facilite la communication entre une WebView et le code Android natif, obtenu en utilisant la addJavascriptInterface méthode. Cela permet une communication bidirectionnelle entre le code JavaScript exécuté dans la WebView et le code Java de l'application Android. La méthode addJavascriptInterface expose un objet Java à toutes les frames d'une WebView, et n'importe quelle frame peut accéder au nom de l'objet et appeler des méthodes sur celui-ci. Toutefois, l'application ne dispose d'aucun mécanisme pour vérifier l'origine de la frame appelante dans la WebView, ce qui soulève des problèmes de sécurité, car la fiabilité du contenu reste indéterminée.

Un pont natif peut également être implémenté avec des canaux de messages HTML à l'aide de WebViewCompat.postWebMessage ou WebMessagePort.postMessage d'Android pour communiquer avec le JavaScript Window.postMessage. WebViewCompat.postWebMessage et WebMessagePort.postMessage peuvent accepter les messages JavaScript envoyés via Window.postMessage, qui seront exécutés dans la WebView.

Les ponts natifs présentent plusieurs risques :

  • Ponts basés sur JavascriptInterface :
    • La méthode addJavascriptInterface injecte un objet Java fourni dans chaque frame de la WebView, y compris les iFrames, ce qui signifie qu'elle est susceptible d'être attaquée par des tiers malveillants qui injectent des frames dans un site Web légitime. Les applications ciblant le niveau d'API 16 ou antérieur sont particulièrement exposées aux attaques, car cette méthode peut être utilisée pour autoriser JavaScript à contrôler l'application hôte.
    • La réflexion du contenu fourni par l'utilisateur non approuvé dans les WebView compatibles avec les ponts natifs permet les attaques de script intersites (XSS).
  • Ponts basés sur MessageChannel :
    • L'absence de vérification de l'origine sur les points de terminaison des canaux de messages signifie que les messages seront acceptés de n'importe quel expéditeur, y compris ceux contenant du code malveillant.
    • Il est possible d'exposer accidentellement Java à un JavaScript arbitraire.

Impact

Les méthodes addJavascriptInterface, postWebMessage et postMessage peuvent être exploitées par des acteurs malveillants pour accéder, manipuler ou injecter du code qu'ils contrôlent dans une WebView. Cela peut entraîner la redirection des utilisateurs vers des sites malveillants, le chargement de contenu malveillant ou l'exécution de code malveillant sur leurs appareils, ce qui peut extraire des données sensibles ou permettre une élévation des privilèges.

Risque : risques liés à addJavascriptInterface

WebView implémente les fonctionnalités de base d'un navigateur, telles que le rendu des pages, la navigation et l'exécution de JavaScript. WebView peut être utilisé dans une application pour afficher du contenu Web dans une mise en page d'activité. L'implémentation d'un pont natif dans une WebView à l'aide de la méthode addJavascriptInterface peut créer des problèmes de sécurité tels que le script intersites (XSS) ou permettre aux pirates d'attaquer de charger du contenu non approuvé via l'injection d'interface et de manipuler l'application hôte de manière involontaire, en exécutant du code Java avec les autorisations de l'application hôte.

Stratégies d'atténuation

Désactiver JavaScript

Dans les scénarios où WebView ne nécessite pas JavaScript, n'appelez pas setJavaScriptEnabled dans WebSettings (par exemple, lors de l'affichage de contenu HTML statique). Par défaut, l'exécution de JavaScript est désactivée dans WebView.

Supprimer l'interface JavaScript lors du chargement de contenu non approuvé

Assurez-vous que les objets de l'interface JavaScript sont supprimés en appelant removeJavascriptInterface avant que le contenu non approuvé ne soit chargé par la WebView. Par exemple, cela peut être effectué lors d'un appel à shouldInterceptRequest.

Kotlin

webView.removeJavascriptInterface("myObject")

Java

webView.removeJavascriptInterface("myObject");

Ne charger le contenu Web que via HTTPS

Si vous devez charger du contenu non approuvé, assurez-vous que la WebView charge le contenu Web via une connexion chiffrée (consultez également nos consignes sur les communications en texte clair). Empêchez le chargement initial de la page sur des connexions non chiffrées en définissant android:usesCleartextTraffic sur false dans le fichier AndroidManifest ou en interdisant le trafic HTTP dans une configuration de sécurité réseau. Pour en savoir plus, consultez la usesCleartextTraffic documentation.

XML

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

Pour vous assurer que les redirections et la navigation ultérieure dans l'application ne se produisent pas sur un trafic non chiffré, recherchez le schéma HTTP dans loadUrl ou 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);
        }
    }
}

Valider le contenu non approuvé

Si des liens externes sont chargés dans une WebView, validez à la fois le schéma et l'hôte (domaines de la liste d'autorisation). Tous les domaines qui ne figurent pas dans la liste d'autorisation doivent être ouverts par le navigateur par défaut.

Ne pas charger de contenu non approuvé

Si possible, ne chargez que les URL et le contenu restreints détenus par le développeur de l'application dans WebView.

Ne pas exposer de données sensibles

Si votre application accède à des données sensibles avec une WebView, envisagez d'utiliser la clearCache méthode pour supprimer tous les fichiers stockés localement avant d'utiliser l' interface JavaScript. Vous pouvez également utiliser des en-têtes côté serveur, tels que no-store, pour indiquer qu'une application ne doit pas mettre en cache un contenu particulier.

Ne pas exposer de fonctionnalités sensibles

Si votre application nécessite des autorisations sensibles ou collecte des données sensibles, assurez-vous qu'elle est appelée à partir du code de l'application et qu'une divulgation visible est fournie aux utilisateurs. Évitez d'utiliser des interfaces JavaScript pour les opérations sensibles ou les données utilisateur.

Cibler le niveau d'API 21 ou supérieur

Une façon sécurisée d'utiliser la méthode addJavascriptInterface consiste à cibler le niveau d'API 21 ou supérieur en vous assurant que la méthode n'est appelée que lorsqu'elle est exécutée sur le niveau d'API 21 ou supérieur. Avant l'API 21, JavaScript pouvait utiliser la réflexion pour accéder aux champs publics d'un objet injecté.


Risque : risques liés à MessageChannel

L'absence de contrôle de l'origine dans postWebMessage() et postMessage() pourrait permettre aux pirates d'intercepter des messages ou d'envoyer des messages à des gestionnaires natifs.

Stratégies d'atténuation

Lorsque vous configurez postWebMessage() ou postMessage(), n'autorisez que les messages provenant de domaines approuvés en évitant d'utiliser * comme origine cible et en spécifiant explicitement le domaine d'envoi attendu.


Ressources