PHP setcookie "SameSite=Strict"?

104

14
即使setcookie函数不起作用,您始终可以输出自己的自定义 header('Set-Cookie: ...') - Marc B
2
这里有一个开放的请求,您可以投票并关注。 - keune
2
这个库让你可以在cookie中使用属性:https://github.com/delight-im/PHP-Cookie。最重要的是,它还支持PHP内置会话的此属性,自动设置和使用cookie。或者,等待PHP本地提供此功能。 - caw
@caw 接受的答案中提供的解决方案已经减轻了这个问题,因为输出已经包含了该属性。 - Lauro Moraes
@LauroMoraes 首先,您关于库如何工作的描述是错误的。它至少与此处选择的答案一样好,并且更加灵活且不太可能在将来的PHP版本中出现问题。它确实直接编写了正确的cookie。其次,拦截和重写cookie仅发生在PHP的会话函数中——因为没有其他方法可以这样做。第三,即使进行了拦截,也是安全的,因为PHP在处理实际输出的第一个字节之前不发送任何标头,对吧?第四,您链接到的问题根本不相关或无效。 - caw
显示剩余5条评论
10个回答

176

1. 对于PHP >= v7.3

您可以使用$options数组来设置samesite值,例如:

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

samesite元素的值应为NoneLaxStrict之一。

请在手册页面中了解更多信息。

2. 对于PHP<v7.3

您可以根据您的代码库/需要使用以下解决方案/变通方法之一:

2.1 使用Apache配置设置SameSite cookie

您可以将以下行添加到您的Apache配置中

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

并且这将使用 SameSite=Lax 标志更新您的所有 cookie

在此处查看更多信息:https://blog.giantgeek.com/?p=1872

2.2 使用 Nginx 配置设置 SameSite cookies

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

同样的情况也会更新你所有带有SameSite=Lax标志的cookie。

更多信息请参见:https://serverfault.com/questions/849888/add-samesite-to-cookies-using-nginx-as-reverse-proxy

2.3 使用header方法设置SameSite cookie

我们知道,cookie只是HTTP请求中的一个标题,具有以下结构

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

所以我们可以使用header方法来设置cookie

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

事实上,Symfony并不需要等待PHP 7.3版本,它已经在幕后做到了,请看这里

Laravel也可以使用相同的方法,因为Laravel在幕后使用Symfony的Symfony\Component\HttpFoundation\Cookie类。

2.4 使用setcookie方法中的漏洞设置SameSite cookie

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

小心处理这个问题,PHP的setcookie方法中存在一个已知的错误,在PHP7.3版本中已经得到解决,查看这里 - https://github.com/php/php-src/commit/5cb825df7251aeb28b297f071c35b227a3949f01


4
在 PHP 7.3+ 中,setcookie(和 setrawcookie)的正确方法签名似乎不是这样的。需要作为第三个参数传递一个选项数组,其中之一称为samesite。请参见 https://github.com/php/php-src/blob/PHP-7.3/UPGRADING#L350。 - rink.attendant.6
1
在 PHP <= 7.2 中,session_set_cookie_params() 是否允许将 samesite 注入到 $path 中? - marcovtwout
如何在2.4 setcookie方法中添加安全和httponly? - Freedo
谢谢您的回答,我正在使用托管在Acquia上的Drupal 7,使用PHP 7.2.2;我对CMS非常陌生,您能告诉我需要更改哪个文件来添加SameSite选项以更改我的会话cookie吗? - Elber CM
1
@marcovtwout 是的,在 PHP 7.2.24 中,session_set_cookie_params() 中的注入对我也起作用,并且我没有在 PHP 变更日志中找到解决方法,这对我来说是可以接受的 :) - malisokan
显示剩余5条评论

65

[重要更新:正如@caw在下面指出的那样,这个黑客技巧在PHP 7.3中将会失效。立即停止使用它以避免不愉快的惊喜!或者至少像if (PHP_VERSION_ID < 70300) { ... } else { ... }这样,在PHP版本检查中包装它。]

看起来你可以滥用PHP的“setcookie”函数的“path”或“domain”参数来窃取SameSite属性,因为PHP不会对分号进行转义:

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

然后PHP发送以下HTTP头:

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

我刚在几分钟前发现了这个问题,请自行测试!我使用的是PHP 7.1.11。


1
我在 phptester 上测试过 PHP 7.0,并且测试成功了...非常感谢您的回复! - Lauro Moraes
曾在Chrome 63 (桌面版)、Chrome 62 (安卓版)、本地安卓应用(v62)和Opera 48上工作过...火狐浏览器57版本不支持,但下一个版本(58)承诺支持。 - Lauro Moraes
1
优秀的黑客技巧——只要它不出问题,希望不会。但是你必须关注未来 PHP 发布对此函数的更改。在最好的情况下,这可以一直使用,直到 PHP 发布支持该功能的更新的 setcookie 方法,这可能是 PHP 7.3,正如其他答案中所述。 - caw
4
看起来这个功能很快就会停止使用(可能在PHP 7.3中),详见以下链接:https://github.com/php/php-src/commit/5cb825df7251aeb28b297f071c35b227a3949f01 - caw
1
这个黑客现在在后续版本中失效了。Marty的选项数组解决方案可行。 - misteraidan
显示剩余5条评论

29

基于上面Steffen的答案,这是我使用的方法来支持php <= 7.2和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://dev59.com/FFkS5IYBdhLWcg3wgXHf#46971326 
 *
 * @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,
    ]);
}

谢谢你的回答,你能告诉我在 Drupal 7 (PHP 7.2.2) 中在哪里实现这个函数吗? - Elber CM
备选语法:if (version_compare(phpversion(),'7.3','<')) { ... } - kenorb
在 PHP 7.4 中完美运行。 - mdikici

3
我写了一个类来设置samesite cookie。
它适用于所有PHP版本。 它还会检查浏览器是否正确支持同站点参数。 https://github.com/ovunctukenmez/SameSiteCookieSetter

Here is the usage:

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


我测试了你的代码。对于 PHP < 7.3,你正在检查头部是否包含 Set-Cookie。但在我的情况下,我的头部没有 Set-Cookie,因此我无法为我的会话 cookie 设置 SameSite 属性。你有任何想法是为什么吗? - Imanez
我正在使用你的 SameSiteCookieSetter 类。 - Imanez
1
对于旧版本的PHP存在一个bug,需要移除_boolval_函数才能正常工作。 - MCunha98

2
根据这个网站的说法,似乎这是PHP 7.3的问题。根据投票结果,正在实现与cookie相关函数的更普遍扩展,php.ini文件中也可能有一个新键。
但正如Marc B已经写过的那样,您可以使用header()函数调用代替,我会在一些用于包含其他初始内容的文件中这样做。

2
这可能对仍在使用PHP >= 7.3.x和CI 3.1.11的人有所帮助。
在根目录中找到index.php,在
if(isset($_COOKIE["PHPSESSID"])){
    header('Set-Cookie: PHPSESSID='.$_COOKIE["PHPSESSID"].'; SameSite=None');
}

对我有用,在尝试了所有方法之后(徒劳无功)


对我也起作用。只需在开始会话时始终放置它。 - Oliver Becker
为什么不在session_start()中指定选项? - theking2

1

补充 Marty Aghajanyan 的答案(因为显然我可以回答,但还不能评论)

在 Apache 2.4.29 (Ubuntu) 中,通过 mod_headers 和 PHP 进行操作并不起作用。在查看文档(http://www.balkangreenfoundation.org/manual/en/mod/mod_headers.html)时,我注意到“always”条件在某些情况下无法从相同的响应头池中工作。因此,在我的情况下,以下设置 SameSite 参数有效。(尽管我在设置最近的 Chrome 80 更新时将其设置为 None)

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

文档还建议,如果您想涵盖所有情况,您可以添加指令,包括和不包括"always",但我没有测试过。

1

0

有很多示例展示了如何设置这个属性,但很少有解释为什么要这样做。

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

如果需要发送跨域cookie,请使用None指令来退出SameSite限制。None指令要求同时使用Secure属性。
将SameSite设置为None或Lax的示例仅适用于跨域场景。如果您的代码不是跨域的,请使用Strict。

0
作为新的浏览器,需要使用samesite属性设置securehttponly。因此,在session_start()之前必须使用所有ini设置。 示例:
ini_set('session.cookie_samesite', 'None');
ini_set('session.cookie_secure', 'On');
ini_set('session.cookie_httponly', 'On');

session_start();

不要忘记以下几点: 在启用安全cookie时,需要使用https。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接