ハトネコエ Web がくしゅうちょう

プログラミングやサーバー・Web制作、チームマネジメントなど得た技術のまとめ

セッションと Cookie について今さら理解する

Webサイトのログインで必要な考えとなる
セッション周りを全然わかっていなかったので、いろいろ自分用にメモ。

1. 動機

今まで Rails や周辺ライブラリがよしなにやってくれていたものを、
Go言語で Web アプリを作ろうとしたら自分でちゃんと実装しないとだったので、
ログイン機構(特に devise の current_user メソッド)を作るためにセッションを理解する。

2. 役立つ記事

この 3 つを読んでおけば、セッションと、そのための Cookie について理解が整う。

3. Tips

3-1. net/http が SetCookie メソッドを持つ

Go言語の場合は net/http パッケージが SetCookie 関数を持つのでそれを使うと楽。

なお第一引数には、HTTP Response を組み立てる ResponseWriter 構造体を用いる。
Cookie をクライアント(Webブラウザ)に Set するのは、 HTTP Response の Header を使っておこなうため。

3-2. Value に入れる値は URL Encode されたもの?

今使っている Web フレームワーク gin のここのコードで、

Value: url.QueryEscape(value),

となっていたため、 URL Encode が必要なのか確認してみました。
stackoverflow のこの回答では、「そうだよ。すべきだ」って回答が一番上に来てますが、他の回答も見ると議論の余地があることがわかります。

There is some confusion over encoding of a cookie value. The commonly held belief is that cookie values must be URL-encoded, but this is a fallacy even though it is the de facto implementation. The original specification indicates that only three types of characters must be encoded: semicolon, comma, and white space. The specification indicates that URL encoding may be used but stops short of requiring it. The RFC makes no mention of encoding whatsoever. Still, almost all implementations perform some sort of URL encoding on cookie values. In the case of name=value formats, the name and value are typically encoded separately while the equals sign is left as is.

(『HTTP cookies explained』の『Cookie encoding』の項より引用)

日本語に訳すと、『CookieValueエンコードについてはいくらかの混乱が起こっています。一般的に Value は URL-encoded が必要と信じられていますが、実際の実装でそうなっている物はあれどこれは誤解です。元の仕様では、セミコロン、カンマ、および空白の3種類の文字のみをエンコードする必要があることが示されています。これは URL Encode を使用できることは示しますが、要求しているわけではありません。RFCでもエンコーディングについて一切言及していません。(以下略)』

そして他の回答では

RFC6265:           "for example Base64"
PHP:               URL encode
Go:                raw
Node.js + Express: URL encode

と、各言語によって実装が異なることを示してくれていました。

PHP を例に取ると、PHP の setcookie 関数は、
Value として与えられた引数を URL Encode して実際は保存します。
PHP 5 以降で実装された setrawcookie 関数を使うと、URL Encode をおこなわず保存します。
(参考: https://www.php.net/manual/ja/function.setcookie.php

RFC6265 内では

To maximize compatibility with user agents, servers that wish to store arbitrary data in a cookie-value SHOULD encode that data, for example, using Base64 [RFC4648].

と書かれてはいますが、たしかにエンコードすべきと言っているまでで、Base64 は例示してるだけですね。
(なんでユーザーエージェント文字列との互換性を持ちたいのかはわからない……)

3-3. SameSite の設定はデフォルトでいいかも

Go言語 v1.11 以降は SameSite ってパラメーターを Cookie に設定できるんですけど、
「厳しいほうがいいだろう」と思って SameSite: http.SameSiteStrictMode に設定したら、OAuth でコールバックを受け取ってからの Cookie 読み込みができない罠にハマりそうだったので、
SameSite: http.SameSiteDefaultMode でとりあえずは良さそうです。

実装がどう違うかはいつか読みます……。

4. おわりに

忘れないように書いておきたかった内容なのでゴチャゴチャしていますが、
セッションへの理解がだいぶ進んで、まともな Web プログラマーに近付けた気がします。

作ってる練習用Webアプリのセッション管理部分はこんな感じ ↓ になりましたー。

https://github.com/nekonenene/gin_quiz_app/tree/master/session