1654491660
アプリケーションのセキュリティは、すべてのWebアプリケーションにとって重要な要素です。Web開発者は、脆弱性防止技術の実装など、さまざまな戦略を使用してWebアプリケーションのセキュリティ層を改善します。
通常、生のHTMLの処理を開始し、信頼できないコンテンツでDOMを操作すると、Webアプリケーションのセキュリティリスクが高まります。サードパーティのソースから直接HTMLをレンダリングしていて、そのソースがインターネットベースの脅威の影響を受ける場合、攻撃者はユーザーの同意なしにアプリケーションユーザーのコンピューターでJavaScriptコードを実行する可能性があります。これらのセキュリティ攻撃は、XSS(クロスサイトスクリプティング)攻撃として知られています。
HTMLサニタイズは、WebアプリケーションのXSS脆弱性を防ぐためにOWASPが推奨する戦略です。HTMLサニタイズは、信頼できない生のHTML文字列から安全でない(そして潜在的に悪意のある)コンテンツをユーザーに提示する前に削除するセキュリティメカニズムを提供します。
実験的な組み込みのブラウザSanitizationAPIは、信頼できないHTML文字列を安全な方法でWebアプリケーションのDOMに挿入するのに役立ちます。
HTMLサニタイズとは、一般に、潜在的に悪意のあるJavaScriptコンテンツを生のHTML文字列から削除することを指します。2つの異なるHTMLサニタイズ実装があります。
XSSの脆弱性を防ぐために、実際には両方のサニタイズレイヤーを使用する必要があります。データベースが悪意のあるXSSペイロードの影響を受ける場合、クライアント側のサニタイズレイヤーはすべてのアプリケーションユーザーを保護しますが、攻撃者が悪意のあるHTMLをRESTful APIから直接送信する場合、サーバー側のサニタイズはシステムを保護します。
Web開発者は、クライアント側/DOMレベルのサニタイズに次のライブラリを使用する傾向があります。
htmlparser2
ベースのサニタイザーライブラリ。特にReact用のラッパーライブラリがあるため、React開発者の間で非常に人気があります。これらのライブラリは通常、ブラウザに組み込まれているDOMイテレータ、またはを使用する前に安全でないHTMLコンテンツを除外するカスタムHTMLパーサーのいずれかを使用して、安全でないHTMLを解析しますinnerHTML
。
HTMLサニタイズAPIは、安全でないHTML文字列またはドキュメントをWebページに安全に追加するのに役立つブラウザ機能です。これは、既存のDOM要素をサニタイズし、生のHTML文字列から新しいサニタイズされたDOM要素を取得するためのメソッドを提供します。
上記のソリューションは、XSS攻撃を防ぐための非常に優れたセキュリティソリューションを提供しますが、それでもいくつかの問題があります。これらのライブラリは、ブラウザの標準が変更されたときに、サニタイズ仕様を最新の状態に保つ必要があります。たとえば、標準のHTML仕様で安全でない可能性のあるHTML属性が導入された場合、これらのライブラリのサニタイズ戦略は不安定になります。
ライブラリベースのサニタイズも遅くなる可能性があります。これは、安全なHTMLをWebページに挿入するときに、解析が2回(最初はライブラリのサニゼーションプロセス中に、もう1つはブラウザのDOM解析プロセス中に)行われるためです。
HTML Sanitization APIの目標は、次の機能を介してDOMレベルのXSS攻撃を軽減することです。
ネイティブサニタイズの大きな魅力は、サニタイズルールに基づいてDOMを直接解析および操作するsetHTML
関数を提供することです。
サニタイザーAPIの背景、機能、現在の開発状況がわかったところで、JavaScript環境に公開されるAPI仕様を見てみましょう。
Sanitizer
Sanitizer APIには、クラスとElement.setHTML
メソッドの2つの主要な開発者インターフェースが付属しています。
Sanitizer
クラスと構成このクラスは、サニタイズ要件に対応Sanitizer
する新しいHTMLオブジェクトを作成するのに役立ちます。sanitizer
次の構文が付属しています。
new Sanitizer()
new Sanitizer(config)
パラメーター化されていないコンストラクターを使用して、次の構文とデフォルト構成で新しいサニタイザーオブジェクトを作成できます。デフォルトの構成では、Sanitizer
既知のXSSの脆弱性を軽減するために、セーフリストベースの手法でオブジェクトが作成されます。
const sanitizer = new Sanitizer();
ただし、Sanitizer
以下に示すように、構成オブジェクトを渡すことでオブジェクトをカスタマイズできます。
const sanitizer = new Sanitizer(config);
configuration
オブジェクトには次の定義があります。APIプロポーザルはまだWebインキュベーターにあるため、この構成定義は将来変更される可能性があることに注意してください。
{
allowElements: <string Array>,
blockElements: <string Array>,
dropElements: <string Array>,
allowAttributes: <Object>,
dropAttributes: <Object>,
allowCustomElements: <Boolean>,
allowComments: <Boolean>
}
allowElements
:消毒剤に含める必要のある要素のリストblockElements
:サニタイザーが子要素を保持することによって除外する必要がある要素のリストdropElements
:blockElements
プロパティなどの要素を除外しますが、除外されたノードに属する子要素ツリー全体も削除しますallowAttributes
:キー配列オブジェクトとして許可された属性'class': ['div']
の属性を許可します—アスタリスク文字()を使用して、任意のHTML要素の特定の属性を許可できますclassdiv*
dropAttributesallowAttributes
:プロパティの反対のバージョンallowCustomElements
:カスタム要素を許可または禁止するブール値(デフォルトはfalse
)allowComments
:コメントを許可または禁止するブール値(デフォルトはfalse
)たとえば、Sanitizer
以下に示すように、カスタムオブジェクトを開始して、基本的なHTMLタグとインラインスタイルのみを許可できます。
{
'allowElements': [
'div',
'span',
'p',
'em',
'b'
],
'allowAttributes': {
'style': ['*']
}
}
sanitize
、sanitizeFor,
およびsetHTML
このSanitizer
クラスはHTMLオブジェクトを開始するのに役立ちSanitizer
ますが、Webアプリケーションでサニタイザーインスタンスを使用するには、他のいくつかのメソッドを使用する必要があります。次のAPI仕様を学習した後、チュートリアルセクションでサニタイザーAPIの使用方法を説明します。
Sanitizer.sanitize
方法サニタイズ(入力)
このメソッドを使用して、sanitize
既存のDOMノードにサニタイザールールを適用できます。Document
この関数はorDocumentFragment
オブジェクトを受け入れ、サニタイズさDocumentFragment
れたものを出力として返します。
Sanitizer.sanitizeFor
方法sanitizeFor(element, input)
このメソッドを使用して、安全でないHTML文字列を送信することにより、サニタイズされた要素ノードを取得できます。つまり、サニタイズルールに従って文字列element
を解析した後、タイプDOMノードを返します。input
Element.setHTML
方法setHTML(input, sanitizer)
このメソッドは、Element.innerHTML
プロパティのより安全でより設定されたバージョンです。このinnerHTML
プロパティは任意のHTML文字列を許可し、XSSペイロードの傾向があります。したがって、このsetHTML
メソッドはサニタイザーインスタンスを受け入れ、新しいノードをDOMに挿入する前に、潜在的に有害なHTMLコンテンツをサニタイズします。
Sanitizer APIの初期の実装は、GoogleChrome/Chromium≥93およびFirefox≥83のWebブラウザーで使用できます。これらの初期の実装は通常、どちらのWebブラウザーでもデフォルトで有効になっていないため、最初にブラウザー構成を変更して有効にする必要があります。
Chrome / Chromiumを使用している場合は、 URL#sanitizer-api
に移動して、次のように切り替えを有効にできます。chrome://flags
Mozilla Firefoxを使用している場合はabout:config
、次のように、を介してこの機能を有効にできます。
このチュートリアルでは、Mozilla Firefox 96を使用して、今後のSanitizerAPIの例を試してみます。
実用的な例でサニタイザーAPIを試してみましょう。これらの例を示すためにJsFiddleオンラインエディターを使用しますが、HTMLファイルを作成することで、ローカルの開発環境でテストすることもできます。
基本から始めましょう。Sanitizer APIを使用して、安全でないHTML文字列からより安全なDOMノードをレンダリングするにはどうすればよいですか?次のサンプルコードを見てください。
<div id="container"></div>
<script>
// unsafe HTML string
const unsafeHTML = `<p onclick="alert('Hello')">Hello</p>`;
// Find the container node
const container = document.getElementById('container');
// Create a sanitizer object with the default config
const sanitizer = new Sanitizer();
// Inject new DOM nodes in a safer way
container.setHTML(unsafeHTML, sanitizer);
</script>
ここでは、プロパティsetHTML
の代わりにセッターを使用しました。innerHTML
上記のコードを実行した後にDOMを調べると、子要素をノードにレンダリングする前に、setHTML
メソッドが自動的に除外されていることがわかります。onclickcontainer
innerHTML
次のコードを使用して、プロパティの不安定さを確認できます。
<div id="container"></div>
<script>
// unsafe HTML string
const unsafeHTML = `<p onclick="alert('Hello')">Hello</p>`;
// Find the container node
const container = document.getElementById('container');
// Inject new DOM nodes
container.innerHTML = unsafeHTML;
</script>
上記のコードは、以下に示すように、安全でないイベントハンドラーを使用して新しいDOMノードを挿入します。
プロパティのセキュリティ問題のデモンストレーションinnerHTML
innerHTML
サニタイズされたDOM要素のプロパティを読み取ることでサニタイズされた生のHTML文字列を取得できますが、サニタイザーAPIを安全に挿入するというサニタイザーAPIの背後にある主な目標をやや破ります。つまり、サニタイザーAPIを別のサニタイズライブラリとして使用しないでください。
sanitizeFor
以前は、このsetHTML
メソッドを使用して、サニタイズプロセスで安全でないHTML文字列をすぐにレンダリングしましたが、シナリオによっては、サニタイズプロセスの後で新しい要素をレンダリングする必要があります。
たとえば、Web開発者は、レンダリングプロセスの後に、インターネットからWYSIWYGエディタに安全でないHTML文字列をレンダリングする必要があることがよくあります。最適化されたエラーのないソリューションとして、最初にコンテンツをフェッチし、サニタイズを適用してから、エディターコンポーネントが完全にレンダリングされたときにサニタイズされたノードをレンダリングできます。
このメソッドを使用して、結果をサニタイズして特定のDOMノードとして一時的に保存できますsanitizeFor
。次の例を見てください。
<div id="container">Loading...</div>
<script>
// unsafe HTML string
const unsafeHTML = `<p onclick="alert('Hello')">Hello</p>`;
// Create a sanitizer object with the default config
const sanitizer = new Sanitizer();
// Hold sanitized node
const sanitizedDiv = sanitizer.sanitizeFor('div', unsafeHTML);
// Inject nodes after sometime
setTimeout(() => {
// Find the container node
const container = document.getElementById('container');
// Inject the sanitized DOM node
container.replaceChildren(sanitizedDiv);
}, 1000);
</script>
上記のコードは、安全でないHTML文字列をサニタイズし、サニタイズされたDOMノードを定数に保存します。後で、メソッドを使用して、サニタイズされたDOMノードを関連するコンテナノードに挿入しますreplaceChildren
。ネットワークとレンダリングの遅延をシミュレートするために、意図的に1秒の遅延を使用したことに注意してください。
関数の使用方法のデモンストレーションsanitizeFor
iframeは、ウィジェットやサードパーティのWebページをWebアプリケーションに追加するのに役立ちますが、他のソース(多くの場合サードパーティのソース)からWebコンテンツをロードするため、通常はセキュリティ上の問題があります。したがって、iframeを介して読み込まれるWebコンテンツをサニタイズするのが間違いなく最も安全です。
以前は、サニタイズAPIメソッドの入力として文字列を使用していましたが、今度は、既存のDOMノードをサニタイズする必要があります。これを行うには、HTMLドキュメントフラグメントまたはドキュメントを受け入れる関数が必要です。
方法を覚えていsanitize
ますか?次の例を見てください。
<iframe id="webpage"></iframe> <!-- Use a URL with cross-origin policy -->
<br/>
<button onclick="sanitize()">Sanitize</button>
<script>
function sanitize() {
// Create a sanitizer object with the default config
const sanitizer = new Sanitizer();
// Find the iframe node
const iframe = document.getElementById('webpage');
// Sanitize the iframe's document node
const sanitizedFrameNodes = sanitizer.sanitize(iframe.contentWindow.document);
iframe.replaceChildren(sanitizeFrameNodes);
}
</script>
構成オブジェクトを送信せずに新しいSanitizer
クラスインスタンスを作成すると、APIはデフォルトの構成を使用して既知のXSSの脆弱性を軽減します。ただし、構成オブジェクトを送信することにより、サニタイズロジックをカスタマイズできます。
div
動的要素に対して基本的なHTMLタグとインラインスタイルを許可する必要があると想定します。以下に示すように、カスタム構成を使用して、この要件に対応するサニタイザーを実装できます。
<div id="container"></div>
<script>
// unsafe HTML string
const unsafeHTML = `<div onclick="alert('Hello')">
<p><b>Hello Sanitizer API</b></p>
<p><em onmovemove="window.location.reload()">Test</em></p>
<img src="image.png" alt="Test"/>
</div>`;
// Find the container node
const container = document.getElementById('container');
// Create a sanitizer object with a custom config
const sanitizer = new Sanitizer(
{
'allowElements': [
'div',
'span',
'p',
'em',
'b'
],
'allowAttributes': {
'style': ['*']
}
});
// Inject new DOM nodes in a safer way
const sanitizedDiv = sanitizer.sanitizeFor('div', unsafeHTML);
container.replaceChildren(sanitizedDiv);
</script>
関数を使用しても同じ出力を達成できることに注意してください。ただし、Firefoxの実験関数にはサニタイズ後もタグが含まれているため、代わりsetHTML
に使用しました。replaceChildrensetHTMLimg
カスタムサニタイザー構成を使用する場合は注意してください。onclick
構成をカスタマイズするときに、任意の要素と属性を許可するように完全に制御できます。たとえば、次のサニタイザー構成では、イベントハンドラーが許可されるため、WebアプリケーションがXSSになりやすくなります。
{
'allowElements': ['div', 'p', 'em'],
'allowAttributes': {
'onclick': ['*']
}
}
サニタイザーAPIの設定ミスに注意してください!
ブラウザ開発者とセキュリティエンジニアは通常、一般的な承認を得るために新しいブラウザAPI提案をW3C組織に提出します。インキュベーション期間と承認の後、W3Cは特定の仕様を公式のWeb標準に追加します。
何人かの寄稿者が2016年にGitHubリポジトリでSanitizationAPIプロポーザルの作成を開始しました。2021年後半、API提案は公式のWebインキュベーターでドラフト段階に達しました。現在、Web開発者コミュニティは、さまざまなアイデアを提案することで仕様を改善し、公式のWeb標準にするよう努めています。
さらに、Google Chrome/Chromium≥93およびFirefox≥83は、それらを今すぐテストすることに関心のあるWeb開発者向けにSanitizerAPIの初期の実装を提供します。これらの初期の実装は安定しておらず、将来的に変更される可能性があります。完全なブラウザサポートの詳細は、CanIUseで確認できます。
ただし、このブラウザ機能は安全なコンテキストで機能します。つまり、このブラウザ機能はHTTPS接続でのみ使用できます。127.0.0.1
ただし、標準のセキュアコンテキストポリシーはローカルホスト(または)をセキュアコンテキストとして識別するため、ローカル開発環境でSanitizerAPIを使用することもできます。
このチュートリアルでは、いくつかの例を使用して実験的なSanitizer APIの使用方法を学び、ブラウザーの実験的な機能リストからそれを有効にすることから始めました。Google Chrome /ChromiumとMozillaFirefoxは、このAPI仕様の初期の実装を提供していますが、それでもW3Cインキュベータープログラムに含まれています。つまり、プロポーザルの編集者は、コミュニティの提案と既知のセキュリティの脆弱性に基づいてAPI仕様を変更する可能性があります。Sanitizer APIの構造を改善する提案がある場合は、GitHubのSanitizerAPIインキュベーターリポジトリに問題を送信できます。
Sanitizer APIは、フロントエンド開発者とフレームワーク開発者の両方を支援することを約束します。たとえば、React開発者は、安全でないHTML文字列をDOMにレンダリングするために、 sanitize-htmlライブラリとReactの小道具を使用する傾向があります。dangerouslySetInnerHTML
ただし、実験的なSanitizer APIがブラウザーの標準になると、Reactは、setHTML
バンドルサイズに影響を与えることなく、任意のHTML文字列をサニタイズおよび挿入するための開発者向けのメソッド(など)を提供できるようになります。
AngularなどのカスタムHTMLサニタイザー実装を使用するフレームワークは、ネイティブのSanitizationAPIを使用してフレームワークバンドルサイズを削減できます。ただし、前述のように、Sanitizer APIはまだ実験段階であるため、安定してW3Cで承認されるまで、本番システムで使用しないでください。
オンラインのHTMLSanitizerAPIプレイグラウンドを使用して、SanitizerAPIをさらに試すことができます。
このストーリーは、もともとhttps://blog.logrocket.com/what-you-need-know-inbuilt-browser-html-sanitization/で公開されました。
1654491660
アプリケーションのセキュリティは、すべてのWebアプリケーションにとって重要な要素です。Web開発者は、脆弱性防止技術の実装など、さまざまな戦略を使用してWebアプリケーションのセキュリティ層を改善します。
通常、生のHTMLの処理を開始し、信頼できないコンテンツでDOMを操作すると、Webアプリケーションのセキュリティリスクが高まります。サードパーティのソースから直接HTMLをレンダリングしていて、そのソースがインターネットベースの脅威の影響を受ける場合、攻撃者はユーザーの同意なしにアプリケーションユーザーのコンピューターでJavaScriptコードを実行する可能性があります。これらのセキュリティ攻撃は、XSS(クロスサイトスクリプティング)攻撃として知られています。
HTMLサニタイズは、WebアプリケーションのXSS脆弱性を防ぐためにOWASPが推奨する戦略です。HTMLサニタイズは、信頼できない生のHTML文字列から安全でない(そして潜在的に悪意のある)コンテンツをユーザーに提示する前に削除するセキュリティメカニズムを提供します。
実験的な組み込みのブラウザSanitizationAPIは、信頼できないHTML文字列を安全な方法でWebアプリケーションのDOMに挿入するのに役立ちます。
HTMLサニタイズとは、一般に、潜在的に悪意のあるJavaScriptコンテンツを生のHTML文字列から削除することを指します。2つの異なるHTMLサニタイズ実装があります。
XSSの脆弱性を防ぐために、実際には両方のサニタイズレイヤーを使用する必要があります。データベースが悪意のあるXSSペイロードの影響を受ける場合、クライアント側のサニタイズレイヤーはすべてのアプリケーションユーザーを保護しますが、攻撃者が悪意のあるHTMLをRESTful APIから直接送信する場合、サーバー側のサニタイズはシステムを保護します。
Web開発者は、クライアント側/DOMレベルのサニタイズに次のライブラリを使用する傾向があります。
htmlparser2
ベースのサニタイザーライブラリ。特にReact用のラッパーライブラリがあるため、React開発者の間で非常に人気があります。これらのライブラリは通常、ブラウザに組み込まれているDOMイテレータ、またはを使用する前に安全でないHTMLコンテンツを除外するカスタムHTMLパーサーのいずれかを使用して、安全でないHTMLを解析しますinnerHTML
。
HTMLサニタイズAPIは、安全でないHTML文字列またはドキュメントをWebページに安全に追加するのに役立つブラウザ機能です。これは、既存のDOM要素をサニタイズし、生のHTML文字列から新しいサニタイズされたDOM要素を取得するためのメソッドを提供します。
上記のソリューションは、XSS攻撃を防ぐための非常に優れたセキュリティソリューションを提供しますが、それでもいくつかの問題があります。これらのライブラリは、ブラウザの標準が変更されたときに、サニタイズ仕様を最新の状態に保つ必要があります。たとえば、標準のHTML仕様で安全でない可能性のあるHTML属性が導入された場合、これらのライブラリのサニタイズ戦略は不安定になります。
ライブラリベースのサニタイズも遅くなる可能性があります。これは、安全なHTMLをWebページに挿入するときに、解析が2回(最初はライブラリのサニゼーションプロセス中に、もう1つはブラウザのDOM解析プロセス中に)行われるためです。
HTML Sanitization APIの目標は、次の機能を介してDOMレベルのXSS攻撃を軽減することです。
ネイティブサニタイズの大きな魅力は、サニタイズルールに基づいてDOMを直接解析および操作するsetHTML
関数を提供することです。
サニタイザーAPIの背景、機能、現在の開発状況がわかったところで、JavaScript環境に公開されるAPI仕様を見てみましょう。
Sanitizer
Sanitizer APIには、クラスとElement.setHTML
メソッドの2つの主要な開発者インターフェースが付属しています。
Sanitizer
クラスと構成このクラスは、サニタイズ要件に対応Sanitizer
する新しいHTMLオブジェクトを作成するのに役立ちます。sanitizer
次の構文が付属しています。
new Sanitizer()
new Sanitizer(config)
パラメーター化されていないコンストラクターを使用して、次の構文とデフォルト構成で新しいサニタイザーオブジェクトを作成できます。デフォルトの構成では、Sanitizer
既知のXSSの脆弱性を軽減するために、セーフリストベースの手法でオブジェクトが作成されます。
const sanitizer = new Sanitizer();
ただし、Sanitizer
以下に示すように、構成オブジェクトを渡すことでオブジェクトをカスタマイズできます。
const sanitizer = new Sanitizer(config);
configuration
オブジェクトには次の定義があります。APIプロポーザルはまだWebインキュベーターにあるため、この構成定義は将来変更される可能性があることに注意してください。
{
allowElements: <string Array>,
blockElements: <string Array>,
dropElements: <string Array>,
allowAttributes: <Object>,
dropAttributes: <Object>,
allowCustomElements: <Boolean>,
allowComments: <Boolean>
}
allowElements
:消毒剤に含める必要のある要素のリストblockElements
:サニタイザーが子要素を保持することによって除外する必要がある要素のリストdropElements
:blockElements
プロパティなどの要素を除外しますが、除外されたノードに属する子要素ツリー全体も削除しますallowAttributes
:キー配列オブジェクトとして許可された属性'class': ['div']
の属性を許可します—アスタリスク文字()を使用して、任意のHTML要素の特定の属性を許可できますclassdiv*
dropAttributesallowAttributes
:プロパティの反対のバージョンallowCustomElements
:カスタム要素を許可または禁止するブール値(デフォルトはfalse
)allowComments
:コメントを許可または禁止するブール値(デフォルトはfalse
)たとえば、Sanitizer
以下に示すように、カスタムオブジェクトを開始して、基本的なHTMLタグとインラインスタイルのみを許可できます。
{
'allowElements': [
'div',
'span',
'p',
'em',
'b'
],
'allowAttributes': {
'style': ['*']
}
}
sanitize
、sanitizeFor,
およびsetHTML
このSanitizer
クラスはHTMLオブジェクトを開始するのに役立ちSanitizer
ますが、Webアプリケーションでサニタイザーインスタンスを使用するには、他のいくつかのメソッドを使用する必要があります。次のAPI仕様を学習した後、チュートリアルセクションでサニタイザーAPIの使用方法を説明します。
Sanitizer.sanitize
方法サニタイズ(入力)
このメソッドを使用して、sanitize
既存のDOMノードにサニタイザールールを適用できます。Document
この関数はorDocumentFragment
オブジェクトを受け入れ、サニタイズさDocumentFragment
れたものを出力として返します。
Sanitizer.sanitizeFor
方法sanitizeFor(element, input)
このメソッドを使用して、安全でないHTML文字列を送信することにより、サニタイズされた要素ノードを取得できます。つまり、サニタイズルールに従って文字列element
を解析した後、タイプDOMノードを返します。input
Element.setHTML
方法setHTML(input, sanitizer)
このメソッドは、Element.innerHTML
プロパティのより安全でより設定されたバージョンです。このinnerHTML
プロパティは任意のHTML文字列を許可し、XSSペイロードの傾向があります。したがって、このsetHTML
メソッドはサニタイザーインスタンスを受け入れ、新しいノードをDOMに挿入する前に、潜在的に有害なHTMLコンテンツをサニタイズします。
Sanitizer APIの初期の実装は、GoogleChrome/Chromium≥93およびFirefox≥83のWebブラウザーで使用できます。これらの初期の実装は通常、どちらのWebブラウザーでもデフォルトで有効になっていないため、最初にブラウザー構成を変更して有効にする必要があります。
Chrome / Chromiumを使用している場合は、 URL#sanitizer-api
に移動して、次のように切り替えを有効にできます。chrome://flags
Mozilla Firefoxを使用している場合はabout:config
、次のように、を介してこの機能を有効にできます。
このチュートリアルでは、Mozilla Firefox 96を使用して、今後のSanitizerAPIの例を試してみます。
実用的な例でサニタイザーAPIを試してみましょう。これらの例を示すためにJsFiddleオンラインエディターを使用しますが、HTMLファイルを作成することで、ローカルの開発環境でテストすることもできます。
基本から始めましょう。Sanitizer APIを使用して、安全でないHTML文字列からより安全なDOMノードをレンダリングするにはどうすればよいですか?次のサンプルコードを見てください。
<div id="container"></div>
<script>
// unsafe HTML string
const unsafeHTML = `<p onclick="alert('Hello')">Hello</p>`;
// Find the container node
const container = document.getElementById('container');
// Create a sanitizer object with the default config
const sanitizer = new Sanitizer();
// Inject new DOM nodes in a safer way
container.setHTML(unsafeHTML, sanitizer);
</script>
ここでは、プロパティsetHTML
の代わりにセッターを使用しました。innerHTML
上記のコードを実行した後にDOMを調べると、子要素をノードにレンダリングする前に、setHTML
メソッドが自動的に除外されていることがわかります。onclickcontainer
innerHTML
次のコードを使用して、プロパティの不安定さを確認できます。
<div id="container"></div>
<script>
// unsafe HTML string
const unsafeHTML = `<p onclick="alert('Hello')">Hello</p>`;
// Find the container node
const container = document.getElementById('container');
// Inject new DOM nodes
container.innerHTML = unsafeHTML;
</script>
上記のコードは、以下に示すように、安全でないイベントハンドラーを使用して新しいDOMノードを挿入します。
プロパティのセキュリティ問題のデモンストレーションinnerHTML
innerHTML
サニタイズされたDOM要素のプロパティを読み取ることでサニタイズされた生のHTML文字列を取得できますが、サニタイザーAPIを安全に挿入するというサニタイザーAPIの背後にある主な目標をやや破ります。つまり、サニタイザーAPIを別のサニタイズライブラリとして使用しないでください。
sanitizeFor
以前は、このsetHTML
メソッドを使用して、サニタイズプロセスで安全でないHTML文字列をすぐにレンダリングしましたが、シナリオによっては、サニタイズプロセスの後で新しい要素をレンダリングする必要があります。
たとえば、Web開発者は、レンダリングプロセスの後に、インターネットからWYSIWYGエディタに安全でないHTML文字列をレンダリングする必要があることがよくあります。最適化されたエラーのないソリューションとして、最初にコンテンツをフェッチし、サニタイズを適用してから、エディターコンポーネントが完全にレンダリングされたときにサニタイズされたノードをレンダリングできます。
このメソッドを使用して、結果をサニタイズして特定のDOMノードとして一時的に保存できますsanitizeFor
。次の例を見てください。
<div id="container">Loading...</div>
<script>
// unsafe HTML string
const unsafeHTML = `<p onclick="alert('Hello')">Hello</p>`;
// Create a sanitizer object with the default config
const sanitizer = new Sanitizer();
// Hold sanitized node
const sanitizedDiv = sanitizer.sanitizeFor('div', unsafeHTML);
// Inject nodes after sometime
setTimeout(() => {
// Find the container node
const container = document.getElementById('container');
// Inject the sanitized DOM node
container.replaceChildren(sanitizedDiv);
}, 1000);
</script>
上記のコードは、安全でないHTML文字列をサニタイズし、サニタイズされたDOMノードを定数に保存します。後で、メソッドを使用して、サニタイズされたDOMノードを関連するコンテナノードに挿入しますreplaceChildren
。ネットワークとレンダリングの遅延をシミュレートするために、意図的に1秒の遅延を使用したことに注意してください。
関数の使用方法のデモンストレーションsanitizeFor
iframeは、ウィジェットやサードパーティのWebページをWebアプリケーションに追加するのに役立ちますが、他のソース(多くの場合サードパーティのソース)からWebコンテンツをロードするため、通常はセキュリティ上の問題があります。したがって、iframeを介して読み込まれるWebコンテンツをサニタイズするのが間違いなく最も安全です。
以前は、サニタイズAPIメソッドの入力として文字列を使用していましたが、今度は、既存のDOMノードをサニタイズする必要があります。これを行うには、HTMLドキュメントフラグメントまたはドキュメントを受け入れる関数が必要です。
方法を覚えていsanitize
ますか?次の例を見てください。
<iframe id="webpage"></iframe> <!-- Use a URL with cross-origin policy -->
<br/>
<button onclick="sanitize()">Sanitize</button>
<script>
function sanitize() {
// Create a sanitizer object with the default config
const sanitizer = new Sanitizer();
// Find the iframe node
const iframe = document.getElementById('webpage');
// Sanitize the iframe's document node
const sanitizedFrameNodes = sanitizer.sanitize(iframe.contentWindow.document);
iframe.replaceChildren(sanitizeFrameNodes);
}
</script>
構成オブジェクトを送信せずに新しいSanitizer
クラスインスタンスを作成すると、APIはデフォルトの構成を使用して既知のXSSの脆弱性を軽減します。ただし、構成オブジェクトを送信することにより、サニタイズロジックをカスタマイズできます。
div
動的要素に対して基本的なHTMLタグとインラインスタイルを許可する必要があると想定します。以下に示すように、カスタム構成を使用して、この要件に対応するサニタイザーを実装できます。
<div id="container"></div>
<script>
// unsafe HTML string
const unsafeHTML = `<div onclick="alert('Hello')">
<p><b>Hello Sanitizer API</b></p>
<p><em onmovemove="window.location.reload()">Test</em></p>
<img src="image.png" alt="Test"/>
</div>`;
// Find the container node
const container = document.getElementById('container');
// Create a sanitizer object with a custom config
const sanitizer = new Sanitizer(
{
'allowElements': [
'div',
'span',
'p',
'em',
'b'
],
'allowAttributes': {
'style': ['*']
}
});
// Inject new DOM nodes in a safer way
const sanitizedDiv = sanitizer.sanitizeFor('div', unsafeHTML);
container.replaceChildren(sanitizedDiv);
</script>
関数を使用しても同じ出力を達成できることに注意してください。ただし、Firefoxの実験関数にはサニタイズ後もタグが含まれているため、代わりsetHTML
に使用しました。replaceChildrensetHTMLimg
カスタムサニタイザー構成を使用する場合は注意してください。onclick
構成をカスタマイズするときに、任意の要素と属性を許可するように完全に制御できます。たとえば、次のサニタイザー構成では、イベントハンドラーが許可されるため、WebアプリケーションがXSSになりやすくなります。
{
'allowElements': ['div', 'p', 'em'],
'allowAttributes': {
'onclick': ['*']
}
}
サニタイザーAPIの設定ミスに注意してください!
ブラウザ開発者とセキュリティエンジニアは通常、一般的な承認を得るために新しいブラウザAPI提案をW3C組織に提出します。インキュベーション期間と承認の後、W3Cは特定の仕様を公式のWeb標準に追加します。
何人かの寄稿者が2016年にGitHubリポジトリでSanitizationAPIプロポーザルの作成を開始しました。2021年後半、API提案は公式のWebインキュベーターでドラフト段階に達しました。現在、Web開発者コミュニティは、さまざまなアイデアを提案することで仕様を改善し、公式のWeb標準にするよう努めています。
さらに、Google Chrome/Chromium≥93およびFirefox≥83は、それらを今すぐテストすることに関心のあるWeb開発者向けにSanitizerAPIの初期の実装を提供します。これらの初期の実装は安定しておらず、将来的に変更される可能性があります。完全なブラウザサポートの詳細は、CanIUseで確認できます。
ただし、このブラウザ機能は安全なコンテキストで機能します。つまり、このブラウザ機能はHTTPS接続でのみ使用できます。127.0.0.1
ただし、標準のセキュアコンテキストポリシーはローカルホスト(または)をセキュアコンテキストとして識別するため、ローカル開発環境でSanitizerAPIを使用することもできます。
このチュートリアルでは、いくつかの例を使用して実験的なSanitizer APIの使用方法を学び、ブラウザーの実験的な機能リストからそれを有効にすることから始めました。Google Chrome /ChromiumとMozillaFirefoxは、このAPI仕様の初期の実装を提供していますが、それでもW3Cインキュベータープログラムに含まれています。つまり、プロポーザルの編集者は、コミュニティの提案と既知のセキュリティの脆弱性に基づいてAPI仕様を変更する可能性があります。Sanitizer APIの構造を改善する提案がある場合は、GitHubのSanitizerAPIインキュベーターリポジトリに問題を送信できます。
Sanitizer APIは、フロントエンド開発者とフレームワーク開発者の両方を支援することを約束します。たとえば、React開発者は、安全でないHTML文字列をDOMにレンダリングするために、 sanitize-htmlライブラリとReactの小道具を使用する傾向があります。dangerouslySetInnerHTML
ただし、実験的なSanitizer APIがブラウザーの標準になると、Reactは、setHTML
バンドルサイズに影響を与えることなく、任意のHTML文字列をサニタイズおよび挿入するための開発者向けのメソッド(など)を提供できるようになります。
AngularなどのカスタムHTMLサニタイザー実装を使用するフレームワークは、ネイティブのSanitizationAPIを使用してフレームワークバンドルサイズを削減できます。ただし、前述のように、Sanitizer APIはまだ実験段階であるため、安定してW3Cで承認されるまで、本番システムで使用しないでください。
オンラインのHTMLSanitizerAPIプレイグラウンドを使用して、SanitizerAPIをさらに試すことができます。
このストーリーは、もともとhttps://blog.logrocket.com/what-you-need-know-inbuilt-browser-html-sanitization/で公開されました。