跳转至

Index

HTML 注入漏洞

描述

HTML 注入是一种安全漏洞,当移动应用程序中用户操纵的输入可用于将任意 HTML 代码插入易受攻击的 webview 时,就会发生这种漏洞。此漏洞可被用来发起各种攻击,例如窃取用户的会话令牌或 CSRF 令牌,然后将其用于进一步的恶意活动。此外,它允许攻击者修改显示给受害者的内容,使他们能够插入恶意代码或用自己的消息破坏页面。

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
    }
}

建议

为了降低与 HTML 注入漏洞相关的风险,请考虑以下建议:

  • 上下文输出编码: 根据其上下文对用户生成的内容进行编码。不同的上下文(如 HTML 属性、JavaScript 代码或 CSS 样式)需要特定的编码技术来防止 HTML 注入攻击。根据上下文使用适当的编码函数或库。

  • 清理用户输入: 通过在将其作为 HTML 代码的一部分呈现之前剥离任何特殊 HTML 字符,对用户提供的数据执行适当的输入验证和清理。

  • 如果不需要,请禁用 Javascript: 在移动 webview 的上下文中,禁用 Javascript 可以帮助显着降低任何潜在的 HTML 注入的影响。

  • 使用无逻辑模板引擎:Mustache 这样的模板引擎可以通过正确处理用户输入来帮助防止 HTML 注入。

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: "&lt;")
                                                        .replacingOccurrences(of: ">", with: "&gt;")
                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
    }

}

链接

标准

  • 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