コンテンツにスキップ

Index

HTMLインジェクションの脆弱性

説明

HTMLインジェクションは、モバイルアプリケーション内でユーザーが操作した入力を使用して、脆弱なwebviewに任意のHTMLコードを挿入できる場合に発生するセキュリティ上の脆弱性です。この脆弱性を悪用して、ユーザーのセッショントークンや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