Dane Iracleous
Dane Iracleous

Reputation: 1759

How to tell PHP to use SameSite=None for cross-site cookies?

According to the article here https://php.watch/articles/PHP-Samesite-cookies and PHP documenation at https://www.php.net/manual/en/session.security.ini.php, There are only 2 possible config options for this new feature, added in PHP 7.3:

  1. session.cookie_samesite=Lax
  2. session.cookie_samesite=Strict

Yet, according to the Chrome console, this needs to be set to "None":

A cookie associated with a cross-site resource at URL was set without the SameSite attribute. It has been blocked, as Chrome now only delivers cookies with cross-site requests if they are set with SameSite=None and Secure. You can review cookies in developer tools under Application>Storage>Cookies and see more details at URL and URL.

Because of this, I can no longer set cross-site cookies. What is the workaround?

Upvotes: 29

Views: 57976

Answers (7)

Ohad Cohen
Ohad Cohen

Reputation: 6152

Mission: let JS Ajax request be done from another domain (e,g: localhost)

steps:

A. add withCredentials to your ajax request

B. set Access-Control-Allow-Credentials: true among the rest of the Access-Control-Allow-... headers

C. add "samesite" => "none" to your session cookie params.

finally my code look like this:

Server:

$currentCookieParams = session_get_cookie_params();
session_set_cookie_params([
    "lifetime" => $currentCookieParams["lifetime"],
    "path" => $currentCookieParams["path"],
    "domain" => $_SERVER["HTTP_HOST"],
    "secure" => true,
    "samesite" => "none",
    "httponly" => $currentCookieParams["httponly"]
]);
header("Access-Control-Allow-Origin: " . $_SERVER["HTTP_ORIGIN"]);
header("Access-Control-Allow-Headers: " . $_SERVER["HTTP_ACCESS_CONTROL_REQUEST_HEADERS"]);
header("Access-Control-Allow-Methods: " . $_SERVER["HTTP_ACCESS_CONTROL_REQUEST_METHOD"]);
header('Access-Control-Max-Age: 1000');
header('Access-Control-Allow-Credentials: true');

js - client:

fetch(`https://example.com/login.php`, {
    method: 'POST',
    withCredentials: true,
    body: `username=${user}&password=${pass}`
 }).then(res => {
     if (res.status === 200) {




        //here session cookie exists and sent to server, so i can access the data
        fetch(`https://example.com/secret.php`, {
            method: 'GET',
            withCredentials: true,
        }).then(res => {
            if (res.status === 200) {
                console.log(res.body);
            } else {
                console.error("error")
            }
        }).catch(err => {
            console.log(err)
        })



    } else {
        console.error("error")
    }
}).catch(err => {
    console.log(err)
})

tested with FF 112 and Chromium 113 on ubuntu

Upvotes: 0

Farhad Aghasaghloo
Farhad Aghasaghloo

Reputation: 101

ini_set('session.cookie_secure', "1"); ini_set('session.cookie_httponly', "1"); ini_set('session.cookie_samesite','None'); session_start();

php 7.4 samesite in phpinfo enter image description here

php 7.2 samesite does not exist in phpinfo enter image description here

$currentCookieParams = session_get_cookie_params();
$cookie_domain= 'your domain';
if (PHP_VERSION_ID >= 70300) {
session_set_cookie_params([
    'lifetime' =>  $currentCookieParams["lifetime"],
    'path' => '/',
    'domain' => $cookie_domain,
    'secure' => "1",
    'httponly' => "1",
    'samesite' => 'None',
]);
} else {
session_set_cookie_params(
    $currentCookieParams["lifetime"],
    '/; samesite=None',
    $cookie_domain,
    "1",
    "1"
);
}
session_start();

Upvotes: 10

ronix75
ronix75

Reputation: 1

If you use nginx, you can also modify the cookie using lua. It is a bit hacky, but I found it to be working well for legacy sites:

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    location ~ \.php$ {
            include snippets/fastcgi-php.conf;

            fastcgi_pass unix:/run/php/php5.6-fpm.sock;

            # This is the relevant part:
            header_filter_by_lua '
                    local cookies = ngx.header.set_cookie
                    if cookies then
                            if type(cookies) ~= "table" then
                                    cookies = {cookies}
                            end
                            local gsub = string.gsub
                            local changed
                            for i, cookie in ipairs(cookies) do
                                    local new_cookie = gsub(cookie, "^PHPSESSION=(.*)", "PHPSESSION=%1; Samesite=strict", 1)
                                    if new_cookie ~= cookie then
                                            cookies[i] = new_cookie
                                            changed = true
                                    end
                            end
                            if changed then
                                    ngx.header.set_cookie = cookies
                            end
                    end
            ';
            # End Lua
    }

You might need to adapt the regex (gsub), but I found it works well.

Upvotes: 0

Nadir
Nadir

Reputation: 715

For PHP 5.6.40, there exists a workaround (the hack on path parameter) which does not involve rebuilding PHP.

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. I needed it for our projects that aren't migrated yet. I know 5.6 branch is deprecated, I am just sharing.

Pull request: https://github.com/php/php-src/pull/6446

Our repo with the changes: https://github.com/Inducido/php-src/tree/PHP-5.6.40

Build tested on Debian 8.11

New Feature

Session: . Added support for the SameSite cookie directive for setcookie(), setrawcookie() and session_set_cookie_params(). Port from PHP 7.x branch they all have an "samesite" additionnal parameter at the very end (string)

prototypes:

bool setcookie(string name [, string value [, int expires [, string path [, string domain [, bool secure[, bool httponly[, string samesite]]]]]]])
bool setrawcookie(string name [, string value [, int expires [, string path [, string domain [, bool secure[, bool httponly[, string samesite]]]]]]])
void session_set_cookie_params(int lifetime [, string path [, string domain [, bool secure[, bool httponly[, string samesite]]]]])
(session_get_cookie_params updated too)

Changes to INI File Handling

  • session.cookie_samesite . New INI option to allow to set the SameSite directive for cookies. Defaults to "" (empty string), so no SameSite directive is set. Can be set to "Lax" or "Strict", or "None" which sets the respective SameSite directive. when using "None", make sure to include the quotes, as none is interpreted like false in ini files.

This solves the issue "This Set-Cookie was blocked due to user preferences" in Chrome.

Upvotes: 0

Anonymous
Anonymous

Reputation: 12027

You can set the value to "None" using ini_set. There's no check that the value is supported when that function is used:

ini_set('session.cookie_samesite', 'None');
session_start();

session_set_cookie_params can also set it:

session_set_cookie_params(['samesite' => 'None']);
session_start();

The bug report for this to be supported in php.ini is here.


As @shrimpwagon said in a comment below, session.cookie_secure must be true for this to work. PHP doesn't require it, but browsers do.

Upvotes: 45

vitaliytv
vitaliytv

Reputation: 834

Bad:

session.cookie_samesite=None

Correct:

session.cookie_samesite="None"

Explanation here

Upvotes: 5

Thanh Phong Đỗ
Thanh Phong Đỗ

Reputation: 1

This method can be helpful for u

Add header's attributes on nginx below Secure + SameSite=None

location / {

proxy_cookie_path / "/; secure; SameSite=none";

}

It's working on me!

Upvotes: 0

Related Questions