WebView loads HTML or URL from a non-literal (potentially attacker-controlled) source
ID |
dart.platform.unrestricted_webview_load |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Platform |
Language |
Dart |
Description
A WebView loads HTML or a URL from a non-literal value (loadHtmlString($X), loadRequest(Uri.parse($X)), loadFile($P), loadData(data: $H), loadUrl(urlRequest: URLRequest(url: WebUri($U))), InAppWebViewInitialData(data: $H)). If the value flows from the network, deeplink, or user input, the WebView renders attacker- controlled content.
Rationale
WebView load APIs are HTML / URL sinks — anything reaching their first argument is treated as a document to render. When that argument is a variable (rather than a string literal), it is trivially reachable from:
-
A deeplink query parameter that the app passes through to
loadHtmlStringwithout sanitization. -
An HTTP response body interpolated into a template.
-
A push-notification payload.
-
File uploads from another app via share-intent.
Once attacker HTML renders inside the WebView, the impact stacks
with webview_javascript_enabled (XSS in WebView origin) and
webview_file_access_enabled (local file exfiltration). Even with
JS disabled, attacker HTML can stage credential phishing UIs that
look identical to the app’s own screens.
The following code illustrates a vulnerable pattern detected by this rule:
class LoadUntrusted {
void wvf(WebViewController controller, String html, String url, String path) {
// VULNERABLE: WebView loads HTML or URL from a non-literal (potentially attacker-controlled) source
controller.loadHtmlString(html);
// VULNERABLE: WebView loads HTML or URL from a non-literal (potentially attacker-controlled) source
controller.loadHtmlString(html, baseUrl: 'https://api.example.com');
// VULNERABLE: WebView loads HTML or URL from a non-literal (potentially attacker-controlled) source
controller.loadRequest(Uri.parse(url));
// VULNERABLE: WebView loads HTML or URL from a non-literal (potentially attacker-controlled) source
controller.loadFile(path);
}
}
Remediation
Prefer loadFlutterAsset(…) for bundled HTML, fully-qualified
HTTPS URLs hardcoded in source for known-trusted endpoints, and
strict sanitization (allow-listed tags / attributes) at the boundary
if user content must reach a WebView.
// Vulnerable: untrusted value reaches the WebView
controller.loadHtmlString(html); // html from network
controller.loadRequest(Uri.parse(url)); // url from deeplink
controller.loadData(data: htmlFromUser);
// Fixed: bundled asset (no untrusted input reaches the document)
await controller.loadFlutterAsset('assets/help.html');
// Fixed: hardcoded known-trusted URL
controller.loadRequest(Uri.parse('https://docs.example.com/help'));
References
-
OWASP Top 10 2025 - A05 : Injection.
-
package:webview_flutter (pub.dev) —
loadFlutterAsset,loadHtmlString,loadRequestreference -
package:flutter_inappwebview (pub.dev) —
loadData,loadUrl,loadFile,InAppWebViewInitialDatareference