URLの二重エンコード問題

URLの二重エンコードとは何か、原因、検出方法、修正方法を解説。スペースの代わりに%2520が表示される理由と防止策を学びましょう。

Character

%25

Encoded

%2525

詳細な説明

二重エンコードは、すでにpercent-encodingされた文字列が再度エンコードされた場合に発生します。エンコード済みシーケンスの各%%25に再エンコードされ、破損したURLが生成されます。例えば、スペース(%20)が%2520になり、デコードするとスペースではなくリテラル文字列%20になります。

二重エンコードの発生過程:

元の値:         "hello world"
1回目のエンコード:  "hello%20world"
2回目のエンコード: "hello%2520world"
                       ^^ %25 はエンコードされた %

1回デコード:   "hello%20world"   (期待した結果ではない)
2回デコード:  "hello world"     (正しいが、二重エンコードを知っている必要がある)

一般的な原因:

  1. すでにエンコードされたユーザー入力のエンコード: URLがフォームフィールドにペーストされ、フォーム送信が再度エンコードする
  2. フレームワークの二重エンコード: 一部のHTTPクライアントライブラリが自動的にパラメータをエンコードする。値を事前にエンコードすると二重にエンコードされる
  3. リダイレクトチェーン: 各リダイレクトがURLを再エンコードする可能性がある
  4. アドレスバーからのコピーペースト: ブラウザはデコードされたURLを表示するが、エンコードされた形式をコピーする場合がある

JavaScriptでの検出と防止:

// URL内の %25 を探して二重エンコードを検出
const isDoubleEncoded = (url) => /%25[0-9A-Fa-f]{2}/.test(url);

// 安全なエンコード: すでにエンコード済みの場合はエンコードしない
function safeEncode(str) {
  try {
    // デコードで文字列が変わる場合、すでにエンコード済みの可能性がある
    const decoded = decodeURIComponent(str);
    if (decoded !== str) {
      // 文字列はすでにエンコード済み。そのまま返す
      return str;
    }
  } catch (e) {
    // decodeURIComponent がエラーをスロー。文字列は有効にエンコードされていない
  }
  return encodeURIComponent(str);
}

// 例
safeEncode("hello%20world"); // "hello%20world" (再エンコードされない)
safeEncode("hello world");   // "hello%20world" (1回だけエンコード)

検出アプローチに関する警告: 上記のsafeEncode関数はヒューリスティックであり、自然にパーセント記号を含む文字列("100%20 discount"など)に対して不正確な結果を生成する可能性があります。文字列がすでにエンコードされているかどうかを確実に検出する方法はありません。ベストプラクティスはエンコードの境界を明確に保つことです:生の文字列が入力され、エンコードされた文字列が出力され、エンコードはURLが構築される時点で正確に1回行われます。

実際の症状:

  • %2520(二重エンコードされたスペース)や%253D(二重エンコードされた等号)を含むURL
  • 検索結果にリテラルの%20が表示される
  • 遷移先URLが二重エンコードされているためリダイレクトURLが失敗する
  • パラメータにリテラルのパーセントシーケンスが含まれるためAPIエラーが発生する

落とし穴: 二重エンコードの最も厄介な形態は、システム間の境界で暗黙的に発生するものです。サービスAがURLをエンコードしてサービスBに渡し、サービスBがリダイレクトに埋め込む際に再度エンコードします。ユーザーは壊れたURLを目にしますが、どちらのチームも単独では問題を再現できません。サービス間のエンコード規約を明示的に文書化してください。

ユースケース

スペースの代わりに%2520が表示されたり、APIパラメータにデコードされた文字の代わりにリテラルのパーセントシーケンスが含まれる、WebアプリケーションのURLエンコード問題のデバッグ。

Try It — URL Encoder

フルツールを開く