不適切な出力エンコーディング・エスケープ

不適切な出力エンコーディング・エスケープ

説明

JavaScriptの replace で $STR.replace('文字', '置換後の値') のように文字列を検索すると、最初に見つかった箇所だけが置換されます。この処理を危険な文字のエスケープに使うと、2回目以降の文字が残り、攻撃者に処理を回避されるおそれがあります。

想定される影響

  • 出力先で特別な意味を持つ文字が残ることによる、スクリプト挿入やSQLインジェクション
  • 不完全なエスケープやフィルタリングの回避

対処方法

  • すべての出現箇所を置換する必要がある場合は、正規表現とグローバルフラグ(g)を使用してください。クォートの置換だけでSQLインジェクションを防ごうとせず、SQLにはパラメーター化クエリを使用してください。
  • 出力先のコンテキストに合うエンコーダーを使用してください。HTMLマークアップを許可する必要がある場合は、DOMPurifyなどのサニタイズライブラリを使用してください。
  • HTMLの本文テキストでは、&、<、>、"、' などの必要な文字をHTMLエンティティに変換してください。この処理をJavaScript、CSS、URLなどの別のコンテキストにそのまま適用しないでください。

例

変更前

javascript
function escapeQuotes(s) {
  return s.replace("'", "''");
}

function escapeHtml(html) {
  return html.replace("<", "&lt;").replace(">", "&gt;");
}

変更後

javascript
function escapeQuotes(s) {
  return s.replace(/'/g, "''"); // すべてのシングルクォートを置換
}

function escapeHtml(html) {
  return html.replace(/[&<>"']/g, (ch) => ({
    "&": "&amp;",
    "<": "&lt;",
    ">": "&gt;",
    '"': "&quot;",
    "'": "&#x27;",
  }[ch])); // HTMLのテキストに必要な文字をエンコード
}

解説:

  • 変更前: .replace("文字", ...) は最初の出現箇所だけを置換します。出力先のコンテキストで処理が必要な他の文字にも対応する必要があります。
  • 変更後: 正規表現とグローバルフラグ(g)ですべての出現箇所を処理しています。HTML用の関数は本文テキストに必要な文字をエンティティに変換し、文字の繰り返しによる回避を防いでいます。

参考資料