Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 12846] ファイルの概要

このコミットは、Go言語のnet/urlパッケージにおけるURLエスケープ処理を、より厳密なRFC 3986の仕様に準拠させるための修正です。具体的には、shouldEscape関数がRFC 3986で予約文字として定義されている一部の文字を正しくエスケープしていなかった問題を解決し、一部の厳格なWebサーバーとの互換性を向上させます。

コミット

commit 56024fa64ebe87d5b209fa5d126b71ab27f7cf3d
Author: Stéphane Travostino <stephane.travostino@gmail.com>
Date:   Thu Apr 5 13:23:08 2012 -0400

    net/url: Correctly escape URL as per RFC 3986
    
    The shouldEscape function did not correctly escape the reserved characters listed in RFC 3986 §2.2, breaking some strict web servers.
    Fixes #3433.
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/5970050

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/56024fa64ebe87d5b209fa5d126b71ab27f7cf3d

元コミット内容

net/url: Correctly escape URL as per RFC 3986

The shouldEscape function did not correctly escape the reserved characters listed in RFC 3986 §2.2, breaking some strict web servers.
Fixes #3433.

R=rsc
CC=golang-dev
https://golang.org/cl/5970050

変更の背景

この変更の背景には、Go言語のnet/urlパッケージが提供するURLエスケープ機能が、一部の「予約文字(Reserved Characters)」を適切に処理していなかったという問題があります。

URLのエスケープ処理は、URLの構文を維持しつつ、特殊な意味を持つ文字や非ASCII文字を安全に表現するために不可欠です。RFC 2396はURLの一般的な構文を定義していましたが、その後のRFC 3986はURI(Uniform Resource Identifier)の一般的な構文をより詳細に定義し、URLのエスケープに関する規則も更新しました。

Goのnet/urlパッケージのshouldEscape関数は、当初RFC 2396に基づいて実装されていましたが、RFC 3986のセクション2.2で定義されている予約文字の一部(特に!, ', (, ), *)を「非予約文字(Unreserved Characters)」として扱っていました。これにより、これらの文字を含むURLが生成された場合、一部の厳格なWebサーバーがRFC 3986の仕様に厳密に従ってURLを解析するため、不正なURLとして認識し、リクエストが失敗する可能性がありました。

この問題は、GoのIssue #3433として報告され、特定のWebサーバーとの相互運用性の問題を引き起こしていました。このコミットは、この問題を解決し、GoのURLエスケープ処理をRFC 3986の最新かつ厳密な仕様に準拠させることを目的としています。

前提知識の解説

URLエスケープとは

URLエスケープ(パーセントエンコーディングとも呼ばれる)は、URL内で特別な意味を持つ文字(例: /, ?, &, =)や、URLとして直接表現できない文字(例: スペース、日本語などの非ASCII文字)を、安全にURLに含めるためのメカニズムです。これらの文字は、%の後にその文字のASCII値またはUTF-8エンコーディングの16進数を続ける形式で表現されます(例: スペースは%20)。

RFC 2396とRFC 3986

  • RFC 2396 (Uniform Resource Identifiers (URI): Generic Syntax): 1998年に公開されたURIの一般的な構文を定義した初期の標準です。URL(Uniform Resource Locator)はURIの一種です。このRFCでは、URIを構成する文字を「予約文字(Reserved Characters)」と「非予約文字(Unreserved Characters)」に分類し、エスケープの必要性を規定しました。

    • 予約文字: URIの構文デリミタとして特別な意味を持つ文字(例: ?, /, #, &, =など)。これらは、その文字が持つ特別な意味で使われる場合はエスケープされませんが、データの一部として使われる場合はエスケープが必要です。
    • 非予約文字: URI内で特別な意味を持たず、常にそのまま使用できる文字。これらはエスケープする必要がありません。RFC 2396では、英数字と一部の記号(-, _, ., !, ~, *, ', (, ))が非予約文字とされていました。
  • RFC 3986 (Uniform Resource Identifier (URI): Generic Syntax): 2005年に公開されたRFC 2396の改訂版であり、現在のURIの主要な標準です。このRFCは、URIの構文をより明確にし、特にエスケープに関する規則を更新しました。RFC 3986では、RFC 2396で非予約文字とされていた一部の記号(!, ', (, ), *)が、より厳密な解釈の下で「予約文字」または「サブデリミタ(sub-delims)」の一部として扱われるようになりました。これにより、これらの文字は、特定のコンテキストで特別な意味を持たない限り、エスケープされるべきであるという解釈が強まりました。

予約文字と非予約文字(RFC 3986の観点から)

RFC 3986では、URIを構成する文字は以下のカテゴリに分類されます。

  • 予約文字 (Reserved Characters):

    • gen-delims (general delimiters): ?, /, #, [, ], @, :
    • sub-delims (sub-delimiters): !, $, &, ', (, ), *, +, ,, ;, = これらの文字は、URIの構文上の区切り文字として使用されるため、データの一部として使用する場合はパーセントエンコードする必要があります。
  • 非予約文字 (Unreserved Characters):

    • 英数字 (ALPHA, DIGIT)
    • -, ., _, ~ これらの文字は、URI内で特別な意味を持たず、常にそのまま使用できます。したがって、パーセントエンコードする必要はありません。

このコミットのポイントは、RFC 2396では非予約文字とされていた!, ', (, ), *が、RFC 3986ではsub-delimsの一部として予約文字に分類された点にあります。GoのshouldEscape関数がこれらの文字をエスケープしないままだと、RFC 3986に厳密に従うシステムでは問題が発生する可能性がありました。

技術的詳細

このコミットの技術的な核心は、Go言語のnet/urlパッケージ内のshouldEscape関数のロジック変更にあります。この関数は、与えられたバイトがURLの一部としてエスケープされるべきかどうかを決定します。

変更前は、shouldEscape関数はRFC 2396の定義に基づいており、以下の文字を非予約文字(エスケープ不要)として扱っていました。

  • 英数字 (A-Z, a-z, 0-9)
  • マーク文字 (-, _, ., !, ~, *, ', (, ))

しかし、RFC 3986のセクション2.3「Unreserved Characters」では、非予約文字は英数字と'-', '.', '_', '~'のみと定義されています。RFC 2396で非予約文字とされていた'!', '*', '', '(', ')'は、RFC 3986では「予約文字」のサブカテゴリである「サブデリミタ(sub-delims)」に含まれるようになりました。

このコミットでは、shouldEscape関数から、RFC 3986で非予約文字ではないとされた'!', '*', '', '(', ')'が、エスケープ不要な文字のリストから削除されました。これにより、これらの文字はデフォルトでエスケープされるようになります。

また、テストファイルsrc/pkg/net/url/url_test.goescapeTests配列に、これらの変更された文字(!, ', (, ), *)を含む新しいテストケースが追加され、それらが正しくエスケープされることを確認しています。さらに、RFC 3986で予約文字とされている:/@$,;もテストケースに追加され、これらも適切にエスケープされることを確認しています。

この修正により、Goのnet/urlパッケージは、RFC 3986に厳密に準拠したURLエスケープ処理を提供し、より広範なWebサーバーやシステムとの互換性が確保されます。

コアとなるコードの変更箇所

diff --git a/src/pkg/net/url/url.go b/src/pkg/net/url/url.go
index 88ff7ebfef..b6e79adc29 100644
--- a/src/pkg/net/url/url.go
+++ b/src/pkg/net/url/url.go
@@ -61,16 +61,16 @@ func (e EscapeError) Error() string {
 }
 
 // Return true if the specified character should be escaped when
-// appearing in a URL string, according to RFC 2396.
+// appearing in a URL string, according to RFC 3986.
 // When 'all' is true the full range of reserved characters are matched.
 func shouldEscape(c byte, mode encoding) bool {
-\t// RFC 2396 §2.3 Unreserved characters (alphanum)
+\t// §2.3 Unreserved characters (alphanum)
 \tif 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' {
 \t\treturn false
 \t}\n-\t// TODO: Update the character sets after RFC 3986.\n+\n \tswitch c {\n-\tcase '-', '_', '.', '!', '~', '*', '\'', '(', ')': // §2.3 Unreserved characters (mark)\n+\tcase '-', '_', '.', '~': // §2.3 Unreserved characters (mark)\n \t\treturn false
 \n \tcase '$', '&', '+', ',', '/', ':', ';', '=', '?', '@': // §2.2 Reserved characters (reserved)\ndiff --git a/src/pkg/net/url/url_test.go b/src/pkg/net/url/url_test.go
index 2d911ed505..d8b253142f 100644
--- a/src/pkg/net/url/url_test.go
+++ b/src/pkg/net/url/url_test.go
@@ -394,8 +394,8 @@ var escapeTests = []EscapeTest{
 \t\tnil,\n \t},\n \t{\n-\t\t" ?&=#+%!<>#\"{}|\\\\^[]`☺\\t",\n-\t\t"+%3F%26%3D%23%2B%25!%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09",\n+\t\t" ?&=#+%!<>#\"{}|\\\\^[]`☺\\t:/@$'()*,;",\n+\t\t"+%3F%26%3D%23%2B%25%21%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09%3A%2F%40%24%27%28%29%2A%2C%3B",\n \t\tnil,\n \t},\n }\n```

## コアとなるコードの解説

### `src/pkg/net/url/url.go`

*   **`shouldEscape`関数のコメント変更**:
    *   `// appearing in a URL string, according to RFC 2396.` から
        `// appearing in a URL string, according to RFC 3986.` へ変更されました。
        これは、この関数が準拠するRFCのバージョンがRFC 2396からRFC 3986へ更新されたことを明確に示しています。
*   **非予約文字の定義の変更**:
    *   `// RFC 2396 §2.3 Unreserved characters (alphanum)` のコメントが
        `// §2.3 Unreserved characters (alphanum)` に簡略化されました。
    *   `// TODO: Update the character sets after RFC 3986.` のコメントが削除されました。これは、このコミットによってRFC 3986への対応が完了したことを意味します。
    *   `switch c`文内の非予約文字のリストが変更されました。
        *   変更前: `case '-', '_', '.', '!', '~', '*', '\'', '(', ')':`
        *   変更後: `case '-', '_', '.', '~':`
        これにより、`'!'`, `'*'`, `''`, `'('`, `')'`の5つの文字が非予約文字のリストから削除されました。これらの文字はRFC 3986では予約文字(サブデリミタ)として扱われるため、`shouldEscape`関数はこれらの文字に対して`true`を返すようになり、結果としてURLエンコードされるようになります。

### `src/pkg/net/url/url_test.go`

*   **`escapeTests`配列のテストケースの更新**:
    *   既存のテストケースが更新され、より多くの文字がエスケープされることを検証しています。
    *   変更前: `" ?&=#+%!<>#\"{}|\\\\^[]`☺\\t"`
    *   変更後: `" ?&=#+%!<>#\"{}|\\\\^[]`☺\\t:/@$'()*,;"`
    *   追加された文字: `:/@$'()*,;`
    *   対応する期待されるエスケープ結果も更新されています。
        *   変更前: `"+%3F%26%3D%23%2B%25!%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09"`
        *   変更後: `"+%3F%26%3D%23%2B%25%21%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09%3A%2F%40%24%27%28%29%2A%2C%3B"`
        特に注目すべきは、`!`が`%21`に、`'`が`%27`に、`(`が`%28`に、`)`が`%29`に、`*`が`%2A`にエスケープされるようになった点です。これは、`shouldEscape`関数の変更が正しく機能していることを確認しています。また、`:`、`/`、`@`、`$`、`,`、`;`といったRFC 3986で予約文字とされる文字も適切にエスケープされることがテストされています。

これらの変更により、Goの`net/url`パッケージは、RFC 3986の仕様に厳密に準拠し、より堅牢なURLエスケープ処理を提供するようになりました。

## 関連リンク

*   GitHubコミットページ: [https://github.com/golang/go/commit/56024fa64ebe87d5b209fa5d126b71ab27f7cf3d](https://github.com/golang/go/commit/56024fa64ebe87d5b209fa5d126b71ab27f7cf3d)
*   Go CL (Code Review): [https://golang.org/cl/5970050](https://golang.org/cl/5970050)
*   Go Issue #3433: [https://code.google.com/p/go/issues/detail?id=3433](https://code.google.com/p/go/issues/detail?id=3433) (現在はGitHub Issuesに移行している可能性がありますが、当時のリンクです)

## 参考にした情報源リンク

*   RFC 3986 - Uniform Resource Identifier (URI): Generic Syntax: [https://datatracker.ietf.org/doc/html/rfc3986](https://datatracker.ietf.org/doc/html/rfc3986)
*   RFC 2396 - Uniform Resource Identifiers (URI): Generic Syntax: [https://datatracker.ietf.org/doc/html/rfc2396](https://datatracker.ietf.org/doc/html/rfc2396)
*   URLエンコードとデコード - MDN Web Docs: [https://developer.mozilla.org/ja/docs/Glossary/URL_encoding](https://developer.mozilla.org/ja/docs/Glossary/URL_encoding)
*   URI scheme - Wikipedia: [https://ja.wikipedia.org/wiki/URI%E3%82%B9%E3%82%AD%E3%83%BC%E3%83%A0](https://ja.wikipedia.org/wiki/URI%E3%82%B9%E3%82%AD%E3%83%BC%E3%83%A0)
*   パーセントエンコーディング - Wikipedia: [https://ja.wikipedia.org/wiki/%E3%83%91%E3%83%BC%E3%82%BB%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0](https://ja.wikipedia.org/wiki/%E3%83%91%E3%83%BC%E3%82%BB%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0)