[インデックス 19197] ファイルの概要
このコミットは、Go言語の標準ライブラリであるnet/http
パッケージにおけるCookieの取り扱いを改善するものです。具体的には、RFC 6265で本来許可されていないCookieの値に含まれるカンマとスペースを許容するように変更されました。これにより、現実世界のブラウザの挙動や、RFCに厳密には準拠していないが広く利用されているCookieとの相互運用性が向上します。値がカンマやスペースで始まる、または終わる場合には、誤解釈を防ぐために値が引用符で囲まれて送信されるようになります。
コミット
commit ed88076c6437fa87c26a568b46020eacc9202e13
Author: Volker Dobler <dr.volker.dobler@gmail.com>
Date: Wed Apr 16 23:01:02 2014 -0700
net/http: allow commas and spaces in cookie values
According to RFC 6265 a cookie value may contain neither
commas nor spaces but such values are very common in the
wild and browsers handle them very well so we'll allow
both commas and spaces.
Values starting or ending in a comma or a space are
sent in the quoted form to prevent missinterpetations.
RFC 6265 conforming values are handled as before and
semicolons, backslashes and double-quotes are still
disallowed.
Fixes #7243
LGTM=nigeltao
R=nigeltao
CC=bradfitz, golang-codereviews
https://golang.org/cl/86050045
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ed88076c6437fa87c26a568b46020eacc9202e13
元コミット内容
net/http: allow commas and spaces in cookie values
According to RFC 6265 a cookie value may contain neither
commas nor spaces but such values are very common in the
wild and browsers handle them very well so we'll allow
both commas and spaces.
Values starting or ending in a comma or a space are
sent in the quoted form to prevent missinterpetations.
RFC 6265 conforming values are handled as before and
semicolons, backslashes and double-quotes are still
disallowed.
Fixes #7243
LGTM=nigeltao
R=nigeltao
CC=bradfitz, golang-codereviews
https://golang.org/cl/86050045
変更の背景
HTTP Cookieの仕様はRFC 6265によって厳密に定義されており、Cookieの値(cookie-value
)にはカンマ(,
)やスペース(
)を含めることが許可されていません。しかし、現実世界のWebアプリケーションやブラウザの実装においては、これらの文字がCookie値に含まれるケースが非常に多く見られます。多くのブラウザは、RFCの規定に反していても、これらの文字を含むCookie値を問題なく処理します。
Go言語のnet/http
パッケージは、これまでの実装ではRFC 6265の規定に厳密に従っていたため、カンマやスペースを含むCookie値を不正なものとして扱っていました。この厳格な挙動は、RFCに準拠していないが広く普及しているCookieを使用するシステムとの間で相互運用性の問題を引き起こしていました。例えば、Goで書かれたサーバーが、カンマやスペースを含むCookieを送信するクライアントからのリクエストを正しく処理できない、あるいはGoサーバーが生成したCookieが他のシステムで正しく解釈されないといった問題が発生していました。
このコミットは、このような現実世界との乖離を解消し、Goのnet/http
パッケージがより堅牢で実用的なHTTPクライアントおよびサーバーとして機能するようにするために行われました。RFCの規定を緩和し、カンマとスペースをCookie値に許容することで、既存のWebシステムとの互換性を高めることが目的です。ただし、セミコロン(;
)、バックスラッシュ(\
)、ダブルクォート("
)といった、より構造的な意味を持つ文字は引き続き禁止されます。
コミットメッセージには Fixes #7243
と記載されていますが、Goの公式GitHubリポジトリでは直接この番号のIssueを見つけることはできませんでした。これは、Issueが非常に古いものであるか、別のトラッカーで管理されていた可能性を示唆しています。しかし、このコミットの目的は、現実のWeb環境におけるCookieの多様な利用実態に対応することにあると理解できます。
前提知識の解説
HTTP Cookie
HTTP Cookieは、WebサーバーがユーザーのWebブラウザに送信する小さなデータ片です。ブラウザはこれを受け取り、通常は保存し、同じサーバーへの後続のリクエストとともに送り返します。Cookieは主に以下の目的で使用されます。
- セッション管理: ユーザーのログイン状態、ショッピングカートの内容など。
- パーソナライゼーション: ユーザー設定、テーマなど。
- トラッキング: ユーザーの行動追跡、広告表示など。
Cookieは通常、「名前-値」のペアと、有効期限、パス、ドメイン、Secure、HttpOnlyなどの属性で構成されます。
RFC 6265 (HTTP State Management Mechanism)
RFC 6265は、HTTP Cookieの動作を定義するIETF(Internet Engineering Task Force)の標準仕様です。このRFCは、Cookieの構文、セマンティクス、セキュリティに関する詳細を規定しています。
特に、Cookieの値(cookie-value
)の構文については厳密な定義があります。RFC 6265のセクション4.1.1では、cookie-octet
という概念が導入されており、これはCookie値に含めることができる文字の集合を定義しています。この定義によると、Cookie値はUS-ASCII文字のうち、制御文字(CTLs)、空白文字(whitespace)、ダブルクォート("
)、カンマ(,
)、セミコロン(;
)、バックスラッシュ(\
)を除くものとされています。
つまり、RFC 6265に厳密に従う場合、Cookie値にカンマやスペースを含めることはできません。
ブラウザの実装と現実世界
RFC 6265は理想的な仕様を記述していますが、実際のWebブラウザの実装は、歴史的な経緯や相互運用性の必要性から、RFCの規定を完全に厳守しない場合があります。特に、カンマやスペースを含むCookie値は、多くのブラウザで問題なく処理されることが知られています。これは、Webの進化の過程で、RFCが策定される以前から存在していた非標準的なCookieの利用方法に対応するためと考えられます。
このような現実世界の挙動とRFCの間の乖離は、「堅牢性の原則(Postel's Law)」の一例と見なすことができます。これは、「自分が送るものは厳密に、他人が受け取るものは寛容に」という考え方です。ブラウザは、サーバーから送られてくる非標準的なCookieに対しても寛容に振る舞うことで、Webの互換性を維持しています。
しかし、サーバー側の実装がRFCに厳密すぎると、ブラウザが送信する非標準的なCookieを拒否したり、誤って解釈したりする問題が発生します。このコミットは、Goのnet/http
パッケージが、この「堅牢性の原則」をより適切に適用し、現実世界のWeb環境との互換性を高めることを目指しています。
技術的詳細
このコミットの主要な変更点は、net/http
パッケージ内のCookieの値を処理するロジック、特にsanitizeCookieValue
関数とvalidCookieValueByte
関数の挙動の変更、およびparseCookieValue
関数の簡素化です。
-
validCookieValueByte
関数の変更:- この関数は、Cookie値として有効なバイトを判定します。
- 変更前は、
0x20 < b && b < 0x7f && b != '"' && b != ',' && b != ';' && b != '\\'
という条件で、スペース(0x20
)とカンマ(,
)を無効な文字としていました。 - 変更後は、
0x20 <= b && b < 0x7f && b != '"' && b != ';' && b != '\\'
となり、スペース(0x20
)とカンマ(,
)が有効な文字として含まれるようになりました。これにより、これらの文字がCookie値に直接含まれることが許可されます。 - ただし、ダブルクォート(
"
)、セミコロン(;
)、バックスラッシュ(\
)は引き続き無効な文字として扱われます。これらはCookieの構文解析において特別な意味を持つため、値に含めることはできません。
-
sanitizeCookieValue
関数の変更:- この関数は、Cookieの値をサニタイズ(無効な文字を除去または変換)し、必要に応じて引用符で囲む役割を担います。
- 変更前は、単に
sanitizeOrWarn
関数を呼び出して、validCookieValueByte
で無効とされた文字を除去していました。 - 変更後は、まず
sanitizeOrWarn
で基本的なサニタイズを行った後、追加のロジックが導入されました。- 値が空文字列の場合はそのまま返します。
- 値の先頭または末尾がスペース(
,
)である場合、その値全体をダブルクォート("
)で囲みます。これは、ブラウザがこのような値を正しく解釈できるようにするための措置です。例えば、Value: " a"
はValue: "\" a\""
として送信されます。 - 上記以外のRFC 6265に準拠した値(カンマやスペースで始まったり終わったりしない値)は、これまで通り引用符なしで送信されます。
-
parseCookieValue
関数の簡素化と関連関数の削除:- 変更前は、
parseCookieValue
、parseCookieExpiresValue
、parseCookieValueUsing
、unquoteCookieValue
、isCookieByte
、isCookieExpiresByte
といった複数の関数が存在し、Cookie値のパースと検証のロジックが分散していました。特にexpires
属性のCookie値は、日付形式の特殊性からカンマやスペースを許容するisCookieExpiresByte
を使用するなど、異なるパースロジックを持っていました。 - このコミットにより、
parseCookieExpiresValue
、parseCookieValueUsing
、unquoteCookieValue
、isCookieByte
、isCookieExpiresByte
といった関数が削除され、parseCookieValue
関数にロジックが集約されました。 - 新しい
parseCookieValue
関数は、まず値がダブルクォートで囲まれていればその引用符を剥がします。その後、validCookieValueByte
関数を使用して、値の各バイトが有効であるかを検証します。これにより、パースロジックが大幅に簡素化され、一貫性が保たれるようになりました。expires
属性のCookie値も、他のCookie値と同様にparseCookieValue
で処理されるようになります。
- 変更前は、
これらの変更により、Goのnet/http
パッケージは、RFC 6265の厳密な解釈から一歩踏み出し、現実世界のWeb環境におけるCookieの多様な利用実態に柔軟に対応できるようになりました。
コアとなるコードの変更箇所
このコミットによる主要なコード変更は、以下のファイルに集中しています。
src/pkg/net/http/cookie.go
: Cookieの処理ロジックが実装されている主要なファイル。readSetCookies
関数内のparseCookieValueFn
の呼び出しがparseCookieValue
に直接変更され、expires
属性に対する特別なパースロジックが削除されました。sanitizeCookieValue
関数の実装が大幅に変更され、カンマとスペースの許容と、条件付きの引用符追加ロジックが導入されました。validCookieValueByte
関数の条件が変更され、カンマとスペースが有効な文字として追加されました。unquoteCookieValue
,isCookieByte
,isCookieExpiresByte
,parseCookieValueUsing
,parseCookieExpiresValue
といった複数の補助関数が削除されました。parseCookieValue
関数が簡素化され、引用符の剥がしとvalidCookieValueByte
による検証が一元化されました。
src/pkg/net/http/cookie_test.go
: Cookie処理のテストケースが記述されているファイル。writeSetCookiesTests
とreadSetCookiesTests
に、カンマやスペースを含むCookie値、および引用符で囲まれるべきCookie値(先頭または末尾にカンマやスペースがある場合)のテストケースが多数追加されました。TestCookieSanitizeValue
にも、新しいsanitizeCookieValue
の挙動を検証するテストケースが追加されました。
コアとなるコードの解説
src/pkg/net/http/cookie.go
func sanitizeCookieValue(v string) string
の変更
// 変更前:
// func sanitizeCookieValue(v string) string {
// return sanitizeOrWarn("Cookie.Value", validCookieValueByte, v)
// }
// 変更後:
func sanitizeCookieValue(v string) string {
v = sanitizeOrWarn("Cookie.Value", validCookieValueByte, v) // まず基本的なサニタイズ
if len(v) == 0 {
return v
}
// 値の先頭または末尾がスペースまたはカンマの場合、引用符で囲む
if v[0] == ' ' || v[0] == ',' || v[len(v)-1] == ' ' || v[len(v)-1] == ',' {
return `"` + v + `"`
}
return v
}
この関数は、Set-Cookie
ヘッダーでクライアントに送信されるCookieの値を整形します。変更の核心は、RFC 6265では許可されていないカンマやスペースを値に含めることを許容しつつ、それらの文字が値の先頭や末尾にある場合に、ブラウザが正しく解釈できるようにダブルクォートで値を囲むロジックが追加された点です。これにより、GoのHTTPサーバーがより柔軟なCookieを生成できるようになります。
func validCookieValueByte(b byte) bool
の変更
// 変更前:
// func validCookieValueByte(b byte) bool {
// return 0x20 < b && b < 0x7f && b != '"' && b != ',' && b != ';' && b != '\\'
// }
// 変更後:
func validCookieValueByte(b byte) bool {
return 0x20 <= b && b < 0x7f && b != '"' && b != ';' && b != '\\'
}
この関数は、Cookie値の各バイトが有効な文字であるかをチェックします。変更前はスペース(0x20
)とカンマ(,
)を無効としていましたが、変更後はこれらを有効な文字として扱うようになりました。これにより、sanitizeCookieValue
関数がこれらの文字を削除することなく、値に含めることができるようになります。ただし、ダブルクォート、セミコロン、バックスラッシュは引き続き無効です。
func parseCookieValue(raw string) (string, bool)
の変更と関連関数の削除
// 変更前は、unquoteCookieValue, isCookieByte, isCookieExpiresByte, parseCookieValueUsing など複数の関数に分散していた。
// parseCookieValue は parseCookieValueUsing(raw, isCookieByte) を呼び出していた。
// 変更後:
func parseCookieValue(raw string) (string, bool) {
// 引用符があれば剥がす
if len(raw) > 1 && raw[0] == '"' && raw[len(raw)-1] == '"' {
raw = raw[1 : len(raw)-1]
}
// 各バイトが有効かチェック
for i := 0; i < len(raw); i++ {
if !validCookieValueByte(raw[i]) {
return "", false
}
}
return raw, true
}
この関数は、クライアントから受信したCookieの値をパースします。変更前は、expires
属性のCookie値に対して特別なパースロジック(カンマやスペースを許容する)が存在していましたが、このコミットによりその特殊性がなくなり、すべてのCookie値が同じparseCookieValue
関数で処理されるようになりました。この関数は、まず値が引用符で囲まれていればそれを剥がし、その後、更新されたvalidCookieValueByte
関数を使用して、値の各バイトが有効であるかを検証します。これにより、Cookie値のパースロジックが簡素化され、一貫性が向上しました。
src/pkg/net/http/cookie_test.go
テストファイルには、新しい挙動を検証するための多数のテストケースが追加されています。特に注目すべきは、writeSetCookiesTests
とreadSetCookiesTests
にspecial-
プレフィックスの付いたテストケースが追加されたことです。これらは、カンマやスペースを含むCookie値が正しく送信され、また正しく受信できることを確認します。
例:
// writeSetCookiesTests に追加されたテストケース
{
&Cookie{Name: "special-1", Value: "a z"},
`special-1=a z`, // スペースを含むが、先頭/末尾ではないので引用符なし
},
{
&Cookie{Name: "special-2", Value: " z"},
`special-2=" z"`, // 先頭にスペースがあるので引用符あり
},
{
&Cookie{Name: "special-5", Value: "a,z"},
`special-5=a,z`, // カンマを含むが、先頭/末尾ではないので引用符なし
},
{
&Cookie{Name: "special-6", Value: ",z"},
`special-6=",z"`, // 先頭にカンマがあるので引用符あり
},
// readSetCookiesTests に追加されたテストケース
{
Header{"Set-Cookie": {`special-1=a z`}},
[]*Cookie{{Name: "special-1", Value: "a z", Raw: `special-1=a z`}},
},
{
Header{"Set-Cookie": {`special-2=" z"`}},
[]*Cookie{{Name: "special-2", Value: " z", Raw: `special-2=" z"`}},
},
これらのテストケースは、sanitizeCookieValue
が期待通りに引用符を追加したりしなかったりすること、そしてparseCookieValue
がそれらの値を正しくパースできることを保証します。
関連リンク
- Go CL 86050045: https://golang.org/cl/86050045
- Fixes #7243: このコミットが修正したIssue番号。Goの公式GitHubリポジトリでは直接見つかりませんでしたが、この変更の背景にある問題を示しています。
参考にした情報源リンク
- RFC 6265 - HTTP State Management Mechanism: https://www.ietf.org/rfc/rfc6265.txt
- Stack Overflow: What characters are allowed in a cookie value? https://stackoverflow.com/questions/1969232/what-characters-are-allowed-in-a-cookie-value
- Stack Overflow: Are commas allowed in cookie values? https://stackoverflow.com/questions/1969232/are-commas-allowed-in-cookie-values
- Stack Overflow: What characters are allowed in a cookie value? (another link) https://stackoverflow.com/questions/1969232/what-characters-are-allowed-in-a-cookie-value
- Stack Overflow: Are spaces allowed in cookie values? https://stackoverflow.com/questions/1969232/are-spaces-allowed-in-cookie-values
- IETF RFC 6265 Section 4.1.1: Syntax of the Set-Cookie Header Field https://www.ietf.org/rfc/rfc6265.txt
- HTTP Working Group: RFC 6265 Errata https://httpwg.org/specs/rfc6265.html