jQueryのDOM操作によるクロスサイトスクリプティング

jQueryのDOM操作によるクロスサイトスクリプティング

説明

ユーザーが制御する文字列をjQueryの html()、append()、prepend()、wrap()、wrapInner()、wrapAll()、before()、after() にHTMLとして渡すと、悪意のあるマークアップが実行されるおそれがあります。globalEval() は文字列をコードとして実行し、getScript() はURLからスクリプトを取得して実行するため、コードとURLの信頼性をそれぞれ確認する必要があります。

想定される影響

  • 悪意のあるスクリプトにより、読み取り可能なCookieや認証情報が漏えいするおそれがあります。
  • セッションを乗っ取られ、ユーザーの権限を悪用される可能性があります。
  • 挿入されたHTMLやJavaScriptでフィッシング画面を表示されるおそれがあります。

対処方法

  • テキストには text()、フォームの値には val() など、用途に合うAPIを使ってください。
  • HTML本文に入れるテキストはエンコードし、実際のHTMLを許可する場合は信頼できるサニタイザーで制限してください。
  • globalEval() に外部入力を渡さず、getScript() には固定された信頼できるスクリプトURLだけを使ってください。HTMLエスケープは、コード実行の保護やスクリプトURLの検証を代替しません。

例

変更前

javascript
// 入力をDOM操作メソッドに直接渡す
function vulnerable(userInput) {
  $("#container").html(userInput); // XSSのリスク
}

// 別の例
const hash = window.location.hash;
$("div").append(hash); // HTMLとして解釈される実際の値によってリスクが異なる

変更後

javascript
// テキストの表示にはtext()を使用
function safe(userInput) {
  $("#container").text(userInput); // テキストとして表示
}

// またはHTMLへ挿入する前にエスケープ
function escapeHTML(str) {
  return String(str).replace(/[&<>'"]/g, function (c) {
    return {
      "&": "&amp;",
      "<": "&lt;",
      ">": "&gt;",
      '"': "&quot;",
      "'": "&#39;",
    }[c];
  });
}

function safeWithEscape(userInput) {
  $("#container").html(escapeHTML(userInput)); // エスケープ後に挿入
}

解説:

  • 変更前: 信頼できない値をHTMLとして解釈するAPIへ渡しています。現在のブラウザーはURLフラグメントを読み取るときにパーセントデコードしないため、window.location.hash にエンコードされたタグがあるだけで実行されるわけではありません。別途行うデコードも含め、実際のHTML値がどう作られるかを確認してください。
  • 変更後: text() またはHTML本文用のエスケープによって、この出力位置では値をテキストとして表示します。

参考資料