![github-actions[bot]](/assets/img/avatar_default.png)
* Create rule S7409 * Initial commit * Use double code tags everywhere * Rephrase the Ask Yourself Whether section --------- Co-authored-by: egon-okerman-sonarsource <egon-okerman-sonarsource@users.noreply.github.com> Co-authored-by: Egon Okerman <egon.okerman@sonarsource.com>
138 lines
5.7 KiB
Plaintext
138 lines
5.7 KiB
Plaintext
Using Javascript interfaces in WebViews is unsafe as it allows JavaScript to invoke Java methods,
|
|
potentially giving attackers access to data or sensitive app functionality. WebViews might include
|
|
untrusted sources such as third-party iframes, making this functionality particularly risky. As
|
|
Javascript interfaces are passed to every frame in the WebView, those iframes are also able to
|
|
access the exposed Java methods.
|
|
|
|
== Ask Yourself Whether
|
|
|
|
* The content in the WebView is fully trusted and secure.
|
|
* Potentially untrusted iframes could be loaded in the WebView.
|
|
* The Javascript interface has to be exposed for the entire lifecycle of the WebView.
|
|
* The exposed Java methods will accept input from potentially untrusted sources.
|
|
|
|
There is a risk if you answered yes to any of these questions.
|
|
|
|
== Recommended Secure Coding Practices
|
|
|
|
=== Disable JavaScript
|
|
|
|
If it is possible to disable JavaScript in the WebView, this is the most secure option. By default,
|
|
JavaScript is disabled in a WebView, so you do not need to explicitly call
|
|
``webSettings.setJavaScriptEnabled(true)`` in your ``WebSettings`` configuration. Of course, sometimes
|
|
it is necessary to enable JavaScript, in which case the following recommendations should be considered.
|
|
|
|
=== Remove JavaScript interface when loading untrusted content
|
|
|
|
JavaScript interfaces can be removed at a later point. It is recommended to remove the JavaScript
|
|
interface when it is no longer needed. If it is needed for a longer time, consider removing it before
|
|
loading untrusted content. This can be done by calling ``webView.removeJavascriptInterface("interfaceName")``.
|
|
|
|
A good place to do this is inside the ``shouldInterceptRequest`` method of a ``WebViewClient``, where you can
|
|
check the URL or resource being loaded and remove the interface if the content is untrusted.
|
|
|
|
=== Alternative methods to implement native bridges
|
|
|
|
If a native bridge has to be added to the WebView, and it is impossible to remove it at a later point,
|
|
consider using an alternative method that offers more control over the communication flow.
|
|
``WebViewCompat.postWebMessage``/``WebViewCompat.addWebMessageListener`` and ``WebMessagePort.postMessage``
|
|
offer more ways to validate incoming and outgoing messages, such as by being able to restrict the origins
|
|
that can send messages to the JavaScript bridge.
|
|
|
|
== Sensitive Code Example
|
|
|
|
[source,kotlin]
|
|
----
|
|
class ExampleActivity : AppCompatActivity() {
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
|
|
val webView = WebView(this)
|
|
webView.settings.javaScriptEnabled = true
|
|
webView.addJavascriptInterface(JavaScriptBridge(), "androidBridge") // Sensitive
|
|
}
|
|
|
|
inner class JavaScriptBridge {
|
|
@JavascriptInterface
|
|
fun accessUserData(userId): String {
|
|
return getUserData(userId)
|
|
}
|
|
}
|
|
}
|
|
----
|
|
|
|
== Compliant Solution
|
|
|
|
The most secure option is to disable JavaScript entirely.
|
|
|
|
[source,kotlin]
|
|
----
|
|
class ExampleActivity : AppCompatActivity() {
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
|
|
val webView = WebView(this)
|
|
webView.settings.javaScriptEnabled = false
|
|
}
|
|
}
|
|
----
|
|
|
|
If possible, remove the JavaScript interface after it is no longer needed, or before loading any untrusted content.
|
|
|
|
[source,kotlin]
|
|
----
|
|
class ExampleActivity : AppCompatActivity() {
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
|
|
val webView = WebView(this)
|
|
webView.settings.javaScriptEnabled = true
|
|
|
|
webView.addJavascriptInterface(JavaScriptBridge(), "androidBridge")
|
|
|
|
// Sometime later, before unsafe content is loaded, remove the JavaScript interface
|
|
webView.removeJavascriptInterface("androidBridge")
|
|
}
|
|
}
|
|
----
|
|
|
|
If a JavaScript bridge must be used, consider using ``WebViewCompat.addWebMessageListener`` instead. This allows you to restrict the origins that can send messages to the JavaScript bridge.
|
|
|
|
[source,kotlin]
|
|
----
|
|
class ExampleActivity : AppCompatActivity() {
|
|
private val ALLOWED_ORIGINS = setOf("https://example.com")
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
|
|
val webView = WebView(this)
|
|
webView.settings.javaScriptEnabled = true
|
|
|
|
WebViewCompat.addWebMessageListener(
|
|
webView, "androidBridge", ALLOWED_ORIGINS, // Only allow messages from these origins
|
|
object : WebViewCompat.WebMessageListener {
|
|
override fun onPostMessage(
|
|
view: WebView,
|
|
message: WebMessageCompat,
|
|
sourceOrigin: Uri,
|
|
isMainFrame: Boolean,
|
|
replyProxy: JavaScriptReplyProxy
|
|
) {
|
|
// Handle the message
|
|
}
|
|
}
|
|
)
|
|
}
|
|
}
|
|
----
|
|
|
|
== See
|
|
|
|
* Android Documentation - https://developer.android.com/privacy-and-security/risks/insecure-webview-native-bridges[Insecure WebView native bridges]
|
|
* Android Documentation - https://developer.android.com/reference/androidx/webkit/WebViewCompat[WebViewCompat API reference]
|
|
* OWASP - https://owasp.org/Top10/A05_2021-Security_Misconfiguration/[Top 10 2021 Category A5 - Security Misconfiguration]
|
|
* OWASP - https://owasp.org/www-project-mobile-top-10/2023-risks/m4-insufficient-input-output-validation.html[Mobile Top 10 2024 Category M4 - Insufficient Input/Output Validation]
|
|
* OWASP - https://owasp.org/www-project-mobile-top-10/2023-risks/m8-security-misconfiguration.html[Mobile Top 10 2024 Category M8 - Security Misconfiguration]
|
|
* CWE - https://cwe.mitre.org/data/definitions/79[CWE-79 - Improper Neutralization of Input During Web Page Generation]
|