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" (正しいが、二重エンコードを知っている必要がある)
一般的な原因:
- すでにエンコードされたユーザー入力のエンコード: URLがフォームフィールドにペーストされ、フォーム送信が再度エンコードする
- フレームワークの二重エンコード: 一部のHTTPクライアントライブラリが自動的にパラメータをエンコードする。値を事前にエンコードすると二重にエンコードされる
- リダイレクトチェーン: 各リダイレクトがURLを再エンコードする可能性がある
- アドレスバーからのコピーペースト: ブラウザはデコードされた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エンコード問題のデバッグ。