EC-CUBE の SameSite cookie 対応まとめ

EC-CUBE の SameSite cookie 対応がバージョンごとに散乱していて、わかりづらいのでまとめてみます

## SameSite cookie とは

[Chrome 80が密かに呼び寄せる地獄 ～ SameSite属性のデフォルト変更を調べてみた](https://qiita.com/ahera/items/0c8276da6b0bed2b580c) がわかりやすいです。

## EC-CUBE の影響

標準状態では影響はありません。

- 標準状態では、クロスサイト Cookie は使用していない
- 外部サイトから POST で戻ってくるケースは、そもそも CSRF 対策でブロックしている

しかし、 **一部の決済連携や独自カスタマイズで影響の出るケースがあります**

- 3Dセキュアや、キャリア決済、ID決済などの決済連携で POST で戻ってくるケース
- 外部サイトから直接カートに入れるようカスタマイズしているサイト

詳細は [EC-CUBE/ec-cube#4457](Chrome80のSameSiteの影響で3Dセキュア等を利用する場合に購入に失敗する) をご覧ください。

影響の出るケースでは、以下のような不具合が発生します。

- 決済完了画面が表示されず、エラーとなる。
- 決済完了画面は表示されるが、ログアウトされた状態となる。
- 決済完了画面は表示されるが、カートの中身がクリアされない。
- カートが空になり、ログアウトされた状態となる。

## 対応にあたっての課題

- PHP の [session_set_cookie_params()](https://www.php.net/session_set_cookie_params) 関数は、 PHP7.3未満とそれ以上で振舞いが違うため、PHPバージョンで分岐する必要がある
- iOS12, macOS10.14 など、SameSite=None に未対応のブラウザがある。これらのブラウザはリリース当時の SameSite standard draft を厳密に実装しているため、 SameSite=None を Strict と認識する
- SameSite=None の場合は、 secure 属性が必須となる

## EC-CUBE バージョンごとの対応方針

基本的に SSL 必須。 今後、 SSL 未対応のサイトでは決済連携できなくなると考えてよいと思われる。

2020年2月17日頃までには、公式パッチがリリースされる予定です。

### 2系

- [SameSite=None を未サポートの UA 向けに, SameSite 属性を削除した互換用 cookie を発行する](https://web.dev/samesite-cookie-recipes/#handling-incompatible-clients)
   - UA の仕様により SameSite=None が SameSite=Strict と見なされて cookie が拒否された場合は, 互換用の cookie を読み込む
   - ローカル環境等、非SSL環境では SameSite 属性を空にし、 secure 属性を付与しない

[EC-CUBE/ec-cube2#374](https://github.com/EC-CUBE/ec-cube2/pull/374) で修正。
2.4〜2.13 までの対応方法も上記 Pull Request 参照

### 3系

[EC-CUBE/ec-cube3#82](https://github.com/EC-CUBE/ec-cube3/pull/82) で修正予定。
- 3系の過去バージョンも、上記と同様の修正をすることで対応可能

### 4系

[EC-CUBE/ec-cube#4519](https://github.com/EC-CUBE/ec-cube/pull/4519) で修正予定。
- 4系の過去バージョンも、上記と同様の修正をすることで対応可能

----
(以下は3系の古い情報です)

- ~~PHP7.3未満 :: `session.storage.options` の cookie.path に `'; SameSite=None'` を追記し、 cookie.secure を true とする~~
- ~~PHP7.3以上 :: Symfony/HttpFoundation bundle にパッチを当てる必要がある~~

`session.storage.options` の cookie.path に `'; SameSite=None'` を追記し、 cookie.secure を true とする

- 上記の対応をすると、 localhost など SSL の使用できない開発環境では、セッションが無効となりカートやログインが動作しなくなるため注意。
- 未対応ブラウザへの対応は個別にする必要がある。
- Symfony は setcookie() 関数ではなく、生の header を送信して cookie を設定するため、 [PHP7.3 における setcookie() 関数仕様変更の影響を受けない模様](https://github.com/php/php-src/commit/5cb825df7251aeb28b297f071c35b227a3949f01)

----

(以下は4系の古い情報です)

[Symfony/HttpFoundation bundle](https://github.com/symfony/symfony/commit/4091feb6934f8fa227c7eb19a4d739d316eabf6e#diff-00d2ec9132184a5d6b384d56ebcb0616) のバージョンアップで対応可能になる予定。

それまでは暫定措置として以下のように対応する

app/config/eccube/packages/framework.yaml の cookie.path に `'; SameSite=None'` を追記し、 cookie.secure を true とする

- 上記の対応をすると、 localhost など SSL の使用できない開発環境では、セッションが無効となりカートやログインが動作しなくなるため注意。
- 未対応ブラウザへの対応は個別にする必要がある。
- ~~Symfony は setcookie() 関数ではなく、生の header を送信して cookie を設定するため、 [PHP7.3 における setcookie() 関数仕様変更の影響を受けない模様](https://github.com/php/php-src/commit/5cb825df7251aeb28b297f071c35b227a3949f01)~~
  - Warning が発生するがログイン等は可能な模様(https://github.com/EC-CUBE/ec-cube/issues/4517)

## その他

[SameSite=None に対応していない環境](https://www.chromium.org/updates/same-site/incompatible-clients)の検証をしようと試みた。
が、以下の環境で外部サイトから POST されるケースでは、 SameSite=None が Strict と認識されるようなことは無く、 SameSite 属性は無視されて Cookie が拒否されることは無かった

- macOS Mojave 10.14.6, Safari バージョン12.1.2 (14607.3.9)
- iOS12.4(simulator)
- iOS12.0(simulator)
- Chromium(386215, 394935, 508578, 540271) [こちらからダウンロード可能](https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html)

**iOS12.4(実機)では、 Cookie が拒否されるのを再現確認しました**

## 参考

- https://www.chromium.org/updates/same-site/incompatible-clients
- https://www.flywheel.jp/blog/cookie-and-privacy/
- [PHPでSame-site cookie](https://qiita.com/tadsan/items/96db7894e285743d996b)
- [Fix #69948: path/domain are not sanitized in setcookie](https://github.com/php/php-src/commit/5cb825df7251aeb28b297f071c35b227a3949f01)