mirror of
https://github.com/github/codeql.git
synced 2026-03-06 15:49:08 +01:00
Merge pull request #5846 from atorralba/atorralba/promote-unsafe-android-webview-fetch
Java: Promote Unsafe resource loading in Android WebView from experimental
This commit is contained in:
35
java/ql/src/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp
Normal file
35
java/ql/src/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp
Normal file
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>Android WebViews that allow externally controlled URLs to be loaded, and whose JavaScript interface is enabled, are potentially vulnerable to cross-site scripting and sensitive resource disclosure attacks.</p>
|
||||
<p>A <code>WebView</code> whose <code>WebSettings</code> object has called <code>setAllowFileAccessFromFileURLs(true)</code> or <code>setAllowUniversalAccessFromFileURLs(true)</code> must not load any untrusted web content.</p>
|
||||
<p>Enabling these settings allows malicious scripts loaded in a <code>file://</code> context to launch cross-site scripting attacks, accessing arbitrary local files including WebView cookies, session tokens, private app data or even credentials used on arbitrary web sites.</p>
|
||||
<p>This query detects the following two scenarios:</p>
|
||||
<ol>
|
||||
<li>A vulnerability introduced by WebViews when JavaScript is enabled and remote inputs are allowed.</li>
|
||||
<li>A more severe vulnerability when "allow cross-origin resource access" is also enabled. This setting was deprecated in API level 30 (Android 11), but most devices are still affected, especially since some Android phones are updated slowly or no longer updated at all.</li>
|
||||
</ol>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>Only allow trusted web content to be displayed in WebViews when JavaScript is enabled. Disallow cross-origin resource access in WebSettings to reduce the attack surface.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>The following example shows both 'BAD' and 'GOOD' configurations. In the 'BAD' configuration, JavaScript and the allow access setting are enabled and URLs are loaded from externally controlled inputs. In the 'GOOD' configuration, JavaScript is disabled or only trusted web content is allowed to be loaded.</p>
|
||||
<sample src="UnsafeAndroidAccess.java" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
Google Help: <a href="https://support.google.com/faqs/answer/7668153?hl=en">Fixing a File-based XSS Vulnerability</a>
|
||||
</li>
|
||||
<li>
|
||||
OWASP: <a href="https://github.com/OWASP/owasp-mstg/blob/master/Document/0x05h-Testing-Platform-Interaction.md#testing-javascript-execution-in-webviews-mstg-platform-5">Testing JavaScript Execution in WebViews (MSTG-PLATFORM-5)</a>
|
||||
</li>
|
||||
<li>
|
||||
OWASP: <a href="https://github.com/OWASP/owasp-mstg/blob/master/Document/0x05h-Testing-Platform-Interaction.md#testing-webview-protocol-handlers-mstg-platform-6">Testing WebView Protocol Handlers (MSTG-PLATFORM-6)</a>
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
21
java/ql/src/Security/CWE/CWE-749/UnsafeAndroidAccess.ql
Normal file
21
java/ql/src/Security/CWE/CWE-749/UnsafeAndroidAccess.ql
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @name Unsafe resource fetching in Android WebView
|
||||
* @description JavaScript rendered inside WebViews can access protected
|
||||
* application files and web resources from any origin exposing them to attack.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @id java/android/unsafe-android-webview-fetch
|
||||
* @tags security
|
||||
* external/cwe/cwe-749
|
||||
* external/cwe/cwe-079
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.security.UnsafeAndroidAccessQuery
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, FetchUntrustedResourceConfiguration conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Unsafe resource fetching in Android WebView due to $@.",
|
||||
source.getNode(), sink.getNode().(UrlResourceSink).getSinkType()
|
||||
@@ -1,31 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>Android WebViews that allow loading URLs controlled by external inputs and whose JavaScript interface is enabled are potentially vulnerable to cross-site scripting and sensitive resource disclosure attacks.</p>
|
||||
<p>A <code>WebView</code> whose <code>WebSettings</code> object has <code>setAllowFileAccessFromFileURLs(true)</code> or <code>setAllowUniversalAccessFromFileURLs(true)</code> called must not load any untrusted web content.</p>
|
||||
<p>Enabling these settings allows malicious scripts loaded in a <code>file://</code> context to launch cross-site scripting attacks, either accessing arbitrary local files including WebView cookies, session tokens, private app data or even credentials used on arbitrary web sites.</p>
|
||||
<p>This query detects the following two scenarios:</p>
|
||||
<ol>
|
||||
<li>Vulnerability introduced by WebViews with JavaScript enabled and remote inputs allowed.</li>
|
||||
<li>A more severe vulnerability when allowing cross-origin resource access is also enabled. The setting was deprecated in API level 30 (Android 11), but most devices are still affected, especially given that some Android phones are updated slowly or no longer updated at all.</li>
|
||||
</ol>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>Only allow trusted web content to be displayed in WebViews when JavaScript is enabled. Disallow cross-origin resource access in WebSetting to reduce the attack surface.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>The following example shows both 'BAD' and 'GOOD' configurations. In the 'BAD' configuration, setting is enabled and JavaScript is enabled while URLs are loaded from externally controlled inputs. In the 'GOOD' configuration, JavaScript is disabled or only trusted web content is allowed to be loaded.</p>
|
||||
<sample src="UnsafeAndroidAccess.java" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
<a href="https://cwe.mitre.org/data/definitions/749.html">CWE-749</a>
|
||||
<a href="https://support.google.com/faqs/answer/7668153?hl=en">Fixing a File-based XSS Vulnerability</a>
|
||||
<a href="https://github.com/OWASP/owasp-mstg/blob/master/Document/0x05h-Testing-Platform-Interaction.md">OWASP - Testing WebView Protocol Handlers (MSTG-PLATFORM-5 and MSTG-PLATFORM-6)</a>
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,130 +0,0 @@
|
||||
/**
|
||||
* @name Unsafe resource fetching in Android webview
|
||||
* @description JavaScript rendered inside WebViews can access any protected
|
||||
* application file and web resource from any origin
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @id java/android/unsafe-android-webview-fetch
|
||||
* @tags security
|
||||
* external/cwe/cwe-749
|
||||
* external/cwe/cwe-079
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.frameworks.android.Intent
|
||||
import semmle.code.java.frameworks.android.WebView
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* Methods allowing any-local-file and cross-origin access in the WebSettings class
|
||||
*/
|
||||
class CrossOriginAccessMethod extends Method {
|
||||
CrossOriginAccessMethod() {
|
||||
this.getDeclaringType() instanceof TypeWebSettings and
|
||||
(
|
||||
this.hasName("setAllowUniversalAccessFromFileURLs") or
|
||||
this.hasName("setAllowFileAccessFromFileURLs")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `setJavaScriptEnabled` method for the webview
|
||||
*/
|
||||
class AllowJavaScriptMethod extends Method {
|
||||
AllowJavaScriptMethod() {
|
||||
this.getDeclaringType() instanceof TypeWebSettings and
|
||||
this.hasName("setJavaScriptEnabled")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a call to `v.setJavaScriptEnabled(true)` exists
|
||||
*/
|
||||
predicate isJSEnabled(Variable v) {
|
||||
exists(MethodAccess jsa |
|
||||
v.getAnAccess() = jsa.getQualifier() and
|
||||
jsa.getMethod() instanceof AllowJavaScriptMethod and
|
||||
jsa.getArgument(0).(BooleanLiteral).getBooleanValue() = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch URL method call on the `android.webkit.WebView` object
|
||||
*/
|
||||
class FetchResourceMethodAccess extends MethodAccess {
|
||||
FetchResourceMethodAccess() {
|
||||
this.getMethod().getDeclaringType() instanceof TypeWebView and
|
||||
this.getMethod().hasName(["loadUrl", "postUrl"])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ma` loads URL `sink`
|
||||
*/
|
||||
predicate fetchResource(FetchResourceMethodAccess ma, Expr sink) { sink = ma.getArgument(0) }
|
||||
|
||||
/**
|
||||
* A URL argument to a `loadUrl` or `postUrl` call, considered as a sink.
|
||||
*/
|
||||
class UrlResourceSink extends DataFlow::ExprNode {
|
||||
UrlResourceSink() { fetchResource(_, this.getExpr()) }
|
||||
|
||||
/** Gets the fetch method that fetches this sink URL. */
|
||||
FetchResourceMethodAccess getMethodAccess() { fetchResource(result, this.getExpr()) }
|
||||
|
||||
/**
|
||||
* Holds if cross-origin access is enabled for this resource fetch.
|
||||
*
|
||||
* Specifically this looks for code like
|
||||
* `webView.getSettings().setAllow[File|Universal]AccessFromFileURLs(true);`
|
||||
*/
|
||||
predicate crossOriginAccessEnabled() {
|
||||
exists(MethodAccess ma, MethodAccess getSettingsMa |
|
||||
ma.getMethod() instanceof CrossOriginAccessMethod and
|
||||
ma.getArgument(0).(BooleanLiteral).getBooleanValue() = true and
|
||||
ma.getQualifier().(VarAccess).getVariable().getAnAssignedValue() = getSettingsMa and
|
||||
getSettingsMa.getMethod() instanceof WebViewGetSettingsMethod and
|
||||
getSettingsMa.getQualifier().(VarAccess).getVariable().getAnAccess() =
|
||||
getMethodAccess().getQualifier()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a description of this vulnerability, assuming Javascript is enabled and
|
||||
* the fetched URL is attacker-controlled.
|
||||
*/
|
||||
string getSinkType() {
|
||||
if crossOriginAccessEnabled()
|
||||
then result = "user input vulnerable to cross-origin and sensitive resource disclosure attacks"
|
||||
else result = "user input vulnerable to XSS attacks"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint configuration tracking flow from untrusted inputs to `loadUrl` or `postUrl` calls.
|
||||
*/
|
||||
class FetchUntrustedResourceConfiguration extends TaintTracking::Configuration {
|
||||
FetchUntrustedResourceConfiguration() { this = "FetchUntrustedResourceConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink instanceof UrlResourceSink and
|
||||
exists(VarAccess webviewVa, MethodAccess getSettingsMa, Variable v |
|
||||
sink.(UrlResourceSink).getMethodAccess().getQualifier() = webviewVa and
|
||||
getSettingsMa.getMethod() instanceof WebViewGetSettingsMethod and
|
||||
webviewVa.getVariable().getAnAccess() = getSettingsMa.getQualifier() and
|
||||
v.getAnAssignedValue() = getSettingsMa and
|
||||
isJSEnabled(v)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, FetchUntrustedResourceConfiguration conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode().(UrlResourceSink).getMethodAccess(), source, sink,
|
||||
"Unsafe resource fetching in Android webview due to $@.", source.getNode(),
|
||||
sink.getNode().(UrlResourceSink).getSinkType()
|
||||
@@ -9,8 +9,8 @@ private class DefaultXssSinkModel extends SinkModelCsv {
|
||||
row =
|
||||
[
|
||||
"android.webkit;WebView;false;loadData;;;Argument[0];xss",
|
||||
"android.webkit;WebView;false;loadUrl;;;Argument[0];xss",
|
||||
"android.webkit;WebView;false;loadDataWithBaseURL;;;Argument[1];xss"
|
||||
"android.webkit;WebView;false;loadDataWithBaseURL;;;Argument[1];xss",
|
||||
"android.webkit;WebView;false;evaluateJavascript;;;Argument[0];xss"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
100
java/ql/src/semmle/code/java/security/UnsafeAndroidAccess.qll
Normal file
100
java/ql/src/semmle/code/java/security/UnsafeAndroidAccess.qll
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Provides classes to reason about Unsafe Resource Fetching vulnerabilities in Android.
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.frameworks.android.WebView
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/**
|
||||
* A sink that represents a method that fetches a web resource in Android.
|
||||
*
|
||||
* Extend this class to add your own Unsafe Resource Fetching sinks.
|
||||
*/
|
||||
abstract class UrlResourceSink extends DataFlow::Node {
|
||||
/**
|
||||
* Gets a description of this vulnerability.
|
||||
*/
|
||||
abstract string getSinkType();
|
||||
}
|
||||
|
||||
/**
|
||||
* A cross-origin access enabled resource fetch.
|
||||
*
|
||||
* Only considered a valid sink when JavaScript is also enabled.
|
||||
*/
|
||||
private class CrossOriginUrlResourceSink extends JavaScriptEnabledUrlResourceSink {
|
||||
CrossOriginUrlResourceSink() {
|
||||
exists(Variable settings, MethodAccess ma |
|
||||
webViewLoadUrl(this.asExpr(), settings) and
|
||||
ma.getMethod() instanceof CrossOriginAccessMethod and
|
||||
ma.getArgument(0).(BooleanLiteral).getBooleanValue() = true and
|
||||
ma.getQualifier() = settings.getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
override string getSinkType() {
|
||||
result = "user input vulnerable to cross-origin and sensitive resource disclosure attacks"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JavaScript enabled resource fetch.
|
||||
*/
|
||||
private class JavaScriptEnabledUrlResourceSink extends UrlResourceSink {
|
||||
JavaScriptEnabledUrlResourceSink() {
|
||||
exists(Variable settings |
|
||||
webViewLoadUrl(this.asExpr(), settings) and
|
||||
isJSEnabled(settings)
|
||||
)
|
||||
}
|
||||
|
||||
override string getSinkType() { result = "user input vulnerable to XSS attacks" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `WebViewLoadUrlMethod` method is called with the given `urlArg` on a
|
||||
* WebView with settings stored in `settings`.
|
||||
*/
|
||||
private predicate webViewLoadUrl(Expr urlArg, Variable settings) {
|
||||
exists(MethodAccess loadUrl, Variable webview, MethodAccess getSettings |
|
||||
loadUrl.getArgument(0) = urlArg and
|
||||
loadUrl.getMethod() instanceof WebViewLoadUrlMethod and
|
||||
loadUrl.getQualifier() = webview.getAnAccess() and
|
||||
getSettings.getMethod() instanceof WebViewGetSettingsMethod and
|
||||
webview.getAnAccess() = getSettings.getQualifier() and
|
||||
settings.getAnAssignedValue() = getSettings
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A method allowing any-local-file and cross-origin access in the WebSettings class.
|
||||
*/
|
||||
private class CrossOriginAccessMethod extends Method {
|
||||
CrossOriginAccessMethod() {
|
||||
this.getDeclaringType() instanceof TypeWebSettings and
|
||||
this.hasName(["setAllowUniversalAccessFromFileURLs", "setAllowFileAccessFromFileURLs"])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `setJavaScriptEnabled` method for the webview.
|
||||
*/
|
||||
private class AllowJavaScriptMethod extends Method {
|
||||
AllowJavaScriptMethod() {
|
||||
this.getDeclaringType() instanceof TypeWebSettings and
|
||||
this.hasName("setJavaScriptEnabled")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a call to `v.setJavaScriptEnabled(true)` exists.
|
||||
*/
|
||||
private predicate isJSEnabled(Variable v) {
|
||||
exists(MethodAccess jsa |
|
||||
v.getAnAccess() = jsa.getQualifier() and
|
||||
jsa.getMethod() instanceof AllowJavaScriptMethod and
|
||||
jsa.getArgument(0).(BooleanLiteral).getBooleanValue() = true
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/** Provides taint tracking configurations to be used in Unsafe Resource Fetching queries. */
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.security.RequestForgery
|
||||
import semmle.code.java.security.UnsafeAndroidAccess
|
||||
|
||||
/**
|
||||
* A taint configuration tracking flow from untrusted inputs to a resource fetching call.
|
||||
*/
|
||||
class FetchUntrustedResourceConfiguration extends TaintTracking::Configuration {
|
||||
FetchUntrustedResourceConfiguration() { this = "FetchUntrustedResourceConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof UrlResourceSink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node sanitizer) {
|
||||
sanitizer instanceof RequestForgerySanitizer
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user