DESIGN / CODE / INFRA / AI

解説

検索語に&や+が入ると壊れる?encodeURIとencodeURIComponentを分けよう

同じ検索語からURLを組み立て、実際のクエリ値を比較。URL全体と値の部品、予約文字、二重エンコードの違いを通信しない作例で確認。

検索語に&や+が入ると壊れる?encodeURIとencodeURIComponentを分けよう
保存リストを見る →
この記事の道しるべ 5項目

検索語をURLへ付けたら、&より後ろが別の項目になった。URL全体を扱うencodeURIと、値の部品を扱うencodeURIComponentは、残す文字が違うよ。

今回は架空のURLだけを作り、ブラウザのURL解析で結果を見る。表示したURLへアクセスしたり、検索語を外部へ送ったりはしない作例だよ。

URLの区切りに意味のある文字がある

URLのクエリでは、&が項目の区切り、=が名前と値の区切りなどとして使われる。#より後ろはフラグメントという目印になる。

こうした文字を検索語そのものに含めたい場合、そのまま付けると意味を取り違えることがある。日本語や空白だけを確認して終わらず、記号も使って試そう。

同じ値を二つの方法で入れてみる

const value = "red&blue=1#note";
const prefix = "https://example.invalid/search?q=";
const whole = new URL(prefix + encodeURI(value));
const part = new URL(prefix + encodeURIComponent(value));
console.log(whole.searchParams.get("q")); // "red"
console.log(part.searchParams.get("q"));  // "red&blue=1#note"

encodeURIは、URLの構造に使う一部の記号を残す。そのため、検索語の部品だけに使った最初の例では、&や#が構造として解釈され、qの値がredだけになる。

encodeURIComponentは値の部品を扱うので、今回の記号もエンコードする。解析し直したqには、元の検索語が残るよ。

作例では、組み立てたURLと解析結果を比べる

  1. 記号付きの検索語を選び、二つの関数を切り替える。
  2. 表示されたURL、qの値、クエリの項目、フラグメントを見る。
  3. a+bも選び、+がそのまま残る場合と、%2Bになる場合を比べる。
  4. 「最初から試す」で最初の検索語とencodeURIへ戻す。

URLSearchParamsでは、クエリの+を空白として扱う。a+bをそのまま付ければa bになり、+を値として残すなら適切なエンコードが必要だね。

名前と値を渡して組み立てる方法もある

const url = new URL("https://example.invalid/search");
url.searchParams.set("q", "red&blue=1#note");
console.log(url.searchParams.get("q"));

URLSearchParamsへ元の値を渡せば、必要な形式で組み立てられる。この方法で先にencodeURIComponentした値を渡すと、%がさらにエンコードされるなど、二重の変換になる場合があるよ。

「いつでも両方呼ぶ」という覚え方ではなく、今の値が未変換の文字列なのか、URLの一部なのかを確認しよう。

エンコードは入力の安全性を保証しない

URLの構造を壊さず運ぶ処理と、その値を受け取ったサーバーで許可・検証する処理は別。エンコードしたから、SQLやHTMLなどの文脈でも安全になるわけではない。

編集部ではNode.jsとChromiumで三つの検索語の組立て・解析とリセットを確認した。実際の検索APIへ送った試験ではないよ。同じ名前の項目を扱う場合はURLSearchParamsのappendとsetも参考になるね。

動かしてみよう:URLと検索語の記号

同じ値を二つの関数で組み立て、qとフラグメントを読み戻そう。通信はしないよ。

参考・出典

公開と変更の記録
  • :初回公開

日時は日本時間です。記載のある変更は、確認できた編集記録に基づきます。更新日の違いだけから変更内容を推測して載せることはありません。

更新・訂正の方針 →

みんなのコメント 0

感想や、試してみたことを聞かせてね。コメント・返信は承認後に公開されます。

最初のコメントをどうぞ。