Compliant Cookies in PHP

A number of years have passed since the last time that I've written about coding solutions on this site, but a number of people have asked me recently how they can have PHP record the SameSite=None attribute when setting cookies. ChromeLabs has a handy example page showing how it's done but, for people who need to have more attributes, this is how it's implemented in many of my PHP-based projects:

$attribs = array( 'SameSite' => (($isHTTPS) ? 'None' : 'Strict'),
                  'Domain'   => strtolower($_SERVER['SERVER_NAME']),
                  'Expires'  => {unix-formatted expiration time},
                  'Max-Age'  => {number of seconds},
                  'Path'     => '/',
                  'Secure'   => $isHTTPS,
                  'HttpOnly' => true,
setcookie( "{cookie name}", "{cookie value}", $attribs );

This little bit of code uses $isHTTPS as a boolean to understand if the site has an SSL certificate1. Expires needs to be passed as an integer in order for PHP to set the value to the correct 'D, d M Y H:i:59' format as outlined in RFC6265.

But My Server Isn't Running PHP 7.3 …

The problem with the solution I've outlined above is that it will not work if a server is running PHP 7.2 or older. In this situation, the solution becomes a little more complicated. For the sake of visual clarity, here it is broken up across lines:

$cookieStr = "Set-Cookie: $key=$val;" .
                        " Expires=" . date('D, d M Y H:i:s', {unix-formatted expiration time}) . " GMT;" .
                        " Max-Age={number of seconds};" .
                        (($isHTTPS) ? " Secure; Path=/;" : "") .
                        " Domain=" . strtolower($_SERVER['SERVER_NAME']) . ";" .
                        " SameSite=" . (($isHTTPS) ? 'None' : 'Strict') . ";" .
                        " HttpOnly";
header( $cookieStr );

This resolves the warnings in browsers like Chrome and Firefox that developers will see when testing their work and it ensures that cookies will be properly read and saved by browsers going forward.

Fun, huh?

  1. This is important in my development process, as my local tests are done without SSL certificates, whereas the live servers are all HTTPS.