104

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:

Community
  • 1
  • 1
Lauro Moraes
  • 1,358
  • 2
  • 14
  • 16
  • 14
    even if the setcookie function doesn't, you can ALWAYS just output your own custom `header('Set-Cookie: ...')` – Marc B Sep 28 '16 at 14:51
  • @MarcB: True, don't forget to URL encode the name and value too. – SilverlightFox Sep 29 '16 at 15:20
  • 2
    [here](https://bugs.php.net/bug.php?id=72230&thanks=6) is an open request, which you can vote and follow. – keune May 16 '17 at 13:40
  • 2
    This library lets you use the attribute with cookies: https://github.com/delight-im/PHP-Cookie Most importantly, it also supports this attribute for PHP’s built-in sessions, which automatically set and use cookies. Alternatively, wait for PHP to ship the feature natively. – caw Jan 21 '18 at 18:15
  • @caw with this library the cookie is generated and written to the output buffer written to the client, and only then does the class get the cookie and overwrite it, however fast this process may be, it does not guarantee that there can be no interception. To an issue that deals with something similar ... https://github.com/delight-im/PHP-Cookie/issues/15 although this issue questions a DRAFT to php session points to the same paradigm. – Lauro Moraes Jan 21 '18 at 19:48
  • @caw The solution presented in the accepted answer mitigates this since the output already contains the attribute – Lauro Moraes Jan 21 '18 at 19:52
  • @LauroMoraes First, your description of how the library works is wrong. It works as least as good as the selected answer here, while being more flexible and less likely to break with future PHP releases. It does write the correct cookie directly. Second, the interception and rewrite of the cookie does only happen for PHP’s session functions – because there’s simply no other way to do that. Third, even that interception is safe, because PHP does not send any headers before the first bytes of actual output are processed, right? Fourth, that issue you linked to is simply irrelevant or invalid. – caw Jan 21 '18 at 20:17
  • Hmm ... I have not tried to describe the library, it's really good, I've used it for a long time, I confess it does not keep up with the current state, 7 versions have been released since September 2016. CSRF is the biggest risk for cookies without the `Same-Site` so I do not think the reference is at all useless hardly anyone uses cookies in PHP if not to manipulate sessions. And the question is specific to the `setcookie()` not `header()`. – Lauro Moraes Jan 21 '18 at 20:56
  • Little tip: If you need to set samesite to None then your cookie must be secure. – Ric Apr 27 '20 at 10:07
  • Great! Btw, does PHP 5.3 support samesite=None attribute? – LoveCoding Dec 14 '20 at 20:36

10 Answers10

172

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.example',
    '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

Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
Marty Aghajanyan
  • 12,651
  • 8
  • 35
  • 37
  • 4
    It doesn't look like that's the correct method signature for `setcookie` (and `setrawcookie`) in PHP 7.3+, an array of options is passed as the third parameter, one of which is called `samesite`. See https://github.com/php/php-src/blob/PHP-7.3/UPGRADING#L350 – rink.attendant.6 Oct 09 '18 at 08:51
  • PHP Manual states that `samesite` is passed as part of an array in the `setcookie` 3rd parameter as shown here: https://www.php.net/manual/en/function.setcookie.php – Jarom Oct 18 '19 at 20:52
  • @Jarom Indeed, the RFC link the answerer posted regarding `setcookie` says at the bottom under **Errata:** "The actually implemented alternative signatures of the functions have been slightly changed from the original RFC. See the documentation in the PHP manual for details". Looking at the manual there is no mention of a samesite argument. Instead the manual only mentions it as a value of an $options array. Can someone confirm if the samesite argument actually works? – Cave Johnson Jan 22 '20 at 21:39
  • 1
    Does `session_set_cookie_params()` on PHP <= 7.2 also allow injecting samesite into $path? – marcovtwout Mar 11 '20 at 14:29
  • How to also add secure and httponly with 2.4 setcookie method? – Freedo Apr 08 '20 at 10:25
  • Thanks for your answer, I'm using Drupal 7 hosted on Acquia, using PHP 7.2.2; I'm really new to CMS, can you point me in which file I need to alter my session cookie to add the SameSite option? – Elber CM Apr 20 '20 at 17:28
  • Somehow .htaccess method worked on my PHP 7.2 box, but it doesn't work on another machine which is running PHP 7.3+ so I moved to setcookie() method on that. – spetsnaz Aug 27 '20 at 22:43
  • 1
    @marcovtwout Yes, the injection in `session_set_cookie_params()` did also work for me in PHP 7.2.24 the same way and I didn't find a fix for this in the PHP changelog, which is ok for me :) – malisokan Sep 11 '20 at 14:29
  • for PHP 5.6.40 If you have no problem rebuilding the PHP binary, I managed to port this feature from PHP 7.3 to PHP 5.6.40, and there is now a pull request. See full answer here: https://stackoverflow.com/a/64960472/1641763 – Nadir Nov 22 '20 at 23:29
  • @MartyAghajanyan - i just came across this tracking why a Facebook login redirect back to my website wasn't loading the cookies. I understand the `strict` vs `lax` now - but my question is there a way to only allow `lax` by certain domains? Something like `lax:facebook.com,google.com` and then apply `strict` to all others? – rolinger Jul 27 '23 at 21:51
65

[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.

Steffen
  • 952
  • 7
  • 8
  • I found the answer interesting, I confess that I have never tried this because the specification does not speak (as far as I remember) of this possibility ... I will test this to see how it behaves – Lauro Moraes Oct 27 '17 at 14:14
  • 1
    I have tested in [phptester](http://phptester.net/) on PHP 7.0 and this test is successfull... many thanks for you response! – Lauro Moraes Dec 24 '17 at 17:15
  • Worked on Chrome 63 (desktop) 62 (Android), native Android (v62) and Opera (48)... Firefox (57) not supported but next release (58) promises support. – Lauro Moraes Dec 24 '17 at 17:23
  • 1
    Excellent hack – as long as it doesn’t break, which it hopefully doesn’t. But you have to watch future PHP releases for changes to this function. In the best case, this works until PHP ships the updated `setcookie` method that supports the feature directly, which may be PHP 7.3, as can be read in the other answer. – caw Jan 21 '18 at 20:34
  • 4
    It looks like this may indeed stop working soon (probably in PHP 7.3): https://github.com/php/php-src/commit/5cb825df7251aeb28b297f071c35b227a3949f01 – caw Apr 06 '18 at 18:24
  • The PHP RFC with the discussion regarding how this will be implemented in PHP can be found here: https://wiki.php.net/rfc/same-site-cookie – jlh Jun 05 '18 at 11:50
  • Note that the value should be `Strict` or `Lax` with a capital first letter according to the draft found at https://tools.ietf.org/html/draft-west-first-party-cookies-07 – jlh Jun 05 '18 at 12:03
  • This comment was super helpful. As an addendum, if your cookie is being created via `session_start()`, you can also highjack the `ini_set`, e.g. `ini_set('session.cookie_domain', $cookie_domain . '; samesite=none');`. – Erebus Mar 04 '20 at 23:56
  • 1
    This hack now fails in later versions. The options array solution from Marty works though. – misteraidan Jun 26 '20 at 02:08
  • Alternative `if` syntax: `if (version_compare(phpversion(), '7.3', '<')) { ... }`. – kenorb Sep 14 '20 at 21:14
28

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,
    ]);
}
William Desportes
  • 1,412
  • 1
  • 22
  • 31
jlyon
  • 381
  • 3
  • 3
3

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'));
jetblack
  • 600
  • 4
  • 10
  • I tested your code. for PHP < 7.3, you are checking if the header contains Set-Cookie. but in my case, my header doesn't have Set-Cookie. so I cant set samsite for my session cookie. do you have any idea why? – Imanez Mar 29 '20 at 12:23
  • I'm using your class SameSiteCookieSetter – Imanez Mar 30 '20 at 08:52
  • 1
    for old versions of php has a bug, remove the _boolval_ function to work correctly – MCunha98 Sep 16 '20 at 15:46
2

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.

Pang
  • 9,564
  • 146
  • 81
  • 122
2

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 <?php

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

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

1

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.

eburnside
  • 117
  • 1
  • 2
1

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/

Y.K.
  • 290
  • 2
  • 9
0

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.

Andrew Koster
  • 1,550
  • 1
  • 21
  • 31
0

As new browsers, it requires to set both secure and httponly with samesite attributes.
So you must use all ini set before the session_start().

Example :

ini_set('session.cookie_samesite', 'None');
ini_set('session.cookie_secure', 'On');
ini_set('session.cookie_httponly', 'On');

session_start();

Don't forget that :

you need to use https when enabling secure cookie

Root
  • 2,269
  • 5
  • 29
  • 58