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

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

このコミットは、Go言語の標準ライブラリの一部として将来的に導入される exp/cookiejar パッケージの初期インフラストラクチャを構築するものです。具体的には、HTTPクッキーを管理するための基盤となるデータ構造とヘルパー関数、そして http.CookieJar インターフェースの実装の骨格が追加されています。

コミット

commit 8c6489bc27b639487f56392d13c43f95fab0ac20
Author: Volker Dobler <dr.volker.dobler@gmail.com>
Date:   Wed Feb 6 22:37:34 2013 +1100

    exp/cookiejar: infrastructure for upcoming implementation
    
    This CL is the first of a handful of CLs which will provide
    the implementation of cookiejar. It contains several helper
    functions and the skeleton of Cookies and SetCookies.
    
    Proper host name handling requires the ToASCII transformation
    from package idna which currently lives in the go.net
    subrepo. This CL thus contains just a TODO for this issue.
    
    R=nigeltao, rsc, bradfitz
    CC=golang-dev
    https://golang.org/cl/7287046

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

https://github.com/golang/go/commit/8c6489bc27b639487f56392d13c43f95fab0ac20

元コミット内容

このコミットは、exp/cookiejar パッケージの今後の実装のためのインフラストラクチャを提供します。これは、cookiejar の実装を提供する一連の変更リスト(CLs)の最初のものです。いくつかのヘルパー関数と、Cookies および SetCookies メソッドの骨格が含まれています。

適切なホスト名処理には、現在 go.net サブリポジトリにある idna パッケージからの ToASCII 変換が必要です。この変更リストには、この問題に対する TODO コメントのみが含まれています。

変更の背景

Webアプリケーションにおいて、HTTPクッキーはセッション管理、ユーザー認証、パーソナライゼーションなど、多岐にわたる機能を実現するために不可欠な要素です。Go言語の net/http パッケージには http.CookieJar インターフェースが定義されていますが、その具体的な実装は提供されていませんでした。開発者は独自のクッキー管理ロジックを実装するか、サードパーティのライブラリを使用する必要がありました。

このコミットは、Go標準ライブラリに堅牢で標準に準拠したクッキー管理機能を提供するための第一歩です。特に、RFC 6265(HTTP State Management Mechanism)に厳密に従った実装を目指しており、クッキーの保存、取得、有効期限、ドメイン、パスなどの複雑なルールを適切に処理できる基盤を構築することが目的です。

また、国際化ドメイン名(IDN)のサポートも重要な考慮事項です。IDNは、ASCII文字以外の文字(日本語、中国語、アラビア語など)を含むドメイン名を使用可能にするもので、これらを適切に処理するためには、Punycodeエンコーディング(ToASCII変換)が必要となります。このコミットでは、そのための TODO が残されており、今後の実装課題として認識されています。

前提知識の解説

HTTPクッキーは、WebサーバーがユーザーのWebブラウザに送信する小さなデータ片です。ブラウザはこれらのクッキーを保存し、同じサーバーへの後続のリクエストでそれらを送り返します。これにより、サーバーはユーザーの状態を「記憶」することができます。クッキーには、名前、値、ドッキーが有効なドメイン、パス、有効期限、Secureフラグ(HTTPS接続のみで送信)、HttpOnlyフラグ(JavaScriptからのアクセスを禁止)などの属性が含まれます。

http.CookieJar インターフェース

Go言語の net/http パッケージで定義されているインターフェースで、HTTPクライアントがクッキーを自動的に管理するために使用されます。このインターフェースは以下の2つのメソッドを定義しています。

  • Cookies(u *url.URL) []*Cookie: 指定されたURLに送信すべきクッキーのリストを返します。
  • SetCookies(u *url.URL, cookies []*Cookie): 指定されたURLから受信したクッキーのリストを保存します。

RFC 6265 (HTTP State Management Mechanism)

HTTPクッキーの動作を定義する標準仕様です。クッキーの構文、セマンティクス、セキュリティに関する詳細なルールが記述されており、ブラウザやサーバーがクッキーをどのように処理すべきかを定めています。このコミットで実装される cookiejar は、このRFCに準拠することを目指しています。

Public Suffix List (PSL)

パブリックサフィックスリストは、ドメイン名の「パブリックサフィックス」(例: .com, .co.uk, .jp など)を列挙したリストです。これは、セキュリティとプライバシーの目的で非常に重要です。例えば、example.comsub.example.com は同じ組織が管理するドメインですが、example.comexample.co.uk は異なる組織が管理する可能性があります。PSLを使用することで、クッキーが意図しないドメインに設定されたり、悪意のあるサブドメインが他のサブドメインのクッキーを盗んだりするのを防ぐことができます。PublicSuffixList インターフェースは、与えられたドメインのパブリックサフィックスを返す機能を提供します。

国際化ドメイン名 (IDN) と Punycode (ToASCII)

国際化ドメイン名(IDN)は、ASCII文字セットに含まれない文字(例: bücher.de)を含むドメイン名です。DNSシステムはASCII文字しか扱えないため、IDNは「Punycode」と呼ばれるエンコーディング方式を使用してASCII互換形式に変換されます。この変換プロセスは「ToASCII」変換と呼ばれます。例えば、bücher.dexn--bcher-kva.de のように変換されます。クッキーのドメイン属性を適切に処理するためには、このToASCII変換が不可欠です。コミットメッセージで言及されている idna パッケージは、この変換機能を提供します。

技術的詳細

このコミットは、exp/cookiejar パッケージの jar.gojar_test.go ファイルに大幅な変更を加えています。

jar.go の変更点

  1. インポートの追加:

    • net: ホスト名とポートの解析に使用されます。
    • strings: 文字列操作(小文字化、接尾辞チェックなど)に使用されます。
    • sync: Jar 構造体のミューテックス (sync.Mutex) を使用して、並行アクセス時のデータ競合を防ぎます。
    • time: クッキーの有効期限や作成時刻、最終アクセス時刻を管理するために使用されます。
  2. Jar 構造体の拡張:

    • mu sync.Mutex: Jar の内部状態(特に entries マップ)へのアクセスを保護するためのミューテックスが追加されました。これにより、複数のゴルーチンからの同時アクセスに対してスレッドセーフになります。
    • entries map[string]map[string]entry: クッキーを保存するための主要なデータ構造です。
      • 外側のマップのキーは「eTLD+1」(effective Top-Level Domain + 1、つまりパブリックサフィックスの1つ上のドメイン)です。これにより、異なるドメインのクッキーが分離されます。
      • 内側のマップのキーは、クッキーの名前、ドメイン、パスを組み合わせたもの(name/domain/path)とコメントに記載されており、クッキーを一意に識別するために使用されます。
      • 値は entry 構造体です。
  3. entry 構造体の追加:

    • これは、RFC 6265 に定義されているクッキーの属性を内部的に表現するための構造体です。
    • Name, Value, Domain, Path, Secure, HttpOnly, Persistent, HostOnly: これらはHTTPクッキーの標準的な属性に対応します。
    • Expires, Creation, LastAccess: クッキーの有効期限、作成時刻、最終アクセス時刻を time.Time 型で保持します。これにより、クッキーのライフサイクル管理が可能になります。
  4. New 関数の実装:

    • New 関数は、新しい Jar インスタンスを初期化して返します。
    • jar.entries マップが make で初期化されます。
    • Options が指定されている場合、PublicSuffixList が設定されます。
  5. Cookies メソッドの骨格実装:

    • http.CookieJar インターフェースの Cookies メソッドの初期実装です。
    • URLスキームが http または https でない場合は空のスライスを返します。
    • canonicalHost を使用してホスト名を正規化します。
    • jarKey を使用して、entries マップから適切なサブマップ (submap) を取得するためのキーを生成します。
    • ミューテックス (j.mu) を使用して、entries マップへのアクセスを保護します。
    • TODO コメントとして、期限切れクッキーの処理とクッキーの選択ロジックが残されています。
    • TODO コメントとして、パスの長さと作成時刻に基づく適切なソートが残されています。
  6. SetCookies メソッドの骨格実装:

    • http.CookieJar インターフェースの SetCookies メソッドの初期実装です。
    • クッキーが空の場合、またはURLスキームが http または https でない場合は何もしません。
    • canonicalHost を使用してホスト名を正規化します。
    • jarKey を使用して、entries マップから適切なサブマップを取得するためのキーを生成します。
    • ミューテックス (j.mu) を使用して、entries マップへのアクセスを保護します。
    • TODO コメントとして、submap 内のエントリの作成、更新、削除ロジックが残されています。
  7. ヘルパー関数の追加:

    • canonicalHost(host string) (string, error):

      • RFC 6265 のセクション 5.1.2 で定義されている正規化されたホスト名を返します。
      • ポート番号があれば削除し、ホスト名を小文字に変換します。
      • 末尾のドット(例: www.example.com.)を削除します。
      • 重要な TODO: RFC 6265 の「正規化されたホスト名」は idna パッケージの ToASCII 変換を必要としますが、このコミットではまだ実装されていません。これは、国際化ドメイン名(IDN)の適切な処理に影響します。
    • hasPort(host string) bool:

      • 与えられたホスト文字列にポート番号が含まれているかどうかを判定します。
      • IPv6アドレスの形式([::1]:8080)も考慮に入れています。
    • jarKey(host string, psl PublicSuffixList) string:

      • Jarentries マップで使用するキーを生成します。
      • ホストがIPアドレスの場合は、IPアドレス自体をキーとします。
      • PublicSuffixList が提供されている場合、eTLD+1(effective Top-Level Domain + 1)をキーとして使用します。これは、www.example.comsub.example.com の両方が example.com をキーとして共有するようにします。
      • PublicSuffixListnil の場合、ホストのトップレベルドメイン(TLD)をキーとして使用します。
      • PublicSuffixList が壊れている(例: PublicSuffix がホスト自体を返す)場合の安全策も含まれています。
    • isIP(host string) bool:

      • 与えられたホスト文字列がIPアドレスであるかどうかを判定します。net.ParseIP を使用します。

jar_test.go の変更点

  • 新規ファイルの追加: jar_test.go が新しく作成され、jar.go で追加されたヘルパー関数の単体テストが含まれています。
  • testPSL 構造体:
    • PublicSuffixList インターフェースのテスト実装です。
    • co.uk とデフォルトの * ルールのみを持つ簡略化されたPSLを提供します。
  • canonicalHostTests: canonicalHost 関数のテストケースを定義しています。様々な形式のホスト名(大文字小文字、ポート付き、末尾ドット付き、IPアドレス)が正規化されることを確認します。
  • TestCanonicalHost: canonicalHostTests を実行し、結果を検証します。TODO としてエラーハンドリングの追加が示唆されています。
  • hasPortTests: hasPort 関数のテストケースを定義しています。ポートの有無を正確に判定できることを確認します。
  • TestHasPort: hasPortTests を実行し、結果を検証します。
  • jarKeyTests: jarKey 関数のテストケースを定義しています。testPSL を使用した場合と nil の場合の両方で、正しいジャキーが生成されることを確認します。
  • TestJarKey: jarKeyTests を実行し、結果を検証します。
  • isIPTests: isIP 関数のテストケースを定義しています。IPアドレスとそうでない文字列を正確に識別できることを確認します。
  • TestIsIP: isIPTests を実行し、結果を検証します。

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

src/pkg/exp/cookiejar/jar.go

  • Jar 構造体に mu sync.Mutexentries map[string]map[string]entry が追加。
  • entry 構造体が新規定義。
  • New 関数が Jar の初期化ロジックを持つように変更。
  • Cookies メソッドと SetCookies メソッドが骨格実装を持つように変更。
  • canonicalHost, hasPort, jarKey, isIP の各ヘルパー関数が新規追加。

src/pkg/exp/cookiejar/jar_test.go

  • 新規ファイルとして追加。
  • testPSL 構造体と、canonicalHost, hasPort, jarKey, isIP の各ヘルパー関数のテストケースが定義され、テスト関数が実装されている。

コアとなるコードの解説

このコミットの核心は、Jar 構造体とその内部の entries マップ、そしてクッキーの内部表現である entry 構造体の導入です。

type Jar struct {
	psList PublicSuffixList

	// mu locks the remaining fields.
	mu sync.Mutex

	// entries is a set of entries, keyed by their eTLD+1 and subkeyed by
	// their name/domain/path.
	entries map[string]map[string]entry
}

type entry struct {
	Name       string
	Value      string
	Domain     string
	Path       string
	Secure     bool
	HttpOnly   bool
	Persistent bool
	HostOnly   bool
	Expires    time.Time
	Creation   time.Time
	LastAccess time.Time
}

Jar 構造体は、クッキーを保存するための中心的なコンテナです。sync.Mutex を持つことで、複数のゴルーチンから安全にアクセスできるようになっています。entries マップは、クッキーを効率的に検索・管理するための階層的な構造を提供します。外側のキーは「eTLD+1」であり、これにより異なるドメインのクッキーが論理的に分離されます。内側のマップは、クッキーの名前、ドメイン、パスの組み合わせで一意に識別される entry 構造体を保持します。

entry 構造体は、RFC 6265 に定義されているクッキーのすべての重要な属性をカプセル化します。これにより、クッキーのライフサイクル(有効期限、作成、最終アクセス)やセキュリティ属性(Secure, HttpOnly)を正確に追跡・管理できます。

ヘルパー関数群は、クッキー管理の複雑な側面を抽象化し、主要な Cookies および SetCookies メソッドの実装を簡素化します。

func canonicalHost(host string) (string, error) {
	var err error
	host = strings.ToLower(host)
	if hasPort(host) {
		host, _, err = net.SplitHostPort(host)
		if err != nil {
			return "", err
		}
	}

	if strings.HasSuffix(host, ".") {
		// Strip trailing dot from fully qualified domain names.
		host = host[:len(host)-1]
	}

	// TODO: the "canonicalized host name" of RFC 6265 requires the idna ToASCII
	// transformation. Possible solutions:
	//  - promote package idna from go.net to go and import "net/idna"
	//  - document behavior as a BUG

	return host, nil
}

canonicalHost 関数は、クッキーのドメイン属性の比較において非常に重要です。RFC 6265 に従ってホスト名を正規化することで、クッキーが正しいドメインに適用されることを保証します。特に、ポート番号の除去や小文字化、末尾ドットの除去は、クッキーのドメインマッチングルールに不可欠です。しかし、idna パッケージの ToASCII 変換に関する TODO は、国際化ドメイン名(IDN)のサポートがまだ不完全であることを示しており、今後の課題として残されています。

func jarKey(host string, psl PublicSuffixList) string {
	if isIP(host) {
		return host
	}
	if psl == nil {
		// Key cookies under TLD of host.
		return host[1+strings.LastIndex(host, "."):]
	}
	suffix := psl.PublicSuffix(host)
	if suffix == host {
		return host
	}
	i := len(host) - len(suffix)
	if i <= 0 || host[i-1] != '.' {
		// The provided public suffix list psl is broken.
		// Storing cookies under host is a safe stopgap.
		return host
	}
	prevDot := strings.LastIndex(host[:i-1], ".")
	return host[prevDot+1:]
}

jarKey 関数は、Jar の内部マップのキーを生成するロジックをカプセル化します。PublicSuffixList の有無によって異なる戦略を採用しており、PSLが利用可能な場合はeTLD+1をキーとすることで、セキュリティとプライバシーの観点から適切なクッキーの分離を実現しています。これにより、example.com のクッキーが sub.example.com にも適用されるが、another.com には適用されない、といったRFCのルールを遵守するための基盤が作られています。

これらの変更は、exp/cookiejar パッケージが将来的にGo標準ライブラリに統合されるための、堅牢で拡張性のある基盤を築くものです。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • RFC 6265
  • Public Suffix List の概念に関する一般的な情報源
  • 国際化ドメイン名(IDN)と Punycode に関する一般的な情報源
  • GitHub上のGo言語リポジトリのコミット履歴と関連する議論
  • Go言語の net/http/cookiejar パッケージの現在の実装 (このコミットの後の進化形)