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: "<")
.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
}
}
リンク
標準
- 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