Chrome 80が密かに呼び寄せる地獄 ～ SameSite属性のデフォルト変更を調べてみた

#### 記事アップデート
* [【2020/2/2】SameSiteのデフォルト変更が適用されるのは2月17日の週になりました。](#追記)
* 【2020/2/6】2月17日の月曜日はアメリカでは祝日であり、そこは避けられるとのことです。([SameSite Updates](https://www.chromium.org/updates/same-site) 2/3更新内容より)
* [【2020/4/7】COVID-19の影響により**SameSiteの動きを一時的に元に戻し**、夏を目途に再開するとのことです。](#20200407)
* 【2020/6/2】7/14のChrome 84リリースと同時に、再び段階的にロールアウトされていく予定です。([SameSite Updates](https://www.chromium.org/updates/same-site) 5/28)
* [【2020/7/17】Chrome 84がリリースされました。80、81、83にも段階的に適用されます。](#20200717)([SameSite Updates](https://www.chromium.org/updates/same-site) 7/14)
* [【2022/1/19】Firefox 96.0からSameSiteがデフォルトでlaxになりました。](#20220119)([Firefox リリースノート](https://www.mozilla.org/en-US/firefox/96.0/releasenotes/))


# Chrome 80で何が変わるのか

2020年2月4日にGoogle Chrome 80がリリース予定されています。
Google Developersの [新しい Cookie 設定 SameSite=None; Secure の準備を始めましょう ](https://developers-jp.googleblog.com/2019/11/cookie-samesitenone-secure.html) の記事にあるように、
このバージョンから以下のように変わります。

* Chrome 79まで
  * SameSiteが未指定の場合 `None` と同じになる。
* Chrome 80から
  * SameSiteが未指定の場合 `Lax` と同じになる。
  * SameSiteに`None` を指定する場合は、Secure属性が必須となる。

※Secure属性とは、HTTPS上だけで読み取りができるCookieです。

# なぜ変わるのか
先のGoogle Developersのブログに書いてあることをざっくりまとめると以下のようになります。

* SameSiteを使えるようにした。  
  `Lax`か`Strict`を指定すれば**CSRF**は防げる。
* でも誰も使っていないので大多数のサイトにCSRFの脅威がある。
* ならばデフォルトを`Lax`にしてしまえ。そうすれば大多数のサイトはCSRFについて安全だ。
* クロスサイトで使いたきゃ明示的に`None`を指定した上でSecure属性を付ければいい。

# SameSiteとは？

CookieのSameSite属性は`Strict` `Lax` `None`の3つの値を取ります。
値により、クロスサイトでの通信でCookieの送信を制御します。

### 設定値

* **Aサイト**…Cookieを発行したサイト
* **Bサイト**…Aとは別ドメインのサイト

|設定値|効果|
|---|---|
|Strict|**Aサイト**に対し、**Bサイト**からどのようなリクエストがあっても、発行したサイトでCookieヘッダーに含めない (Cookieを使用しない)|
|Lax|Chrome 80からのデフォルト値。<br>**Aサイト**に対し、**Bサイト**からのPOSTや画像読み込み、XHR、iframeでのリクエストでは発行したサイトでCookieヘッダーに含めない (Cookieを使用しない)<br>GETであればCookieヘッダーに含める (Cookieを使用する)|
|None|Chrome 79以下や他ブラウザのデフォルト値。<br>Chrome 80からこの値を設定する場合、Secure属性も必須となる。<br>**Aサイト**に対し、**Bサイト**からどのようなリクエストがあっても、発行したサイトでCookieヘッダーに含める (Cookieを使用する)|

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

#### Strict
外部サイトからのアクセスではCookieを送らない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117852/5c12b2b7-2bbc-dadd-9498-ea2388b92293.png)

#### Lax
外部サイトからのアクセスはGETリクエストのときだけCookieを送る。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117852/9b38f33f-88a9-3cbd-773a-609492283312.png)

#### None
従来通りの動き。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117852/91b6cc46-4167-7b79-bafa-d409dea178e3.png)
[【追記】なおChrome 80以降でSecure属性を付けずSameSite=Noneを指定した場合、set-cookie自体が無効になります。](#secure属性を付けずにsamesitenoneを指定した場合の動き)


### セキュリティ上の効果
**CSRF対策**になります。

[CSRF (クロスサイト・リクエスト・フォージェリ)](https://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AD%E3%82%B9%E3%82%B5%E3%82%A4%E3%83%88%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E3%83%95%E3%82%A9%E3%83%BC%E3%82%B8%E3%82%A7%E3%83%AA) とは、
WEBサイトが**ユーザー本人の意図した動作であることを検証していない**ためにおこる脆弱です。

  * たとえば会員の退会ページを `https://example.com/mypage/delete/`で用意し、  
    ボタン操作で`submit=1`が送信されて退会処理が実行される仕様の場合、  
    パラメータが誰でもわかるので、外部に用意された悪意のあるフォームからも  
    同じパラメータで送信することができます。
  * ログイン済みの会員ユーザーがその状態で悪意のあるフォームを操作してしまうと  
    意図せず退会されていまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117852/390f9ac6-663c-2ab0-7ab6-acd91bc346ff.png)

CSRF対策でよくある方法は、
本人しかわからないパラメータ（ランダム文字列など）を発行して一緒に送信することで
ボタン操作が本人にひもづく動作であることを確認しています。 

 * SameSite (`Lax` or `Strict`)の場合、ユーザー本人にひもづかないようにしてしまうことでCSRF対策としています。  
  ユーザー本人かどうかWEBサイトが知るにはCookieに識別情報に持つことで実現しているのですが、  
  外部の悪意のあるフォームなどからの送信でそのCookieを付けないようにすれば  
  WEBから見ればログインしていない人が退会しようとしていますので、エラーとなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117852/eefaf0b5-8911-d6ab-af74-41430adf2696.png)

このようにCSRFに一定の効果があることから、Chrome 80からデフォルトを`Lax`として扱うようになります。

# 対策
### 特に何もしなくても大丈夫なサイト
* 特にCookieを発行していないサイト。
* 外部からPOSTや画像のロード、XHR、iframeでの呼び出しが無いサイト。
* 外部からPOSTなどされてくるけど、特にCookieが読み込めなくても問題ないサイト。

### 上記以外のサイトでは対策が必要になりそう
SameSite=`None`を付けた上でSecure属性を付与をすることで、従来通りの動作になります。
Secure属性を付けるためにはHTTPSである必要があるため、HTTPのサイトは証明書を取得しSSL/TLSに対応する必要があります。

##### 画像やXHR、iframeなどを使ってユーザートラッキングのCookieを発行しているサイト  
  * いわゆるサードパーティCookieを発行しているサイト。
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117852/89d19849-3916-1279-4afe-137c330d9cdd.png)


##### ECやサービス系の会員サイト**で、決済や認証などで**外部サービスを利用し、外部サイトからPOSTで戻ってくるサイト
  * 決済でいえばクレジットカードの3Dセキュアが本人認証のためにイシュアのサイトにリダイレクトし、  
    戻るときにPOSTしている可能性があります。
  * ユーザー識別をCookieで行っている場合、外部サイトから戻ってきたときにユーザーが識別できなくなります。  
    つまり**大抵はこういった外部サービスとの連携処理がエラーになります**。  
    ECサイトであれば注文ができないことになるので**損失が出る**可能性があります。
  * ユーザー識別CookieはファーストパーティCookieですが、SameSiteは特にファーストパーティ、サードパーティかは区別しません。  
    「サードパーティCookieだけだから関係ないんじゃない？」と誤解している人も周りに多くいました…。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117852/84f60298-2a3f-65b0-5ff6-daacf12a1c4b.png)

# テスト方法
* Chrome 79以下では `chrome://flags/` を開き、以下を `Enabled` にしてブラウザを再起動します。
  * `SameSite by default cookies`
  * `Cookies without SameSite must be secure`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117852/dd13b945-d1f0-b511-2c59-9e2b0c980ca7.png)

### 注意点

* ただしChrome 78, 79では想定通り**動いたり動かなかったり**するような動作をします。
* これはChrome 78以降は「POST+Lax」のとき介入サポートとして、  
  Laxであっても**2分間はCookieを送信する**というChrome独自の動作によるものです。
* この2分間の介入サポートは将来的に削除され、本来のLaxの動きになる予定です。
* テストする際には2分待ちましょう。  
  [コマンドライン](https://www.chromium.org/updates/same-site/test-debug)で`--enable-features=ShortLaxAllowUnsafeThreshold`を付けると変えられるみたいです。(未検証)
* [Chrome Platform Status - Cookies default to SameSite=Lax](https://www.chromestatus.com/feature/5088147346030592) より。

この2分間という介入サポート、かえって混乱を招くのでは…（ボソッ
[【追記】2分間について補足を追加しました](#20200209)。

# まとめ
* **2020年2月4日リリース予定**のChrome 80からSameSite属性のないCookieは`Lax`になる。
* 外部からPOSTや画像のロード、XHR、iframeでの呼び出しでCookieは付かなくなる。
* つまりCookieを使った会員識別をしている際、外部からアクセスされると識別できなくなる。
* サードパーティCookieを使っているサービスのほか、ファーストパーティCookieでも  
  **ECやサービス系の会員サイトで外部と画面上で連携している場合は早急に確認と対策が必要。**

# 追記
#### 2020/02/02
[The Chromium Projects - SameSite Updates](https://www.chromium.org/updates/same-site) に1/30に更新された内容によると
**2/4のStableリリースと同時に適用されず、2/17の週からごく一部のユーザーに適用され、段階的に適用を拡大するようです。**
([Antoine Bourlon](https://twitter.com/kyotonio/status/1223932433440968705)さん、情報ありがとうございます)

2週後ろ倒しになり、なおかつ段階的な適用拡大ということで大きな混乱は避けられそうです…。

> The SameSite-by-default and SameSite=None-requires-Secure behaviors will begin rolling out to Chrome 80 Stable for an initial limited population starting the week of February 17, 2020. We will be closely monitoring and evaluating ecosystem impact from this initial limited phase through gradually increasing rollouts.

#### 2020/02/03
**iOS 12とmacOS 10.14のSafari**では
SameSiteに`None`もしくは`無効な値`を指定すると**[Strictとして扱われてしまいます](https://bugs.webkit.org/show_bug.cgi?id=198181)**。
( @keitaromiura-link さん、情報ありがとうございます)

このためChrome 80以降への対応で`None`を付けつつ、これらのSafariでも動作させたい場合は
[こちらのコメント](https://bugs.webkit.org/show_bug.cgi?id=198181#c24)にあるように正規表現で**ユーザーエージェント**で判定し出し分ける方法があります。
コメントの例ですと下記にマッチするものはSameSiteを送信しない、としています。（あくまで例なのでよく検証ください）

```
^.*iPhone; CPU iPhone OS 1[0-2].*$
^.*iPad; CPU OS 1[0-2].*$
^.*iPod touch; CPU iPhone OS 1[0-2].*$
^.*Macintosh; Intel Mac OS X.*Version\/1[0-2].*Safari.*$
```


出し分けをしないという方法もありますが、[StatCounter](https://gs.statcounter.com/)を見ると2020年2月現在で

* [iOS 13より前のバージョンのシェア…iOS内で約23%](https://gs.statcounter.com/ios-version-market-share/mobile-tablet/japan/#monthly-202002-202002-bar) (iOS自体は[全OS](https://gs.statcounter.com/os-market-share/all/japan/#monthly-202002-202002-bar)の約31%なので**全体の約7%**)
* [macOS 10.15より前のバージョンのシェア…macOS内で約58%](https://gs.statcounter.com/macos-version-market-share/desktop/japan/#monthly-202002-202002-bar) (macOS自体は[全OS](https://gs.statcounter.com/os-market-share/all/japan/#monthly-202002-202002-bar)の約11%なので**全体の約6%**)

と、まだまだシェアは多少あり、無視はしにくい印象です。
`Strict`として扱われてしまうと単なるGETでの遷移でもCookieが付かなくなり影響が大きいため、
完全でないですが、ある程度緩和が期待できるこういった施策を入れるのが良さそうです。

#### 2020/02/06
##### ユーザーエージェント判定について
[NoneがStrictになるブラウザのユーザーエージェント判定](#20200203)を含め、
Noneに互換性のないブラウザ（ユーザーエージェント）をざっくり和訳＆まとめました。
判定方法など詳しくは[SameSite=None: Known Incompatible Clients](https://www.chromium.org/updates/same-site/incompatible-clients)をご覧ください。
またそのページにもある通り、完全なものではなく、しっかりと動作検証する必要があります。
（ @sakai さん、情報ありがとうございます ）

|ブラウザ|概要|
|---|---|
|Chrome 51以上～66以下のブラウザ|`None`を指定すると拒否する。<br>Chromium由来の古いバージョンのブラウザーとAndroid WebViewも同様<br>これは当時[Noneが仕様に無かった](https://tools.ietf.org/html/draft-west-first-party-cookies-07#section-3.1)から。<br>なお51未満は無視されるので`None`と同じ動作になる。|
|AndroidのUC Browser 12.13.2未満|`None`を指定すると拒否する。<br>こちらも当時`None`が仕様に無かった。|
|macOS 10.14のSafariと組み込みブラウザ<br>iOS 12のすべてのバージョンのブラウザ|※[2020/02/03の追記](#20200203)と同じ。|

##### Secure属性を付けずにSameSite=Noneを指定した場合の動き
Secure属性を付けずにSameSite=Noneを指定した場合、`set-cookie`ヘッダー自体が無効となります。
すなわちCookie自体を持つことができません。

Chrome 80 (flagsで設定変更した上で) で確認したところ、F12を押して出てくるDevToolsの**Network**のHeadersで見てみると、
`set-cookie`の行末に⚠アイコンが表示されSameSite=NoneのときはSecureが必須である旨の警告が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117852/94499ea4-3fde-b113-7dd2-a919abe0995d.png)
ほかSameSite未指定の場合のサードパーティCookie発行でも警告がでます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117852/dc286c38-8f53-436c-60f2-7ba6f972d171.png)

またChrome 80ではDevToolsのNetworkに **Only show requests with SameSite issues** のチェックが追加されました。
ここにチェックを入れると、上記のような⚠アイコンのもののみにフィルタされます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117852/5a11a8d0-f6d1-db51-0ce5-cb16ecfef549.png)
  
  
#### 2020/02/09
##### SameSite無しのCookie発行後、2分間はPOSTでも送信できる点について
[2020 年 2 月の SameSite Cookie の変更: 知っておくべきこと](https://developers-jp.googleblog.com/2020/02/2020-2-samesite-cookie.html) より、
**2分間**はPOST等でもCookieを送れる仕様について、
認証の過程でウェブサイトと認証の連携先（サードパーティプロバイダ）とでCookieを渡すときに
ログインに影響がないように配慮されたものとのことです。

2分である理由は書いていませんが、HTTPの接続タイムアウトが120秒であることが多いからでしょうか。
とはいえ**ログイン時にユーザー識別Cookieを発行し
その場で外部と連携して2分以内に戻ってくる場合のみ有効**で、
ログイン前からユーザーセッションをCookieで管理をしていたり、
3Dセキュアのように2分で戻るかわからない場合には無効です。

また[将来的には廃止される](https://www.chromestatus.com/feature/5088147346030592)とのことなので、
現在この仕様でうまく動いていてもいずれ対応が必要になってきます。
> Support for this intervention ("Lax + POST") will be removed in the future.

#### 2020/04/07
4/3に[SameSite Updates](https://www.chromium.org/updates/same-site)に更新がありました。
COVID-19 (新型コロナウイルス) の影響により重要なサイトへのアクセスの安定性を確保するため、
SameSiteの動きを一時的に元に戻すようです。
再開は夏を目標としているとのことです。

#### 2020/07/17
7/14にChrome 84がリリースされました。([SameSite Updates](https://www.chromium.org/updates/same-site) 7/14)
84だけでなく、**80、81、83のStableにも数週間で段階的に適用されます**ので、
アップデートしなければ挙動が変わらないということではありません。
※なお82は欠番です。

#### 2022/01/19
FirefoxでもSameSiteがデフォルトで`lax`になりました。
バージョン96.0からになります。
[Firefox リリースノート](https://www.mozilla.org/en-US/firefox/96.0/releasenotes/)