Table of Contents

PHP RFC: Cookies Having Independent Partitioned State (CHIPS)

Introduction

Modern browsers have several security levels to separate cross-site communications, including various settings to how cookies should behave across the sites. CHIPS is best explained by the description on MDN:

Cookies Having Independent Partitioned State (CHIPS, also known as Partitioned cookies) allows developers to opt a cookie into partitioned storage, with a separate cookie jar per top-level site.

Without cookie partitioning, third-party cookies can enable services to track users and associate their information across unrelated top-level sites. Cookies marked Partitioned are double-keyed: by the origin that sets them and the origin of the top-level page.

CHIPS is necessary to keep cross-site cookies working properly, as Google Chrome has been phasing third-party cookies out for some time now.

CHIPS technology was introduced not so long ago, but still has “little” adoption (currently “only” available in Blink-based or Gecko-based browsers). Prior to Firefox 141, it was temporarily disabled due to web compatibility issues. Starting with Firefox 141, the feature is fully enabled. The feature is also implemented in Safari 18.4 (released recently).

CHIPS implementation introduces a new Cookie parameter named Partitioned, which should be applied during Secure context.

This RFC proposes two groups of changes:

1. Adding a new option “partitioned” for setcookie() and setrawcookie() functions.

<?php
 
setcookie('name', 'value', ['secure' => true, 'partitioned' => true]);
 
?>

Notably, only the array overload of these functions is affected. This is in a similar vain to the recent samesite addition that also was only added to the array overload.

2. Add support for partitioned session cookies. This means that session_set_cookie_params(), session_get_cookie_params(), and session_start() will receive support for partitioned cookies.

Currently, frameworks and applications have to work around not having this option by manually setting a header, which is cumbersome. This feature was requested via https://github.com/php/php-src/issues/12646, and has seen some activity and workarounds being posted.

Proposal

Extend setcookie() and setrawcookie() functions and make them able to accept one more option named “partitioned” to control CHIPS behavior. Using “partitioned” without setting “secure” will throw a ValueError.

Extend session_start() options with “cookie_partitioned”, and session_set_cookie_params()'s array overload to support the “partitioned” key. Also update the returned array from session_get_cookie_params() to include the “partitioned” key with a boolean value.

Examples

Example 1: Regular CHIPS usage:

<?php
 
setcookie("name", "value", ["secure" => true, "partitioned" => true]);
 
// will result in headers:
// Set-Cookie: name=value; secure; Partitioned
 
?>

Example 2: Using “partitioned” without “secure” or with disabled “secure” option:

<?php
 
setcookie("name", "value", ["partitioned" => true]);
setcookie("name", "value", ["secure" => false, "partitioned" => true]);
 
// Both raise a ValueError:
// Uncaught ValueError: setcookie(): "partitioned" option cannot be used without "secure" option in ...
 
?>

Example 3: Starting a session with partitioned cookies:

<?php
 
session_id("12345");
session_set_cookie_params(["secure" => true, "partitioned" => true]);
session_start();
 
// will result in headers:
// Set-Cookie: PHPSESSID=12345; path=/; secure; Partitioned
 
?>

Example 4: Setting the session cookie settings and dumping those settings:

<?php
 
session_set_cookie_params([
  "secure" => true,
  "partitioned" => true
]);
var_dump(session_get_cookie_params());
 
// will dump this array:
// array(7) {
//   ["lifetime"]=>
//   int(0)
//   ["path"]=>
//   string(1) "/"
//   ["domain"]=>
//   string(0) ""
//   ["secure"]=>
//   bool(true)
//   ["partitioned"]=>
//   bool(true)
//   ["httponly"]=>
//   bool(false)
//   ["samesite"]=>
//   string(0) ""
// }
 
?>

Example 5: Configuring the session with partitioned, but not secure:

<?php
 
var_dump(session_start(["cookie_secure" => false, "cookie_partitioned" => true])); // bool(false)
 
// Warning: session_start(): Partitioned session cookie cannot be used without also configuring it as secure in ...
 
?>

The reason we defer the check until the session is started, is because any combination of ini_set(), session_start(), session_set_cookie_params() can set some cookie settings.

Backward Incompatible Changes

No breaking changes.

Proposed PHP Version(s)

PHP 8.5

RFC Impact

To the Ecosystem

No effects.

To Existing Extensions

  1. ext/standard, as the existing setcookie() and setrawcookie() PHP APIs are contained there. Furthermore, there is an internal API php_setcookie for C extensions that now gets an additional boolean argument partitioned.
  2. ext/session, as described above.

To SAPIs

An additional cookie parameter is introduced, but it has no direct API influence on the SAPI layer.

php.ini Defaults

The session cookie settings for session_start() and session_set_cookie_params() are stored in INI settings. These functions internally modify the session INI settings for the lifetime of the request. For example, using session_start(["cookie_secure" => true]) will set the session.cookie_secure setting to true. Likewise, we will add a session.cookie_partitioned boolean setting that defaults to false, but can be set via the above-mentioned functions. It is also possible to default this setting to true in the php.ini if one desires to do so.

Open Issues

No issues.

Future Scope

Currently, there is no future scope. Any CHIPS evolution steps will be made separately.

Voting Choices

Single voting widget that requires 2/3rd majority.

Implement CHIPS as outlined in the RFC?
Real name Yes No
asgrim (asgrim)  
ayesh (ayesh)  
cmb (cmb)  
crell (crell)  
derick (derick)  
girgias (girgias)  
josh (josh)  
kalle (kalle)  
nicolasgrekas (nicolasgrekas)  
nielsdos (nielsdos)  
ocramius (ocramius)  
pmjones (pmjones)  
sergey (sergey)  
theodorejb (theodorejb)  
timwolla (timwolla)  
Count: 15 0

Patches and Tests

Implementation (patch+test): https://github.com/php/php-src/pull/12652

Implementation

After the RFC is implemented, this section should contain:

  1. the version(s) it was merged into
  2. a link to the git commit(s)
  3. a link to the PHP manual entry for the feature

References

CHIPS MDN: https://developer.mozilla.org/en-US/docs/Web/Privacy/Guides/Privacy_sandbox/Partitioned_cookies

Rejected Features

None yet.

Changelog

Version 0.9.1: Extra clarifications on non-Blink browsers, thanks Claude! Version 0.9: First version.