HTML Injection Vulnerability
HTML Injection Vulnerability
Description
HTML injection is a security vulnerability that occurs when user-manipulated input in a mobile application can be used to insert arbitrary HTML code into a vulnerable webview. This vulnerability can be exploited to launch various attacks, such as stealing a user's session tokens or CSRF tokens, which can then be used for further malicious activities. Additionally, it allows attackers to modify the content displayed to victims, giving them the ability to insert malicious code or deface the page with their own message.
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'HTML Injection Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: WebViewScreen(),
);
}
}
class WebViewScreen extends StatefulWidget {
@override
_WebViewScreenState createState() => _WebViewScreenState();
}
class _WebViewScreenState extends State<WebViewScreen> {
late WebViewController _webViewController;
String? htmlInput;
@override
void initState() {
super.initState();
getHtmlInputFromIntent();
}
void getHtmlInputFromIntent() {
// Retrieve the intent extras
Map<String, dynamic>? extras = ModalRoute.of(context)?.settings.arguments as Map<String, dynamic>?;
// Extract the user input from the intent extras
htmlInput = extras?['htmlInput'];
}
void _injectHtml() async {
if (htmlInput != null) {
await _webViewController.loadUrl(Uri.dataFromString(htmlInput!, mimeType: 'text/html').toString());
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('HTML Injection Demo'),
),
body: Column(
children: [
ElevatedButton(
onPressed: _injectHtml,
child: Text('Inject HTML'),
),
Expanded(
child: WebView(
initialUrl: 'about:blank',
onWebViewCreated: (WebViewController controller) {
_webViewController = controller;
},
),
),
],
),
);
}
}
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate {
var webView: WKWebView!
var htmlInput: String?
override func viewDidLoad() {
super.viewDidLoad()
// Retrieve the user input from the intent extras
if let extras = self.navigationController?.navigationBar.accessibilityUserInputLabels {
htmlInput = extras["htmlInput"] as? String
}
// Create and configure the web view
webView = WKWebView(frame: view.bounds)
webView.navigationDelegate = self
view.addSubview(webView)
// Load the web page
let htmlString = "<html><body><h1>\(htmlInput ?? "")</h1></body></html>"
webView.loadHTMLString(htmlString, baseURL: nil)
}
}
import android.annotation.SuppressLint
import android.os.Bundle
import android.webkit.WebSettings
import android.webkit.WebView
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
private lateinit var webView: WebView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
webView = findViewById(R.id.webView)
val name = intent.getStringExtra("name")
val html = "<html><body><h1>Hello, $name!</h1></body></html>"
webView.loadDataWithBaseURL(null, html, "text/html", "UTF-8", null)
val webSettings: WebSettings = webView.settings
webSettings.javaScriptEnabled = false
}
}
Recommendation
To mitigate the risks associated with HTML injection vulnerabilities, consider the following recommendations:
-
Contextual Output Encoding: Encode user-generated content based on its context. Different contexts, such as HTML attributes, JavaScript code, or CSS styles, require specific encoding techniques to prevent HTML injection attacks. Use appropriate encoding functions or libraries based on the context.
-
Sanitize User Input: Perform proper input validation and sanitization on user-supplied data by stripping any special HTML characters before rendering it as part of HTML code.
-
Disable Javascript if not needed: in the context of mobile webviews, disabling Javascript can help significantly reduce the impact of any potential HTML injection.
-
Use Logic-less Templating Engines: Templating engines like
Mustache
can help prevent HTML injection by properly handling user input.
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:sanitize_html/sanitize_html.dart' show sanitizeHtml;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'HTML Injection Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: WebViewScreen(),
);
}
}
class WebViewScreen extends StatefulWidget {
@override
_WebViewScreenState createState() => _WebViewScreenState();
}
class _WebViewScreenState extends State<WebViewScreen> {
late WebViewController _webViewController;
String? htmlInput;
@override
void initState() {
super.initState();
getHtmlInputFromIntent();
}
void getHtmlInputFromIntent() {
// Retrieve the intent extras
Map<String, dynamic>? extras =
ModalRoute.of(context)?.settings.arguments as Map<String, dynamic>?;
// Extract the user input from the intent extras
htmlInput = extras?['htmlInput'];
}
void _injectHtml() async {
if (htmlInput != null) {
final sanitizedHtml = sanitizeHtml(htmlInput);
await _webViewController.loadUrl(
Uri.dataFromString(sanitizedHtml, mimeType: 'text/html', encoding: Encoding.getByName('utf-8'))!.toString(),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('HTML Injection Demo'),
),
body: Column(
children: [
ElevatedButton(
onPressed: _injectHtml,
child: Text('Inject HTML'),
),
Expanded(
child: WebView(
initialUrl: 'about:blank',
onWebViewCreated: (WebViewController controller) {
_webViewController = controller;
},
),
),
],
),
);
}
}
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate {
private var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
webView = WKWebView(frame: view.bounds)
webView.navigationDelegate = self
view.addSubview(webView)
let name = "John Doe"
let plainText = "Hello, \(name)!"
let sanitizedText = sanitizeHTML(plainText)
let html = "<html><body><h1>\(sanitizedText)</h1></body></html>"
webView.loadHTMLString(html, baseURL: nil)
}
private func sanitizeHTML(_ html: String) -> String {
// Replace any '<' and '>' characters with HTML entities
let sanitized = html.replacingOccurrences(of: "<", with: "<")
.replacingOccurrences(of: ">", with: ">")
return sanitized
}
}
import android.annotation.SuppressLint
import android.os.Bundle
import android.webkit.WebSettings
import android.webkit.WebView
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
private lateinit var webView: WebView
PolicyFactory policy = new HtmlPolicyBuilder()
.allowElements("a")
.allowUrlProtocols("https")
.allowAttributes("href").onElements("a")
.requireRelNofollowOnLinks()
.build();
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
webView = findViewById(R.id.webView)
val name = intent.getStringExtra("name")
val sanitizedName = policy.sanitize(name)
val html = "<html><body><h1>Hello, $sanitizedName!</h1></body></html>"
webView.loadDataWithBaseURL(null, html, "text/html", "UTF-8", null)
val webSettings: WebSettings = webView.settings
webSettings.javaScriptEnabled = false
}
}
Links
Standards
- OWASP_MASVS_L1:
- MSTG_PLATFORM_5
- MSTG_PLATFORM_7
- MSTG_PLATFORM_2
- OWASP_MASVS_L2:
- MSTG_PLATFORM_5
- MSTG_PLATFORM_7
- MSTG_PLATFORM_2
- PCI_STANDARDS:
- REQ_6_2
- REQ_6_3
- REQ_6_4
- REQ_11_3
- OWASP_MASVS_v2_1:
- MASVS_CODE_4
- MASVS_PLATFORM_2
- MASVS_PLATFORM_3
- SOC2_CONTROLS:
- CC_2_1
- CC_3_4
- CC_4_1
- CC_7_1
- CC_7_2
- CC_7_4
- CC_7_5