レッスン 2
パーセントエンコードのルール
`%XX` がバイトを表す仕組みと、エンコードが必要な文字。
パーセントエンコードは、エスケープする各オクテットに % とちょうど 2 桁の 16 進数を書きます。
byte 32 (space) → %20
byte 38 (&) → %26
byte 231 (decimal) → hex E7 → %E7 (意味はデコードコンテキスト次第!)
A–F は大文字小文字どちらも可(%e7 ≡ %E7)。ツールは一貫した方が相互運用しやすいが、パーサーは混在を受け入れるべき。
1 エスケープ = 1 バイト(「1 UTF-16 コードユニット」ではない)
Unicode 文字列をエンコードするとき、スキームが別途定めない限り UTF-8 のオクテットをエンコード:
π (U+03C0)
UTF-8 bytes: CF 80 → %CF%80
誤ったメンタルモデル: コードポイント桁 "03C0" を hex でエンコード
正しいモデル: 先にバイト列化し、問題のある各バイトを %HH
どのバイトをエスケープするか
おおよそのチェックリスト:
- URI 区切りと相互作用するグラフィック文字(
?,&,#,/,:,@,[,])で、それらが句読点ではなく不透明データに属するとき。 - ASCII 外のバイト(広い互換パス向け)。
- 制御バイト、スペース、DEL、曖昧な空白(パラメータ内のリテラルデータとして)。
RFC 3986 は汎用ルールと正規化動作を定義; フレームワークは厳格化することがある(例: stray % 尾を禁止)。
無効なエスケープ
不完全シーケンスはパーサーを混乱させる:
% → 不正(桁なし)
%AB → 不正(2 ニブル必要)
%GG → 無効な hex 桁
良いライブラリは不正入力を検証または拒否し、黙って「最善推測」しない。
過剰エンコードと二重エンコード
percent エンコードを 2 回適用するのは繰り返しバグ:
literal value: 100%
first pass: 100%25 (リテラル % が %25 に)
mistaken pass: 100%2525 (既に安全な % を再エンコード — 表示意図と違う)
サーバーが %2525 を受け取ると、2 回デコードして単一 % — 表示データの意図ではない。サブシステムがエンコード済みかデコード済みか一度決め、その不変条件を明確に。
正規化のヒント
正規化には次の選択が含まれる:
- 16 進の大文字 vs 小文字(どちらも有効)。
- コンポーネント内で法的に曖昧でないとき、句読点をエンコードするかリテラルに残すか。
- エンコード前に Unicode を NFC に正規化(任意だが、綴り重複でサーバーが分岐するのを避ける)。
ゲートウェイ(CDN vs アプリ vs DB)間で一貫性を保ち、署名検証やキャッシュ中の微妙な不一致を避ける。