信頼できるタイプを使用して、DOMベースのクロスサイトスクリプティングの脆弱性を防止します
アプリケーションのDOMXSS攻撃対象領域を減らします。
なぜあなたは気にする必要がありますか? #
DOMベースのクロスサイトスクリプティング(DOM XSS)は、最も一般的なWebセキュリティの脆弱性の1つであり、アプリケーションに導入するのは非常に簡単です。信頼できるタイプは、危険なWeb API関数をデフォルトで安全にすることにより、DOM XSSの脆弱性のないアプリケーションを作成、セキュリティレビュー、および維持するためのツールを提供します。信頼できるタイプはChrome83でサポートされており、他のブラウザではポリフィルを利用できます。最新のクロスブラウザサポート情報については、ブラウザの互換性を参照してください。
バックグラウンド #
何年もの間、 DOM XSSは、最も一般的で危険なWebセキュリティの脆弱性の1つでした。
クロスサイトスクリプティングには2つの異なるグループがあります。一部のXSSの脆弱性は、Webサイトを形成するHTMLコードを安全に作成しないサーバー側のコードが原因で発生します。他の人はクライアントに根本的な原因があり、JavaScriptコードがユーザー制御のコンテンツで危険な関数を呼び出します。
サーバー側のXSSを防ぐために、文字列を連結してHTMLを生成せず、代わりに安全なコンテキスト自動エスケープテンプレートライブラリを使用してください。バグが必然的に発生するため、バグをさらに軽減するために、ノンスベースのコンテンツセキュリティポリシーを使用してください。
これで、ブラウザーは、信頼できるタイプのクライアント側(DOMベースとも呼ばれる)XSSを防ぐのにも役立ちます。
APIの紹介 #
トラステッドタイプは、次の危険なシンク機能をロックダウンすることで機能します。ブラウザベンダーとWebフレームワークは、セキュリティ上の理由からこれらの機能の使用をすでに避けているため、すでにそれらのいくつかを認識しているかもしれません。
スクリプト操作:
<script src>
との設定のテキストコンテンツ<script>
要素。文字列からのHTMLの生成:
innerHTML
、outerHTML
、insertAdjacentHTML
、<iframe> srcdoc
、document.write
、document.writeln
、およびDOMParser.parseFromString
プラグインコンテンツの実行:
<embed src>
、<object data>
、および<object codebase>
ランタイムJavaScriptコードのコンパイル:
eval
、setTimeout
、setInterval
、new Function()
トラステッドタイプでは、データを上記のシンク関数に渡す前にデータを処理する必要があります。ブラウザはデータが信頼できるかどうかわからないため、文字列を使用するだけでは失敗します。 してはいけないことanElement.innerHTML = location.href;
データが安全に処理されたことを示すために、特別なオブジェクト(信頼できるタイプ)を作成します。 すべきことanElement.innerHTML = aTrustedHTML;
信頼できるタイプは、アプリケーションのDOMXSS攻撃対象領域を大幅に削減します。これにより、セキュリティレビューが簡素化され、ブラウザで実行時にコードをコンパイル、リント、またはバンドルするときに実行されるタイプベースのセキュリティチェックを実施できます。
信頼できるタイプの使用方法 #
コンテンツセキュリティポリシー違反レポートの準備 #
レポートコレクター(オープンソースのgo-csp-collectorなど)をデプロイするか、同等の商用製品の1つを使用できます。ブラウザで違反をデバッグすることもできます。
window.addEventListener('securitypolicyviolation',
console.error.bind(console));
レポートのみのCSPヘッダーを追加する #
信頼できるタイプに移行するドキュメントに、次のHTTP応答ヘッダーを追加します。
Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
これで、すべての違反が//my-csp-endpoint.example
に報告されますが、Webサイトは引き続き機能します。次のセクションでは、 //my-csp-endpoint.example
どのように機能するかを説明します。
信頼できるタイプの違反を特定する #
これ以降、信頼できるタイプが違反を検出するたびに、構成されたreport-uri
が送信されます。たとえば、アプリケーションが文字列をinnerHTML
渡すと、ブラウザは次のレポートを送信します。
{
"csp-report": {
"document-uri": "https://my.url.example",
"violated-directive": "require-trusted-types-for",
"disposition": "report",
"blocked-uri": "trusted-types-sink",
"line-number": 39,
"column-number": 12,
"source-file": "https://my.url.example/script.js",
"status-code": 0,
"script-sample": "Element innerHTML <img src=x"
}
}
これは、39行目のhttps://my.url.example/script.js
<img src=x
innerHTML
が呼び出されたことを示しています。この情報は、コードのどの部分がDOM XSSを導入していて、変更する必要があるかを絞り込むのに役立ちます。
違反を修正する #
信頼できるタイプの違反を修正するには、いくつかのオプションがあります。問題のあるコードを削除したり、ライブラリを使用したり、信頼できるタイプのポリシーを作成したり、最後の手段としてデフォルトのポリシーを作成したりできます。
問題のあるコードを書き直してください #
おそらく、不適合な機能はもう必要ないのでしょうか、それともエラーが発生しやすい関数を使用せずに最新の方法で書き直すことができるのでしょうか。 してはいけないこと すべきことel.innerHTML = '<img src=xyz.jpg>';
el.textContent = '';
const img = document.createElement('img');
img.src = 'xyz.jpg';
el.appendChild(img);
ライブラリを使用する #
一部のライブラリは、シンク関数に渡すことができる信頼できるタイプをすでに生成しています。たとえば、 DOMPurifyを使用してHTMLスニペットをサニタイズし、XSSペイロードを削除できます。
import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(html, {RETURN_TRUSTED_TYPE: true});
DOMPurifyはTrustedTypesをサポートし、ブラウザーが違反を生成しないようTrustedHTML
れたHTMLを返します。
信頼できるタイプのポリシーを作成する #
機能を削除できない場合があり、値をサニタイズして信頼できるタイプを作成するためのライブラリがありません。そのような場合は、TrustedTypeオブジェクトを自分で作成してください。
そのためには、最初にポリシーを作成します。ポリシーは、入力に特定のセキュリティルールを適用するトラステッドタイプのファクトリです。
if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
const escapeHTMLPolicy = trustedTypes.createPolicy('myEscapePolicy', {
createHTML: string => string.replace(/\</g, '<')
});
}
このコードは、 createHTML()
関数をTrustedHTML
オブジェクトを生成できるmyEscapePolicy
定義されたルールは、新しいHTML要素の作成を防ぐために、 <
次のようなポリシーを使用します。
const escaped = escapeHTMLPolicy.createHTML('<img src=x onerror=alert(1)>');
console.log(escaped instanceof TrustedHTML); // true
el.innerHTML = escaped; // '<img src=x onerror=alert(1)>'
デフォルトのポリシーを使用する #
問題のあるコードを変更できない場合があります。たとえば、これは、CDNからサードパーティのライブラリをロードする場合に当てはまります。その場合は、 デフォルトのポリシーを使用してください。
if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
trustedTypes.createPolicy('default', {
createHTML: (string, sink) => DOMPurify.sanitize(string, {RETURN_TRUSTED_TYPE: true})
});
}
信頼できるタイプのみを受け入れるシンクで文字列が使用される場合は常に、 default
の名前のポリシーが使用されます。
コンテンツセキュリティポリシーの実施に切り替える #
アプリケーションで違反が発生しなくなったら、信頼できるタイプの適用を開始できます。
Content-Security-Policy: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
出来上がり!これで、Webアプリケーションがどれほど複雑であっても、DOM XSSの脆弱性をもたらす可能性があるのは、ポリシーの1つに含まれるコードだけです。 ポリシーの作成を制限することで、それをさらに制限できます。