Percent Encoding Inspector(パーセントエンコーディング検査)

パーセントエンコーディングは 1 つの規則ではなく複数あり、十数種類の文字を境に意見が分かれます。このページは同じテキストを encodeURIComponent、encodeURI、application/x-www-form-urlencoded シリアライゼーションに並べて通し、意見が分かれる文字に印を付け、文字ごとの行に UTF-8 バイトと RFC 3986 クラスを印字し、文字列が何回エンコードされたかを示します。 デコード側は厳格または寛容の規則で文字列を読み、黙って間違った結果を返す代わりに、どのエスケープが壊れているかとその位置を示します。
すべての処理はブラウザー内でローカルに行われます — データがサーバーへアップロードされることはありません。

パーセントエンコーディングが何か分かりませんか?パーセントエンコーディングを平易な言葉で解説 — それが何か、なぜ空白が %20や + になるのか、URL が %25 を示すとき何を意味するのか。
この機能:
エスケープの読み方:
encodeURIComponent
encodeURI
form(application/x-www-form-urlencoded)
入力欄で Ctrl+Enter を押すとアクティブなモードが実行されます。
モードを選び、そのアクションボタンをクリックしてください。エンコード・検査はテキストの 3 つのエンコーディングに加えて文字ごとの行を表示し、デコードはエスケープを読み戻して層数を数えます。セルフテストボタンは記録されたすべてのサンプル、印字可能な ASCII 文字すべて、このページのデータファイルに記録された層のチェーンを再実行し、件数をここに報告します。
エンコーダーが意見を分けるところ:3 つの方式が違う扱いをするのは下の文字だけです。それ以外は 3 つすべてで無変更で通るか、3 つすべてが同一のエスケープを書きます。
すべての文字、1 文字 1 行:文字が変換される UTF-8 バイト、RFC 3986 が置くクラス、各文脈が出力するエスケープ。4 つの文脈列は WHATWG URL の percent-encode セットで、ブラウザーがパス・クエリ・フラグメントを組み立てるときに使うものです。網掛けの行は 3 つのエンコーダーが違う扱いをする文字です。
URL の各部分に対してこのページがエスケープする正確な集合:エンコーダー自身から印字されるため、このテーブルと上の列を埋めるコードがずれることはありません。すべての集合はさらに C0制御文字(U+0000〜U+001F)と U+007E を超えるすべてのコードポイントをエスケープします。非 ASCII テキストがすべての文脈で常にエスケープされるのはこのためです。
これらの集合は WHATWG の percent-encode セットから導出され、2 つの意図的な場所で異なります。どちらも手動組み立てを安全にするためです:クエリ内ではハッシュがエスケープされます(生のハッシュはフラグメントを始めてしまうため)。そして component セットは encodeURIComponent がエスケープする文字 — アポストロフィ込み — に正確に固定され、form セットは URLSearchParams がエスケープするものに固定されます。したがって上のテーブルの 2 つの列は、再実装ではなくプラットフォーム自身の挙動です。このリストは、人が意外に思う結果も説明します:パーセント記号は encodeURIComponent と encodeURI がエスケープしますが、WHATWG のどの文脈セットにも入っていないため、文字ごとのテーブルのパス・クエリ・フラグメント列はそれをそのまま残します。手で組み立てた URL の生のパーセント記号は依然としてリスクです。規格は入力が先に検証済みであることを期待するからです:%41 はクエリ内で多くのサーバーフレームワークには単一の A であり、このページ自身の厳格な読みは孤立した % を壊れたエスケープとして扱います。自分で %25 としてエスケープするほうが安全な習慣で、文字ごとのテーブルが両方の綴りを並べて示します。
この文字列がエンコードされた回数:デコードの各ラウンドが 1 層です。2 回エンコードされた値は%25で始まり、1 回のエンコードなら%20が残ります。3 回エンコードされた値は%2525.
URL がそのまま運べる文字:RFC 3986 は ASCII 範囲を 3 つのグループに分け、エスケープ規則はその分割に対してのみ意味を持ちます。
予約文字は禁止されているのではなくすでに使われているのです:URL パーサーの中で意味を持っています。リストから導かれる規則は短いです — 文字が予約されていてデータとして意味するつもりならエスケープする。非予約なら決して必要ない。
3 つの方式と、それぞれがそのまま残すもの:
リストは各方式が ASCII 範囲でエスケープせずにそのまま残す正確な集合です。セルフテストは印字可能な ASCII 文字すべてをそれらに対してチェックするため、テーブルが記述するエンコーダーからずれることはありません。
このページが受け付ける入力の大きさと、その理由:パーセントエンコーディングはテキストへの 1 回のパスなので、このサイトの基数コンバーターと違い、コストは 2 乗ではなく直線的に増えます。算術自体が問題になる長さは存在しません。下の上限が存在するのは、文字ごとのテーブルが文書内に描画されるためであり、100万行のテーブルこそが実際にブラウザーを止めるものだからです。
パーセントエンコーディングとは?

パーセントエンコーディング — URL エンコーディングとも、古い文書ではエスケーピングとも呼ばれる — は、URL が含めてはならない文字を運べるようにする規則です。パーセントエスケープは 3 つの ASCII 文字です:パーセント記号と 2 桁の 16 進数字で、1 バイトを表します。%20 はバイト 0x20、つまり空白です。%2F はバイト 0x2F、つまりスラッシュです。他に何も関与しません。エスケープが表すのは文字ではなくバイトです。

バイトを表すため、テキストは先にバイトへ変換されなければならず、そのバイトエンコーディングは UTF-8 です。長いエスケープの由来はここにあります:中国語 1 文字は UTF-8 の 3 バイト、つまり 3 つのエスケープであり、絵文字 1 つは 4 バイト、12 文字の ASCII です。

30 秒版:空白は URL の値としては %20、フォームデータでは +。# は %23。リテラルのパーセント記号は %25。このページの 3 つのエンコーダーは文字、数字、-_. について一致し、チルダを含む十数個の句読点について意見が分かれます — まさにそのため、同じテキストに対して encodeURIComponent と encodeURI が別の文字列を出力し、間違ったほうが 1 つの値を 2 つのクエリパラメーターに分割してしまうのです。
なぜ URL にはそれが必要なのか

URI は ASCII 文字のシーケンスとして定義され(RFC 3986 セクション 2.1)、その ASCII 範囲の中に予約された文字があります:URL に構造を与える句読点です。コロンと 2 つのスラッシュがスキームを分け、アットマークがホストからユーザー情報を分け、疑問符がクエリを始め、ハッシュがフラグメントを始め、アンパサンドがクエリパラメーターを分け、パーセント記号がエスケープを導入します。

それが 1 つの問題と 1 つの抜け道を作ります。問題:それらの文字を自分自身で含む値 — 「a&b」を含む検索ボックス、スラッシュを含むファイル名、コロンを含むパスワード — は、そのままでは運ばれる URL の構造を変えてしまいます。抜け道:パーセント記号自身であり、エンコーダーは自分のアルファベットを単純に拡張できます。% は %25 と書かれ、その後の 2 桁の 16 進数字は普通の非予約文字です。

具体例

これはこのページが実行する 3 つのエンコーダーの正確な出力で、互いに違う振る舞いをするよう選ばれた入力に対するものです。ページはあなたの目の前で再計算し、セルフテストはすべての行をプラットフォーム自身の実装に対して再チェックします。

3 つの方式と、それぞれが正しい選択になるとき

ほとんどのバグを防ぐ短い版:単一のパラメーター値に入るものはすべて encodeURIComponent を通す(または別の安全集合で同じ役割を果たすフォームシリアライザー)。そしてすでに組み立てられた完全な URL は、エスケープが必要なら encodeURI を通す。この 2 つの取り違えが、Web コードで最も一般的なパーセントエンコーディングの間違いです。

URL が運べる文字、全リスト
2 つのプラス記号、2 つの意味

空白は、テキストの行き先によってエンコードが変わる唯一の文字であり、プラス記号がその理由です。

罠はそのテーブルの中央にあります。フォームデータではプラス記号が空白を意味するため、本当にプラス記号を含む値 — +44 20 7946 0958 と書かれた電話番号、算術式、プラスタグ付きのメールアドレス — は %2B として運ばれなければなりません。リテラルのプラスとして運ばれると、受信者はそれを空白として読み戻し、+44 は「 44」として届きます。

2 回エンコード、そしてその見分け方

2 重エンコードは、すでにエスケープされた文字列がもう一度エスケープされたときに起こります。最初のエスケープのパーセント記号も他の文字と同じように 1 文字なので %25 になり、下の層は目に見えるテキストとして生き延びます。

見分けるのは数えることです:文字列を 1 回デコードして結果を見てください。それでも意図的に見えるエスケープ — 有効な %XX、または空白が期待される場所のフォームのプラス記号 — を含むなら、もう 1 層あります。%25 の後に 2 桁の 16 進数字が続くなら、次の層は確実にエスケープです。

実務的な注意が 2 つ。第一に、2 回のデコードは無害ではありません:セキュリティフィルターとその背後のコードが別の文字列を見るよく知られた方法であり、2 回目のデコードの前に実行されるフィルターは間違ったテキストをフィルタリングしています。第二に、ここの層数は文字列の属性であって意図についての約束ではありません:正当な値が 1 層の深さであることも、壊れた値が 2 層であることもあります。

このページが捕まえるために作られた間違い
  • encodeURI でエンコードされた値。区切りが生き延びるため、& や = を含む値は向こう側で複数のパラメーターに分割されます。このページの上の 2 列を入力 a&b=c で比較してください:component 列は両方の文字をエスケープし、encodeURI 列は保持します。
  • フォームデータとして送られたリテラルのプラス記号。空白として届きます。%2B を使ってください。
  • 「念のため」2 回エスケープされた値。安全にはなりません。受信者が 1 回デコードすると、パーセントエスケープをまだ含む文字列が得られます。それは 1 層下の文字集合の間違いと同じ失敗です。
  • エンコードされたことのないものをデコードする。テキストの中のリテラルのパーセント記号 — 100% sure — はエスケープではありません。厳格な読みでは壊れたエスケープであり、このページが報告する位置にあります。寛容な読みでは生き延びます。だからこそここでは 寛容が既定であり、その逆ではないのです。
  • エスケープは大文字でなければならないと仮定する。デコードは %2f と %2F を同じバイトとして扱い、どちらも有効です。大文字は RFC 3986 が推奨する正規の綴りであり、他のツールが出力する形にすぎません。
  • パーセントエンコーディングがテキストを安全にすると仮定する。URL の中で曖昧さをなくすだけです。注入を止めず、暗号化ではなく、サニタイザーでもありません。エスケープは、テキストが行く先の文脈に対して、組み立て直前の最後の工程でなければなりません。
クイック回答

「URL でプラス記号は空白なのですか?」

フォームデータの中でだけ、しかもフォーム送信のクエリ部分でだけです。パス、フラグメント、普通のリンクのクエリでは、プラス記号はリテラルのプラス記号です。だからこそこのページは推測の代わりに切替スイッチを持っています。

「リンクに %25 が表示されるのはなぜ?」

何かがパーセント記号をエンコードしたためです。リテラルのパーセント記号がエンコーダーを通ったか(100% sure は 100%25%20sure になる)、すでにエスケープを含む文字列がもう一度エンコードされたかのどちらかです。文字列を 1 回デコードして何が返るか見てください:結果がまだ %XX を含むなら、もう 1 層あります。

「クエリ値の中のスラッシュが %2Fか / かは重要ですか?」

URL パーサーにとっては重要ではありません:どちらもサーバーへはスラッシュとして届き、WHATWG のクエリセットはスラッシュをそのまま残します。相互運用性にとってはときどき重要です:多くのサーバーフレームワークが生のクエリ文字列に独自のデコードを実行し、そのうちのいくつかは、エスケープされたことのない文字について意見が分かれます。値がシリアライザーではなく手で組み立てられる場合は、予約文字を明示的にエスケープするほうが安全な習慣です。

「フォーム列が、他の 2 つがそのまま残すチルダをエスケープするのはなぜ?」

フォームエンコーダーの安全集合が狭いためです:文字、数字、-_.* だけです。WHATWG のフォームエンコーダーは、RFC 3986 が ~ を非予約として挙げているにもかかわらず、~ ! ' ( ) をエスケープします。どちらの綴りも同じ値へデコードされます。違いが問題になるのは文字列をバイト単位で比較するときだけです。

「同じ文字がブラウザーのアドレスバーではエスケープされずに現れることがありますか?」

はい、しかしそれは URL ではなくアドレスバーです。ブラウザーは URL の読める形を表示し、送信時にエンコードするため、リンクの中の こんにちは は通信路上で %E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF になります。アドレスバーから URL をコピーすると、ブラウザー次第でどちらの形も得られ得ます。

「ここでのエスケープはすべて有効に見えるのに、出てくる文字が期待したものではないのはなぜ?」

2 つの異なる失敗が「デコードが間違っていた」の背後に隠れており、ページはそれらを違う方法で報告します。第一は壊れたエスケープです:2 桁の 16 進数字が続かないパーセント記号、たとえば末尾の 100% や %zz。これは構文の問題であり、厳格な読みはそれを拒否してどこにあるかを示し、寛容な読みはそれをそのままのテキストとして保持して一覧します。第二はバイトが完全に合法でも、そのバイトが有効な UTF-8 文字を綴らないエスケープです。たとえば単独の %FF や、3 バイト文字が %E4%B8 と切り詰められたもの。これは構文の問題ではないため、両方の読みがテキストを返し、デコードできないバイトを置換文字 U+FFFD("�")に置き換え、その旨の警告を一覧します。その警告こそ有用な部分です。置換文字として届くテキストは、たいてい送信者が別の文字集合を使ったか、文字列が 1 回多くエスケープされたことを意味します。パーセントエンコーディングはバイトを表し、このページも URL 規格と同じく、常にそれらのバイトを UTF-8 として読みます。

「このページがしないことは何ですか?」

URL をスキーム・ホスト・パス・クエリに分割しません — それは URL パーサーの役割です。リンクを開いて安全かチェックせず、何にも接触せず、エスケープの背後のバイトの文字集合を推測しません:常に UTF-8 として読みます。URL 規格が要求する通りです。

「デコードにはどのフォールバックが必要で、それが正しいとどう分かりますか?」

セルフテストを押してください。ページ自身のエンコーダーをプラットフォームの encodeURIComponent、encodeURI、URLSearchParams と印字可能な ASCII 文字すべてに対して比較し、上のテーブルのすべての例を再実行し、記録された層のチェーンを再チェックし、その件数をここに報告します。

規格とスコープ

パーセントエンコーディングは RFC 3986 セクション 2.1 で規定され、予約文字と非予約文字の定義はセクション 2.2 と 2.3、大文字小文字の規則はセクション 6.2.2.1 にあります。ブラウザーが URL を組み立てるときに実際に使う集合は WHATWG URL Standard にあり、文脈で分割します:フラグメント用、クエリ用、特殊クエリ用、パス用、ユーザー情報用、そして単一の component 用のより狭い集合。プラス記号を含むフォーム送信は HTML Standard の application/x-www-form-urlencoded の下にあります。encodeURI と encodeURIComponent の 2 つの関数は ECMAScript 自体の一部です。

このページはパス・クエリ・フラグメント列を自分の文脈別集合から埋め、結果の隣に各文脈の正確なリストを印字します。それらの集合は WHATWG のものから導出され、2 つの意図的な場所で異なります:クエリ内ではハッシュがエスケープされる(生のハッシュはフラグメントを始めるため)、そして component セットは encodeURIComponent がエスケープする文字に正確に固定される。プラットフォーム関数が答えの定義である場所 — encodeURIComponent、encodeURI、フォームシリアライゼーション — では、その関数が直接呼ばれます。したがってそれらの列は再実装ではなくブラウザー自身の挙動です。

ページのテキストは本サイトのために書かれました。上のテーブルのすべての値は読み込み時にページ自身のコードが出力し、本サイトのテストツールでプラットフォームの実装に対して検証済みです。セルフテストがその比較をあなたのブラウザーで繰り返します。