Index
Vulnerabilidad de inyección HTML
Descripción
La inyección HTML es una vulnerabilidad de seguridad que se produce cuando la entrada manipulada por el usuario en una aplicación móvil se puede utilizar para insertar código HTML arbitrario en una webview vulnerable. Esta vulnerabilidad puede ser explotada para lanzar varios ataques, como el robo de tokens de sesión de un usuario o tokens CSRF, que luego pueden ser utilizados para otras actividades maliciosas. Además, permite a los atacantes modificar el contenido que se muestra a las víctimas, dándoles la capacidad de insertar código malicioso o desfigurar la página con su propio mensaje.
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
}
}
Recomendación
Para mitigar los riesgos asociados con las vulnerabilidades de inyección HTML, considere las siguientes recomendaciones:
-
Codificación de salida contextual: Codifique el contenido generado por el usuario en función de su contexto. Diferentes contextos, como atributos HTML, código JavaScript o estilos CSS, requieren técnicas de codificación específicas para prevenir ataques de inyección HTML. Utilice funciones o bibliotecas de codificación adecuadas según el contexto.
-
Sanear la entrada del usuario: Realice una validación y un saneamiento adecuados de los datos proporcionados por el usuario eliminando cualquier carácter HTML especial antes de renderizarlo como parte del código HTML.
-
Desactivar Javascript si no es necesario: en el contexto de las webviews móviles, desactivar Javascript puede ayudar a reducir significativamente el impacto de cualquier posible inyección HTML.
-
Utilizar motores de plantillas sin lógica (Logic-less Templating Engines): Los motores de plantillas como
Mustachepueden ayudar a prevenir la inyección HTML manejando adecuadamente la entrada del usuario.
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
}
}
Enlaces
Estándares
- 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
- HIPAA_CONTROLS:
- SECURITY212
- SECURITY213
- SECURITY255