Cookie周りの仕様および図解

# 目次

1. Cookieの必要性
2. Cookieの送受信
3. Cookieの仕様
4. 3rd Party製 Cookie
5. CSRF攻撃

# 1. Cookie（というか状態管理）の必要性
## 初期のWEB
初期のWEBでは、HTMLファイルを作って、そこに論文を書いて共有するために存在していました。
なので、ブラウザから受けたリクエストに基づいて、ファイルを返すだけ。
ブラウザも同じように、取得したファイルから画面を描画するだけ。
目的自体は論文の共有だったので、これで十分だったわけです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87538/1e92e3c2-408c-9760-eb6f-40b1bd950536.png)

## WEBサイト、ECサイトの要求
時代が移り変わり情報共有を超えたWEBへ進化しました。
ファイルを返すだけのサーバから、情報・状態を管理するサーバへ変化しました。

例えば、今アクセスしてきた者は【誰】なのか？
誰がアクセスしてきていて、どういった【状態】にあるのか？
などを意識する必要が出てきました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87538/8abbbdda-1d1c-e698-b4ad-0adf4ce0353b.png)

## Cookieを用いた状態管理
その要求に応えたのが、Netscape Navigatorブラウザであり、それが正式に[RFC2109](https://tools.ietf.org/html/rfc2109)で一般化されました。
その中身が`Cookie`, `Set-Cookie`による状態管理メカニズムでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87538/072895af-86cb-7f4c-3b27-5a5d2c5abb24.png)

上記のように、接続元に対して、一意なキー[^1]を振ってユーザーを管理しようというものです。
ここでは、【name】という属性を管理していますが、これが【買い物かご】でも同じ方式で管理します。
これを【セッション】や【ユーザーセッション】などと呼びます。
[^1]: UUIDなど推測されないものをベースにIDを生成すべき。ここでは簡便的に1,2,3と記載しています。


# 2. Cookieの送受信
## Cookieの送受信のアーキテクチャ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87538/915bf33b-81ec-5f69-5f09-88d1988ad207.png)


上図の通り、サーバから`Set-Cookie: key=value; option群`を返します。[^2]
ブラウザは`Set-Cookie`を受け取り、受信時にブラウザに`www.example.org`の`Cookie`として、保存します。
次回以降の`www.example.org`へのリクエスト時に、`Cookie`をHTTP Headerに乗せて送信します。
[^2]: `Set-Cookie`は複数指定可能。


# 3. Cookieの仕様
## Cookieの属性
| 名称 | 内容 |
|:-:|:--|
| Path| パス。デフォルトは、現在の文書の位置。 |
| Domain | 送信先。<br>指定しない場合、返送してきたサーバドメインへのみ送信。<br>指定した場合、返送してきたサーバドメイン＋そのサブドメイン。|
| Expires | 有効期限。GMT String。<br>例：Wed, 31 Oct 2021 07:28:00 GMT  |
| Max-Age  | 有効期限（秒） |
| Secure | HTTPS通信時のみCookieを送信する。 |
| SameSite | クロスサイト間リクエスト時のポリシー。※後述。 |
| HttpOnly | JavaScriptによるdocument.cookieでアクセス不可。|

## ドメインごとに管理される
調べていて最も混乱したポイント。
Cookieは、ドメインに紐付きます。`Set-Cookie`を返してきたサーバのドメインにのみ送信可能です。
サーバへ送る`Cookie`も、送信先サーバからもらった`Cookie`のみを送信します。
`Set-Cookie`でもらった`Cookie`を他のドメインへ送ることはできません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87538/420b15d5-ccec-150f-6a88-6b2dae9bd0b6.png)


### SameSiteオプション
ドメインごとに管理される`Cookie`ですが、`SameSite`オプションでさらに混乱します。が、よくよく読めば単純です。

`SameSite`オプションは、`Cookie`の制限をより強めるものになります。
通常、`Cookie`は、`Set-Cookie`を送信してきたドメインに対して送れます。
しかし、ブラウザで現在表示しているURLを問わない点が、CSRF攻撃のリスクを孕んでいます。

図解すると、以下のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87538/4ea7206e-c027-b1b3-452b-fd018dfca0f8.png)

表でまとめると以下のようになります。

| Option | 内容 |
|:-:|:--|
|None|クロスサイトでもCookieは送る。ただし、Secureであることが条件（HTTPSでCookieにSecureが付いていること）。|
|Lax|クロスサイトの場合、Getリクエストに限り送る。|
|Strict|クロスサイトの場合、リクエスト時にCookieは送られない。|

Chromeは、デフォルトでLaxとなっているようです。Lax化の流れがあるようです。

## JavaScriptからアクセス可能
`document.cookie`でアクセス可能。

```JavaScript
console.log(document.cookie); // 参照
document.cookie="aaaa=bbbb"; // 設定
```

JavaScriptにおける参照範囲も、ブラウザと同様で、
現在のページを表示しているドメインから`Set-Cookie`を通じて返却されたCookieに限る。

# 4. 3rd Party製 Cookie

下図が仕組みです。
バナー表示のための、HTTPリクエストがバナー広告.comへ飛びます。
それにより、HTTPリクエストに関わる情報からある程度ユーザの行動・リクエスト元サイトなどが把握できます。
Chromeは、これを問題視しており、将来的に排除するようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87538/f365617c-efe5-b1ca-3810-043260cbbd48.png)


# 5. CSRF攻撃
大量の解説記事があるので記載はいったん省略。
もっとも分かりやすかった事例だけ載せておく。

[大量の「はまちちゃん」を生み出したCSRFの脆弱性とは？](https://www.itmedia.co.jp/enterprise/articles/0504/23/news005.html)

## CSRF対策

### OWASPで紹介されている対策
[Cross-Site Request Forgery Prevention Cheat Sheet¶](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#General_Recommendation:_Synchronizer_Token_Pattern)

### IPAで紹介されている対策
[リクエスト強要（CSRF）対策](https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/301.html)

### Spring Security（ライブラリによる）対策
[13. Cross Site Request Forgery (CSRF)](https://docs.spring.io/spring-security/site/docs/3.2.0.CI-SNAPSHOT/reference/html/csrf.html)
IPAで紹介されている対策が「13.2 Synchronizer Token Pattern」。

# 参考
* [HTTP Cookie(MDN)](https://developer.mozilla.org/ja/docs/Web/HTTP/Cookies)
* [Set-Cookie(MDN HTTP Header)](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Set-Cookie)
* [Cookie(MDN HTTP Header)](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Cookie)
* [Document.cookie(MDN DOM)](https://developer.mozilla.org/ja/docs/Web/API/Document/cookie)