PHP setcookie "SameSite=Strict"?

PhpSecurityCookies

Php Problem Overview


I recently read "RFC 6265" on the attribute "Same Site", I looked at some articles that talked about that in April 2016, "same-site" attribute has been implemented for Chrome 51 and Opera 39 ...

I wonder if current PHP supports creating cookies with this attribute?

Reference:

Php Solutions


Solution 1 - Php

1. For PHP >= v7.3

You can use the $options array to set the samesite value, for example:

setcookie($name, $value, [
    'expires' => time() + 86400,
    'path' => '/',
    'domain' => 'domain.com',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'None',
]);

The value of the samesite element should be either None, Lax or Strict.

Read more in the manual page.

2. For PHP < v7.3

You can use one of the following solutions/workarounds depending on your codebase/needs

2.1 Setting SameSite cookies using Apache configuration

You can add the following line to your Apache configuration

Header always edit Set-Cookie (.*) "$1; SameSite=Lax"

and this will update all your cookies with SameSite=Lax flag

See more here: https://blog.giantgeek.com/?p=1872

2.2 Setting SameSite cookies using Nginx configuration
location / {
    # your usual config ...
    # hack, set all cookies to secure, httponly and samesite (strict or lax)
    proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict";
}

Same here, this also will update all your cookies with SameSite=Lax flag

See more here: https://serverfault.com/questions/849888/add-samesite-to-cookies-using-nginx-as-reverse-proxy

2.3 Setting SameSite cookies using header method

As we know cookies are just a header in HTTP request with the following structure

Set-Cookie: key=value; path=/; domain=example.org; HttpOnly; SameSite=Lax

so we can just set the cookies with header method

header("Set-Cookie: key=value; path=/; domain=example.org; HttpOnly; SameSite=Lax");

In fact, Symfony is not waiting for PHP 7.3 and already doing it under the hood, see here

You can use same in Laravel too because Laravel under the hood using Symfony's Symfony\Component\HttpFoundation\Cookie class

2.4 Setting SameSite cookies using a bug in setcookie method
setcookie('cookie-name', '1', 0, '/; samesite=strict');

Be careful with this one, it's a known bug in PHP setcookie method and already resolved in PHP7.3 version, see here - https://github.com/php/php-src/commit/5cb825df7251aeb28b297f071c35b227a3949f01

Solution 2 - Php

[Important update: As @caw pointed out below, this hack WILL BREAK in PHP 7.3. Stop using it now to save yourself from unpleasant surprises! Or at least wrap it in a PHP version check like if (PHP_VERSION_ID < 70300) { ... } else { ... }.]

It seems like you can abuse the "path" or "domain" parameter of PHP's "setcookie" function to sneak in the SameSite attribute because PHP does not escape semicolons:

setcookie('samesite-test', '1', 0, '/; samesite=strict');

Then PHP sends the following HTTP header:

> Set-Cookie: samesite-test=1; path=/; samesite=strict

I've just discovered this a few minutes ago, so please do your own testing! I'm using PHP 7.1.11.

Solution 3 - Php

Based on Steffen's answer above, this is the method I am using to support both php <= 7.2 and php >= 7.3:

/**
 * Support samesite cookie flag in both php 7.2 (current production) and php >= 7.3 (when we get there)
 * From: https://github.com/GoogleChromeLabs/samesite-examples/blob/master/php.md and https://stackoverflow.com/a/46971326/2308553 
 *
 * @see https://www.php.net/manual/en/function.setcookie.php
 *
 * @param string $name
 * @param string $value
 * @param int $expire
 * @param string $path
 * @param string $domain
 * @param bool $secure
 * @param bool $httponly
 * @param string $samesite
 * @return void
 */
function setCookieSameSite(
    string $name, string $value,
    int $expire, string $path, string $domain,
    bool $secure, bool $httponly, string $samesite = 'None'
): void {
    if (PHP_VERSION_ID < 70300) {
        setcookie($name, $value, $expire, $path . '; samesite=' . $samesite, $domain, $secure, $httponly);
        return;
    }
    setcookie($name, $value, [
        'expires' => $expire,
        'path' => $path,
        'domain' => $domain,
        'samesite' => $samesite,
        'secure' => $secure,
        'httponly' => $httponly,
    ]);
}

Solution 4 - Php

I wrote a class for setting samesite cookies.

https://github.com/ovunctukenmez/SameSiteCookieSetter

It works on all PHP versions. It also checks if the browser supports samesite parameter properly.

Here is the usage:

//set samesite strict php cookie
SameSiteCookieSetter::setcookie('samesite_test','testvalue', array('samesite' => 'Strict'));

Solution 5 - Php

According to this site, it seems it is a matter of PHP 7.3. As of the voting results, a more general extension to cookie-related functions is being implemented + there might be also a new key in php.ini file.

But as Marc B already wrote, you can use header() function call instead, I would do it in some file with used for inclusion of other initial stuff.

Solution 6 - Php

Adding to the answer by Marty Aghajanyan (because apparently I can answer, but not yet comment)

Doing it in Apache via mod_headers in conjunction with PHP was not working for me in Apache 2.4.29 (Ubuntu). In reviewing the docs (http://www.balkangreenfoundation.org/manual/en/mod/mod_headers.html) I noticed the "always" condition has certain situations where it does not work from the same pool of response headers. Thus the following worked for me to set the SameSite parameter. (Tho in my case I am setting None for the recent Chrome 80 update)

Header edit Set-Cookie ^(.*)$ "$1; Secure; SameSite=None"

The docs also suggest that if you want to cover all your bases you could add the directive both with and without "always", but I have not tested that.

Solution 7 - Php

Worth mentioning that Safari 12 on both macOS and iOS will not recognise a value of None for the SameSite attribute, and default to a value of Strict.

Version 13 will accept "None", but without explicitly setting a value, it defaults to "Lax".

Here's a good explanation:

https://www.thinktecture.com/en/identity/samesite/samesite-in-a-nutshell/

Solution 8 - Php

This might also help for someone still struggling, and using PHP >= 7.3.x and using CI 3.1.11

In the index.php found in the root, add the line below

if(isset($_COOKIE["PHPSESSID"])){
    header('Set-Cookie: PHPSESSID='.$_COOKIE["PHPSESSID"].'; SameSite=None');
}

It worked for me, after trying it all (in vain)

Solution 9 - Php

There are a lot of examples showing how to set this attribute, but not many explanations of why.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#SameSite_attribute

> If a cookie is needed to be sent cross-origin, opt out of the SameSite restriction by using the None directive. The None directive requires that the Secure attribute also be used.

The examples that are setting SameSite to None or Lax are only appropriate for cross-domain scenarios. If your code isn't cross-domain, use Strict.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLauro MoraesView Question on Stackoverflow
Solution 1 - PhpMarty AghajanyanView Answer on Stackoverflow
Solution 2 - PhpSteffenView Answer on Stackoverflow
Solution 3 - PhpjlyonView Answer on Stackoverflow
Solution 4 - PhpjetblackView Answer on Stackoverflow
Solution 5 - PhpDavid KomanekView Answer on Stackoverflow
Solution 6 - PhpEthan BurnsideView Answer on Stackoverflow
Solution 7 - PhpY.K.View Answer on Stackoverflow
Solution 8 - PhpKimondo EzraView Answer on Stackoverflow
Solution 9 - PhpAndrew KosterView Answer on Stackoverflow