【PHP】setcookieでPartitionedを発行したい場合はどうすればいいか

# サードパーティCookieについて

サードパーティCookieは2024年以降、[基本的に禁止されます](https://qiita.com/rana_kualu/items/16383af7f6f56f6e982e)。

この理由の大きなひとつが、サイトAとサイトBの情報を紐付けされてしまうことです。

![01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26088/62e51047-c5a7-4e0f-7ada-4ac41b971c37.jpeg)

※画像出典：[Chrome 114 の新機能](https://developer.chrome.com/blog/new-in-chrome-114?hl=ja)

`tracking.com`は`a.com`上でのあなたの動きを監視することができますし、`tracking.com`は`b.com`上でのあなたの動きを監視することができます。
そして`tracking.com`は、`a.com`と`b.com`の情報を紐付けることが可能です。

すなわち、多数のサイトで紐付けを行うことで、あなたの趣味嗜好思想信条が完全に把握されてしまうわけです。
FacebookやInstagramでは聖人面しておきながら裏ではあんなサイトやこんなサイトを見ているんだ、みたいなことが現在のCookieの仕組みでは可能です。
可能ですというかおそらく実際やっているでしょう。

逆に言うと、サイトを横断して情報収集ができないのであればサードパーティCookieを発行できても問題ないわけです。
という発想で作られたのが[Cookies Having Independent Partitioned State](https://qiita.com/rana_kualu/items/8d8e8cd0d4737e7884e3)、通称CHIPSという仕組みです。

![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26088/55d4b78e-1e2f-e93b-738a-a619c239d167.png)

`tracking.com`は`a.com`上でのあなたの動きを監視することができますし、`tracking.com`は`b.com`上でのあなたの動きを監視することができます。
しかし`tracking.com`は、`a.com`と`b.com`の情報を紐付けることはできません。

めでたしめでたし。

使い方としてはcookieに[Partitioned属性](https://developer.mozilla.org/en-US/docs/Web/Privacy/Partitioned_cookies)を発行するだけであり、簡単に切り替えることができます。


# setcookieの問題点

ということでPHPでもCHIPSに対応したいですね。
ところが実はそう素直にはいきません。

以下は[setcookie](https://www.php.net/manual/ja/function.setcookie.php)関数のシグネチャです。

```php
setcookie(string $name, string $value = "", array $options = []): bool

// 古い書き方
setcookie(
    string $name,
    string $value = "",
    int $expires_or_options = 0,
    string $path = "",
    string $domain = "",
    bool $secure = false,
    bool $httponly = false
): bool
```

元々は引数を個々に受け取るシグネチャだけでしたが、引数が多くなりすぎてしまったためPHP7.3において配列で受け取るオプションが追加されたという歴史があります。
また[複数のシグネチャを持つ関数は徐々に整理されつつある](https://qiita.com/rana_kualu/items/4d67ffdf4cb05d1ba86f)ため、古い書き方は今後使用できなくなる可能性があります。
従って、今後は配列で渡すほうを使うようにしましょう。

さてこの`$options`引数ですが、ひとつ問題があります。
なにかっつーと対応している属性以外受け取ってくれないんだなこれが。

```php
setcookie('name', 'value', ['secure'=>true, 'Partitioned'=>true]);
```

![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26088/1220d823-5a30-8f89-0b26-e03089e7002e.png)


`Partitioned`なんて知らねーよのFatal errorが出ます。

なんで。

PHP8.3時点では`$options`に渡せることのできる属性は`expires`・`path`・`domain`・`secure`・`httponly`・`samesite`だけであり、それ以外の値を渡すことはできません。

なんで。


# setcookieでPartitionedを渡せるようにするプルリクエスト

ということで[`Partitioned`を渡せるようにするプルリクエスト](https://github.com/php/php-src/pull/12652)が投げられていました。

[メーリングリスト](https://externals.io/message/122028)にも投稿がありますが、特に大きな反応もないまま埋もれているようです。

このプルリクは、おそらくRFCを通さずマージされるんじゃないかなと思います。
というかこの程度でRFCとかいらんやろ。

と言いたいところですがかつてSameSiteが追加されたときは[わざわざRFCが立ってた](https://wiki.php.net/rfc/same-site-cookie)ので、もしかしたらRFC経由になるかもしれません。
こんなんそのまま通してええやろ…


# 旧バージョンでもPartitionedを発行したいんだけど

この対応によって、今後のバージョンでPartitionedなCookieが発行できるようになる予定です。

しかし、諸事情でサーバの更新ができない、あるいは今すぐPartitionedを発行したいという場合はどうすればいいのでしょうか。


```php
$options = [
    'expires' => 0,
    'path' => '/',
    'samesite' => 'strict; Partitioned; Priority=Low; hoge=fuga',
    'secure' => true,
    'httponly' => true,
];
setcookie('name', 'value', $options);
```

![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26088/a89504ac-1560-d6e7-eaaa-36c7a3973116.png)

マジかよ…

正直バグというか脆弱性なのでは？という挙動ですが、現状では関数が対応していない属性のCookieを発行するにはこんなことをしなければなりません。
これ[ずっと前から既知の動作](https://github.com/php/php-src/pull/2613#issuecomment-401266510)なので、このような場合のためにあえて潰さずに残しているのかもしれませんね。

あと[Mixiが公式にこの方法を案内している](https://developer.mixi.co.jp/news/news_apps/016704.html)のはちょっと笑う。


# 感想

というかむしろ、そもそも渡せる引数の制限を外してくれんかね。
今の仕様だと、今後もキーが増えるたびに毎回対応する必要があるわけで、それなら最初から好きな値を渡せるようにしておいた方がいいと思うのだが。

実際既に、[SameParty](https://developers.google.com/privacy-sandbox/blog/first-party-sets-sameparty?hl=ja)や[Priority](https://developer.chrome.com/blog/new-in-devtools-81?hl=ja)といった一部ブラウザの独自属性には、まともな方法では対応できず、バグっぽい怪しい方法を使わざるを得ません。
まあ独自実装など知ったこっちゃねえという話ではあるのですが、実際そう言われても困ることもあるので、ここはまあどうにかしてほしいところですね。